diff --git a/packages/flutter_tools/lib/src/android/android_studio_validator.dart b/packages/flutter_tools/lib/src/android/android_studio_validator.dart index 29cbe4a15e..b2bf7fb9a4 100644 --- a/packages/flutter_tools/lib/src/android/android_studio_validator.dart +++ b/packages/flutter_tools/lib/src/android/android_studio_validator.dart @@ -52,7 +52,7 @@ class AndroidStudioValidator extends DoctorValidator { } @override - Future validate() async { + Future validateImpl() async { final List messages = []; ValidationType type = ValidationType.missing; @@ -125,7 +125,7 @@ class NoAndroidStudioValidator extends DoctorValidator { final UserMessages _userMessages; @override - Future validate() async { + Future validateImpl() async { final List messages = []; final String? cfgAndroidStudio = _config.getValue('android-studio-dir') as String?; diff --git a/packages/flutter_tools/lib/src/android/android_workflow.dart b/packages/flutter_tools/lib/src/android/android_workflow.dart index b7bb13df90..b2833a4e4b 100644 --- a/packages/flutter_tools/lib/src/android/android_workflow.dart +++ b/packages/flutter_tools/lib/src/android/android_workflow.dart @@ -134,7 +134,7 @@ class AndroidValidator extends DoctorValidator { } @override - Future validate() async { + Future validateImpl() async { final List messages = []; final AndroidSdk? androidSdk = _androidSdk; if (androidSdk == null) { @@ -267,7 +267,7 @@ class AndroidLicenseValidator extends DoctorValidator { String get slowWarning => 'Checking Android licenses is taking an unexpectedly long time...'; @override - Future validate() async { + Future validateImpl() async { final List messages = []; // Match pre-existing early termination behavior diff --git a/packages/flutter_tools/lib/src/doctor.dart b/packages/flutter_tools/lib/src/doctor.dart index fcb98e7859..c8375b5e35 100644 --- a/packages/flutter_tools/lib/src/doctor.dart +++ b/packages/flutter_tools/lib/src/doctor.dart @@ -63,9 +63,11 @@ abstract class DoctorValidatorsProvider { } /// The singleton instance, pulled from the [AppContext]. - static DoctorValidatorsProvider get _instance => context.get()!; + static DoctorValidatorsProvider get _instance => + context.get()!; - static final DoctorValidatorsProvider defaultInstance = _DefaultDoctorValidatorsProvider( + static final DoctorValidatorsProvider defaultInstance = + _DefaultDoctorValidatorsProvider( logger: globals.logger, platform: globals.platform, featureFlags: featureFlags, @@ -93,7 +95,8 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { featureFlags: featureFlags, ); - late final WebWorkflow webWorkflow = WebWorkflow(platform: platform, featureFlags: featureFlags); + late final WebWorkflow webWorkflow = + WebWorkflow(platform: platform, featureFlags: featureFlags); late final MacOSWorkflow macOSWorkflow = MacOSWorkflow( platform: platform, @@ -126,15 +129,16 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { processManager: globals.processManager, logger: _logger, ), - ...VsCodeValidator.installedValidators(globals.fs, platform, globals.processManager), + ...VsCodeValidator.installedValidators( + globals.fs, platform, globals.processManager), ]; final ProxyValidator proxyValidator = ProxyValidator(platform: platform); _validators = [ FlutterValidator( fileSystem: globals.fs, platform: globals.platform, - flutterVersion: - () => globals.flutterVersion.fetchTagsAndGetVersion(clock: globals.systemClock), + flutterVersion: () => globals.flutterVersion + .fetchTagsAndGetVersion(clock: globals.systemClock), devToolsVersion: () => globals.cache.devToolsVersion, processManager: globals.processManager, userMessages: globals.userMessages, @@ -152,8 +156,10 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { ), ), if (androidWorkflow!.appliesToHostPlatform) - GroupedValidator([androidValidator!, androidLicenseValidator!]), - if (globals.iosWorkflow!.appliesToHostPlatform || macOSWorkflow.appliesToHostPlatform) + GroupedValidator( + [androidValidator!, androidLicenseValidator!]), + if (globals.iosWorkflow!.appliesToHostPlatform || + macOSWorkflow.appliesToHostPlatform) GroupedValidator([ XcodeValidator( xcode: globals.xcode!, @@ -183,7 +189,9 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { if (ideValidators.isNotEmpty) ...ideValidators else NoIdeValidator(), if (proxyValidator.shouldShow) proxyValidator, if (globals.deviceManager?.canListAnything ?? false) - DeviceValidator(deviceManager: globals.deviceManager, userMessages: globals.userMessages), + DeviceValidator( + deviceManager: globals.deviceManager, + userMessages: globals.userMessages), HttpHostValidator( platform: globals.platform, featureFlags: featureFlags, @@ -208,10 +216,13 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider { } class Doctor { - Doctor({required Logger logger, required SystemClock clock, Analytics? analytics}) - : _logger = logger, - _clock = clock, - _analytics = analytics ?? globals.analytics; + Doctor( + {required Logger logger, + required SystemClock clock, + Analytics? analytics}) + : _logger = logger, + _clock = clock, + _analytics = analytics ?? globals.analytics; final Logger _logger; final SystemClock _clock; @@ -224,39 +235,41 @@ class Doctor { /// Return a list of [ValidatorTask] objects and starts validation on all /// objects in [validators]. List startValidatorTasks() => [ - for (final DoctorValidator validator in validators) - ValidatorTask( - validator, - // We use an asyncGuard() here to be absolutely certain that - // DoctorValidators do not result in an uncaught exception. Since the - // Future returned by the asyncGuard() is not awaited, we pass an - // onError callback to it and translate errors into ValidationResults. - asyncGuard( - () { - final Completer timeoutCompleter = Completer(); - final Timer timer = Timer(doctorDuration, () { - timeoutCompleter.completeError( - Exception( - '${validator.title} exceeded maximum allowed duration of $doctorDuration', - ), - ); - }); - final Future validatorFuture = validator.validate(); - return Future.any(>[ - validatorFuture, - // This future can only complete with an error - timeoutCompleter.future, - ]).then((ValidationResult result) async { - timer.cancel(); - return result; - }); - }, - onError: (Object exception, StackTrace stackTrace) { - return ValidationResult.crash(exception, stackTrace); - }, - ), - ), - ]; + for (final DoctorValidator validator in validators) + ValidatorTask( + validator, + // We use an asyncGuard() here to be absolutely certain that + // DoctorValidators do not result in an uncaught exception. Since the + // Future returned by the asyncGuard() is not awaited, we pass an + // onError callback to it and translate errors into ValidationResults. + asyncGuard( + () { + final Completer timeoutCompleter = + Completer(); + final Timer timer = Timer(doctorDuration, () { + timeoutCompleter.completeError( + Exception( + '${validator.title} exceeded maximum allowed duration of $doctorDuration', + ), + ); + }); + final Future validatorFuture = + validator.validate(); + return Future.any(>[ + validatorFuture, + // This future can only complete with an error + timeoutCompleter.future, + ]).then((ValidationResult result) async { + timer.cancel(); + return result; + }); + }, + onError: (Object exception, StackTrace stackTrace) { + return ValidationResult.crash(exception, stackTrace); + }, + ), + ), + ]; List get workflows { return DoctorValidatorsProvider._instance.workflows; @@ -277,7 +290,8 @@ class Doctor { final StringBuffer lineBuffer = StringBuffer(); ValidationResult result; try { - result = await asyncGuard(() => validator.validate()); + result = + await asyncGuard(() => validator.validateImpl()); } on Exception catch (exception) { // We're generating a summary, so drop the stack trace. result = ValidationResult.crash(exception); @@ -290,7 +304,8 @@ class Doctor { case ValidationType.missing: lineBuffer.write('is not installed.'); case ValidationType.partial: - lineBuffer.write('is partially installed; more components are available.'); + lineBuffer + .write('is partially installed; more components are available.'); case ValidationType.notAvailable: lineBuffer.write('is not available.'); case ValidationType.success: @@ -318,7 +333,8 @@ class Doctor { if (sawACrash) { buffer.writeln(); - buffer.writeln('Run "flutter doctor" for information about why a doctor check crashed.'); + buffer.writeln( + 'Run "flutter doctor" for information about why a doctor check crashed.'); } if (missingComponent) { @@ -332,7 +348,8 @@ class Doctor { } Future checkRemoteArtifacts(String engineRevision) async { - return globals.cache.areRemoteArtifactsAvailable(engineVersion: engineRevision); + return globals.cache + .areRemoteArtifactsAvailable(engineVersion: engineRevision); } /// Maximum allowed duration for an entire validator to take. @@ -360,7 +377,8 @@ class Doctor { } if (!verbose) { - _logger.printStatus('Doctor summary (to see all details, run flutter doctor -v):'); + _logger.printStatus( + 'Doctor summary (to see all details, run flutter doctor -v):'); } bool doctorResult = true; int issues = 0; @@ -369,7 +387,8 @@ class Doctor { // were sent for each doctor validator and its result final int analyticsTimestamp = _clock.now().millisecondsSinceEpoch; - for (final ValidatorTask validatorTask in startedValidatorTasks ?? startValidatorTasks()) { + for (final ValidatorTask validatorTask + in startedValidatorTasks ?? startValidatorTasks()) { final DoctorValidator validator = validatorTask.validator; final Status status = _logger.startSpinner( timeout: validator.slowWarningDuration, @@ -437,29 +456,41 @@ class Doctor { DoctorResultEvent(validator: validator, result: result).send(); } - final String leadingBox = showColor ? result.coloredLeadingBox : result.leadingBox; + final String executionDuration = () { + final Duration? executionTime = result.executionTime; + if (!verbose || executionTime == null) { + return ''; + } + final String formatted = executionTime.inSeconds < 2 + ? getElapsedAsMilliseconds(executionTime) + : getElapsedAsSeconds(executionTime); + return ' [$formatted]'; + }(); + + final String leadingBox = + showColor ? result.coloredLeadingBox : result.leadingBox; if (result.statusInfo != null) { _logger.printStatus( - '$leadingBox ${validator.title} (${result.statusInfo})', - hangingIndent: result.leadingBox.length + 1, - ); + '$leadingBox ${validator.title} (${result.statusInfo})$executionDuration', + hangingIndent: result.leadingBox.length + 1); } else { - _logger.printStatus( - '$leadingBox ${validator.title}', - hangingIndent: result.leadingBox.length + 1, - ); + _logger.printStatus('$leadingBox ${validator.title}$executionDuration', + hangingIndent: result.leadingBox.length + 1); } for (final ValidationMessage message in result.messages) { if (!message.isInformation || verbose) { int hangingIndent = 2; int indent = 4; - final String indicator = showColor ? message.coloredIndicator : message.indicator; + final String indicator = + showColor ? message.coloredIndicator : message.indicator; for (final String line - in '$indicator ${showPii ? message.message : message.piiStrippedMessage}'.split( - '\n', - )) { - _logger.printStatus(line, hangingIndent: hangingIndent, indent: indent, emphasis: true); + in '$indicator ${showPii ? message.message : message.piiStrippedMessage}' + .split( + '\n', + )) { + _logger.printStatus(line, + hangingIndent: hangingIndent, indent: indent, emphasis: true); // Only do hanging indent for the first line. hangingIndent = 0; indent = 6; @@ -501,7 +532,8 @@ class Doctor { return doctorResult; } - bool get canListAnything => workflows.any((Workflow workflow) => workflow.canListDevices); + bool get canListAnything => + workflows.any((Workflow workflow) => workflow.canListDevices); bool get canLaunchAnything { if (FlutterTesterDevices.showFlutterTesterDevice) { @@ -527,16 +559,16 @@ class FlutterValidator extends DoctorValidator { required ProcessManager processManager, required String Function() flutterRoot, required OperatingSystemUtils operatingSystemUtils, - }) : _flutterVersion = flutterVersion, - _devToolsVersion = devToolsVersion, - _platform = platform, - _userMessages = userMessages, - _fileSystem = fileSystem, - _artifacts = artifacts, - _processManager = processManager, - _flutterRoot = flutterRoot, - _operatingSystemUtils = operatingSystemUtils, - super('Flutter'); + }) : _flutterVersion = flutterVersion, + _devToolsVersion = devToolsVersion, + _platform = platform, + _userMessages = userMessages, + _fileSystem = fileSystem, + _artifacts = artifacts, + _processManager = processManager, + _flutterRoot = flutterRoot, + _operatingSystemUtils = operatingSystemUtils, + super('Flutter'); final Platform _platform; final FlutterVersion Function() _flutterVersion; @@ -549,7 +581,7 @@ class FlutterValidator extends DoctorValidator { final OperatingSystemUtils _operatingSystemUtils; @override - Future validate() async { + Future validateImpl() async { final List messages = []; String? versionChannel; String? frameworkVersion; @@ -561,7 +593,8 @@ class FlutterValidator extends DoctorValidator { frameworkVersion = version.frameworkVersion; final String flutterRoot = _flutterRoot(); - messages.add(_getFlutterVersionMessage(frameworkVersion, versionChannel, flutterRoot)); + messages.add(_getFlutterVersionMessage( + frameworkVersion, versionChannel, flutterRoot)); _validateRequiredBinaries(flutterRoot).forEach(messages.add); messages.add(_getFlutterUpstreamMessage(version)); @@ -577,16 +610,21 @@ class FlutterValidator extends DoctorValidator { ), ), ); - messages.add(ValidationMessage(_userMessages.engineRevision(version.engineRevisionShort))); - messages.add(ValidationMessage(_userMessages.dartRevision(version.dartSdkVersion))); - messages.add(ValidationMessage(_userMessages.devToolsVersion(_devToolsVersion()))); + messages.add(ValidationMessage( + _userMessages.engineRevision(version.engineRevisionShort))); + messages.add(ValidationMessage( + _userMessages.dartRevision(version.dartSdkVersion))); + messages.add( + ValidationMessage(_userMessages.devToolsVersion(_devToolsVersion()))); final String? pubUrl = _platform.environment[kPubDevOverride]; if (pubUrl != null) { messages.add(ValidationMessage(_userMessages.pubMirrorURL(pubUrl))); } - final String? storageBaseUrl = _platform.environment[kFlutterStorageBaseUrl]; + final String? storageBaseUrl = + _platform.environment[kFlutterStorageBaseUrl]; if (storageBaseUrl != null) { - messages.add(ValidationMessage(_userMessages.flutterMirrorURL(storageBaseUrl))); + messages.add( + ValidationMessage(_userMessages.flutterMirrorURL(storageBaseUrl))); } } on VersionCheckError catch (e) { messages.add(ValidationMessage.error(e.message)); @@ -595,8 +633,10 @@ class FlutterValidator extends DoctorValidator { // Check that the binaries we downloaded for this platform actually run on it. // If the binaries are not downloaded (because android is not enabled), then do // not run this check. - final String genSnapshotPath = _artifacts.getArtifactPath(Artifact.genSnapshot); - if (_fileSystem.file(genSnapshotPath).existsSync() && !_genSnapshotRuns(genSnapshotPath)) { + final String genSnapshotPath = + _artifacts.getArtifactPath(Artifact.genSnapshot); + if (_fileSystem.file(genSnapshotPath).existsSync() && + !_genSnapshotRuns(genSnapshotPath)) { final StringBuffer buffer = StringBuffer(); buffer.writeln(_userMessages.flutterBinariesDoNotRun); if (_platform.isLinux) { @@ -606,7 +646,8 @@ class FlutterValidator extends DoctorValidator { buffer.writeln( 'Flutter requires the Rosetta translation environment on ARM Macs. Try running:', ); - buffer.writeln(' sudo softwareupdate --install-rosetta --agree-to-license'); + buffer.writeln( + ' sudo softwareupdate --install-rosetta --agree-to-license'); } messages.add(ValidationMessage.error(buffer.toString())); } @@ -619,7 +660,8 @@ class FlutterValidator extends DoctorValidator { // in that case, make it clear that it is fine to continue, but freshness check/upgrades // won't be supported. valid = ValidationType.partial; - messages.add(ValidationMessage(_userMessages.flutterValidatorErrorIntentional)); + messages.add( + ValidationMessage(_userMessages.flutterValidatorErrorIntentional)); } return ValidationResult( @@ -653,17 +695,21 @@ class FlutterValidator extends DoctorValidator { return ValidationMessage(flutterVersionMessage); } if (versionChannel == kUserBranch) { - flutterVersionMessage = '$flutterVersionMessage\n${_userMessages.flutterUnknownChannel}'; + flutterVersionMessage = + '$flutterVersionMessage\n${_userMessages.flutterUnknownChannel}'; } if (frameworkVersion == '0.0.0-unknown') { - flutterVersionMessage = '$flutterVersionMessage\n${_userMessages.flutterUnknownVersion}'; + flutterVersionMessage = + '$flutterVersionMessage\n${_userMessages.flutterUnknownVersion}'; } return ValidationMessage.hint(flutterVersionMessage); } List _validateRequiredBinaries(String flutterRoot) { - final ValidationMessage? flutterWarning = _validateSdkBinary('flutter', flutterRoot); - final ValidationMessage? dartWarning = _validateSdkBinary('dart', flutterRoot); + final ValidationMessage? flutterWarning = + _validateSdkBinary('flutter', flutterRoot); + final ValidationMessage? dartWarning = + _validateSdkBinary('dart', flutterRoot); return [ if (flutterWarning != null) flutterWarning, if (dartWarning != null) dartWarning, @@ -684,8 +730,7 @@ class FlutterValidator extends DoctorValidator { } final String resolvedFlutterPath = flutterBin.resolveSymbolicLinksSync(); if (!_filePathContainsDirPath(flutterRoot, resolvedFlutterPath)) { - final String hint = - 'Warning: `$binary` on your path resolves to ' + final String hint = 'Warning: `$binary` on your path resolves to ' '$resolvedFlutterPath, which is not inside your current Flutter ' 'SDK checkout at $flutterRoot. Consider adding $flutterBinDir to ' 'the front of your path.'; @@ -697,9 +742,8 @@ class FlutterValidator extends DoctorValidator { bool _filePathContainsDirPath(String directory, String file) { // calling .canonicalize() will normalize for alphabetic case and path // separators - return _fileSystem.path - .canonicalize(file) - .startsWith(_fileSystem.path.canonicalize(directory) + _fileSystem.path.separator); + return _fileSystem.path.canonicalize(file).startsWith( + _fileSystem.path.canonicalize(directory) + _fileSystem.path.separator); } ValidationMessage _getFlutterUpstreamMessage(FlutterVersion version) { @@ -710,11 +754,14 @@ class FlutterValidator extends DoctorValidator { // VersionUpstreamValidator can produce an error if repositoryUrl is null if (upstreamValidationError != null) { final String errorMessage = upstreamValidationError.message; - if (errorMessage.contains('could not determine the remote upstream which is being tracked')) { - return ValidationMessage.hint(_userMessages.flutterUpstreamRepositoryUnknown); + if (errorMessage.contains( + 'could not determine the remote upstream which is being tracked')) { + return ValidationMessage.hint( + _userMessages.flutterUpstreamRepositoryUnknown); } // At this point, repositoryUrl must not be null - if (errorMessage.contains('Flutter SDK is tracking a non-standard remote')) { + if (errorMessage + .contains('Flutter SDK is tracking a non-standard remote')) { return ValidationMessage.hint( _userMessages.flutterUpstreamRepositoryUrlNonStandard(repositoryUrl!), ); @@ -727,13 +774,15 @@ class FlutterValidator extends DoctorValidator { ); } } - return ValidationMessage(_userMessages.flutterUpstreamRepositoryUrl(repositoryUrl!)); + return ValidationMessage( + _userMessages.flutterUpstreamRepositoryUrl(repositoryUrl!)); } bool _genSnapshotRuns(String genSnapshotPath) { const int kExpectedExitCode = 255; try { - return _processManager.runSync([genSnapshotPath]).exitCode == kExpectedExitCode; + return _processManager.runSync([genSnapshotPath]).exitCode == + kExpectedExitCode; } on Exception { return false; } @@ -743,9 +792,9 @@ class FlutterValidator extends DoctorValidator { class DeviceValidator extends DoctorValidator { // TODO(jmagman): Make required once g3 rolls and is updated. DeviceValidator({DeviceManager? deviceManager, UserMessages? userMessages}) - : _deviceManager = deviceManager ?? globals.deviceManager!, - _userMessages = userMessages ?? globals.userMessages, - super('Connected device'); + : _deviceManager = deviceManager ?? globals.deviceManager!, + _userMessages = userMessages ?? globals.userMessages, + super('Connected device'); final DeviceManager _deviceManager; final UserMessages _userMessages; @@ -754,25 +803,27 @@ class DeviceValidator extends DoctorValidator { String get slowWarning => 'Scanning for devices is taking a long time...'; @override - Future validate() async { + Future validateImpl() async { final List devices = await _deviceManager.refreshAllDevices( timeout: DeviceManager.minimumWirelessDeviceDiscoveryTimeout, ); List installedMessages = []; if (devices.isNotEmpty) { - installedMessages = - (await Device.descriptions( - devices, - )).map((String msg) => ValidationMessage(msg)).toList(); + installedMessages = (await Device.descriptions( + devices, + )) + .map((String msg) => ValidationMessage(msg)) + .toList(); } List diagnosticMessages = []; - final List diagnostics = await _deviceManager.getDeviceDiagnostics(); + final List diagnostics = + await _deviceManager.getDeviceDiagnostics(); if (diagnostics.isNotEmpty) { - diagnosticMessages = - diagnostics - .map((String message) => ValidationMessage.hint(message)) - .toList(); + diagnosticMessages = diagnostics + .map( + (String message) => ValidationMessage.hint(message)) + .toList(); } else if (devices.isEmpty) { diagnosticMessages = [ ValidationMessage.hint(_userMessages.devicesMissing), @@ -800,9 +851,11 @@ class DeviceValidator extends DoctorValidator { /// Wrapper for doctor to run multiple times with PII and without, running the validators only once. class DoctorText { - DoctorText(BufferLogger logger, {SystemClock? clock, @visibleForTesting Doctor? doctor}) - : _doctor = doctor ?? Doctor(logger: logger, clock: clock ?? globals.systemClock), - _logger = logger; + DoctorText(BufferLogger logger, + {SystemClock? clock, @visibleForTesting Doctor? doctor}) + : _doctor = doctor ?? + Doctor(logger: logger, clock: clock ?? globals.systemClock), + _logger = logger; final BufferLogger _logger; final Doctor _doctor; @@ -812,7 +865,8 @@ class DoctorText { late final Future piiStrippedText = _runDiagnosis(false); // Start the validator tasks only once. - late final List _validatorTasks = _doctor.startValidatorTasks(); + late final List _validatorTasks = + _doctor.startValidatorTasks(); Future _runDiagnosis(bool showPii) async { try { diff --git a/packages/flutter_tools/lib/src/doctor_validator.dart b/packages/flutter_tools/lib/src/doctor_validator.dart index bc3d7cdfda..aed8053339 100644 --- a/packages/flutter_tools/lib/src/doctor_validator.dart +++ b/packages/flutter_tools/lib/src/doctor_validator.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:meta/meta.dart'; import 'base/async_guard.dart'; @@ -36,7 +38,7 @@ enum ValidationType { crash, missing, partial, notAvailable, success } enum ValidationMessageType { error, hint, information } abstract class DoctorValidator { - const DoctorValidator(this.title); + DoctorValidator(this.title); /// This is displayed in the CLI. final String title; @@ -48,7 +50,19 @@ abstract class DoctorValidator { /// Duration before the spinner should display [slowWarning]. Duration get slowWarningDuration => _slowWarningDuration; - Future validate(); + /// Performs validation by invoking [validateImpl]. + /// + /// Tracks time taken to execute the validation step. + Future validate() async { + final Stopwatch stopwatch = Stopwatch()..start(); + final ValidationResult result = await validateImpl(); + stopwatch.stop(); + result._executionTime = stopwatch.elapsed; + return result; + } + + /// Validation implementation. + Future validateImpl(); } /// A validator that runs other [DoctorValidator]s and combines their output @@ -74,10 +88,11 @@ class GroupedValidator extends DoctorValidator { String _currentSlowWarning = 'Initializing...'; @override - Future validate() async { + Future validateImpl() async { final List tasks = [ for (final DoctorValidator validator in subValidators) - ValidatorTask(validator, asyncGuard(() => validator.validate())), + ValidatorTask(validator, + asyncGuard(() => validator.validate())), ]; final List results = []; @@ -123,24 +138,26 @@ class GroupedValidator extends DoctorValidator { } } -@immutable class ValidationResult { /// [ValidationResult.type] should only equal [ValidationResult.success] /// if no [messages] are hints or errors. - const ValidationResult(this.type, this.messages, {this.statusInfo}); + ValidationResult(this.type, this.messages, {this.statusInfo}); factory ValidationResult.crash(Object error, [StackTrace? stackTrace]) { - return ValidationResult(ValidationType.crash, [ - const ValidationMessage.error( - 'Due to an error, the doctor check did not complete. ' - 'If the error message below is not helpful, ' - 'please let us know about this issue at https://github.com/flutter/flutter/issues.', - ), - ValidationMessage.error('$error'), - if (stackTrace != null) - // Stacktrace is informational. Printed in verbose mode only. - ValidationMessage('$stackTrace'), - ], statusInfo: 'the doctor check crashed'); + return ValidationResult( + ValidationType.crash, + [ + const ValidationMessage.error( + 'Due to an error, the doctor check did not complete. ' + 'If the error message below is not helpful, ' + 'please let us know about this issue at https://github.com/flutter/flutter/issues.', + ), + ValidationMessage.error('$error'), + if (stackTrace != null) + // Stacktrace is informational. Printed in verbose mode only. + ValidationMessage('$stackTrace'), + ], + statusInfo: 'the doctor check crashed'); } final ValidationType type; @@ -149,28 +166,36 @@ class ValidationResult { final List messages; String get leadingBox => switch (type) { - ValidationType.crash => '[☠]', - ValidationType.missing => '[✗]', - ValidationType.success => '[✓]', - ValidationType.notAvailable || ValidationType.partial => '[!]', - }; + ValidationType.crash => '[☠]', + ValidationType.missing => '[✗]', + ValidationType.success => '[✓]', + ValidationType.notAvailable || ValidationType.partial => '[!]', + }; + + /// The time taken to perform the validation, set by [DoctorValidator.validate]. + Duration? get executionTime => _executionTime; + Duration? _executionTime; String get coloredLeadingBox { - return globals.terminal.color(leadingBox, switch (type) { - ValidationType.success => TerminalColor.green, - ValidationType.crash || ValidationType.missing => TerminalColor.red, - ValidationType.notAvailable || ValidationType.partial => TerminalColor.yellow, - }); + return globals.terminal.color( + leadingBox, + switch (type) { + ValidationType.success => TerminalColor.green, + ValidationType.crash || ValidationType.missing => TerminalColor.red, + ValidationType.notAvailable || + ValidationType.partial => + TerminalColor.yellow, + }); } /// The string representation of the type. String get typeStr => switch (type) { - ValidationType.crash => 'crash', - ValidationType.missing => 'missing', - ValidationType.success => 'installed', - ValidationType.notAvailable => 'notAvailable', - ValidationType.partial => 'partial', - }; + ValidationType.crash => 'crash', + ValidationType.missing => 'missing', + ValidationType.success => 'installed', + ValidationType.notAvailable => 'notAvailable', + ValidationType.partial => 'partial', + }; @override String toString() { @@ -192,22 +217,23 @@ class ValidationMessage { /// /// The [contextUrl] may be supplied to link to external resources. This /// is displayed after the informative message in verbose modes. - const ValidationMessage(this.message, {this.contextUrl, String? piiStrippedMessage}) - : type = ValidationMessageType.information, - piiStrippedMessage = piiStrippedMessage ?? message; + const ValidationMessage(this.message, + {this.contextUrl, String? piiStrippedMessage}) + : type = ValidationMessageType.information, + piiStrippedMessage = piiStrippedMessage ?? message; /// Create a validation message with information for a failing validator. const ValidationMessage.error(this.message, {String? piiStrippedMessage}) - : type = ValidationMessageType.error, - piiStrippedMessage = piiStrippedMessage ?? message, - contextUrl = null; + : type = ValidationMessageType.error, + piiStrippedMessage = piiStrippedMessage ?? message, + contextUrl = null; /// Create a validation message with information for a partially failing /// validator. const ValidationMessage.hint(this.message, {String? piiStrippedMessage}) - : type = ValidationMessageType.hint, - piiStrippedMessage = piiStrippedMessage ?? message, - contextUrl = null; + : type = ValidationMessageType.hint, + piiStrippedMessage = piiStrippedMessage ?? message, + contextUrl = null; final ValidationMessageType type; final String? contextUrl; @@ -223,17 +249,19 @@ class ValidationMessage { bool get isInformation => type == ValidationMessageType.information; String get indicator => switch (type) { - ValidationMessageType.error => '✗', - ValidationMessageType.hint => '!', - ValidationMessageType.information => '•', - }; + ValidationMessageType.error => '✗', + ValidationMessageType.hint => '!', + ValidationMessageType.information => '•', + }; String get coloredIndicator { - return globals.terminal.color(indicator, switch (type) { - ValidationMessageType.error => TerminalColor.red, - ValidationMessageType.hint => TerminalColor.yellow, - ValidationMessageType.information => TerminalColor.green, - }); + return globals.terminal.color( + indicator, + switch (type) { + ValidationMessageType.error => TerminalColor.red, + ValidationMessageType.hint => TerminalColor.yellow, + ValidationMessageType.information => TerminalColor.green, + }); } @override @@ -255,7 +283,7 @@ class NoIdeValidator extends DoctorValidator { NoIdeValidator() : super('Flutter IDE Support'); @override - Future validate() async { + Future validateImpl() async { return ValidationResult( // Info hint to user they do not have a supported IDE installed ValidationType.notAvailable, @@ -273,5 +301,5 @@ class ValidatorWithResult extends DoctorValidator { final ValidationResult result; @override - Future validate() async => result; + Future validateImpl() async => result; } diff --git a/packages/flutter_tools/lib/src/http_host_validator.dart b/packages/flutter_tools/lib/src/http_host_validator.dart index 211838ab6c..bf32a438e3 100644 --- a/packages/flutter_tools/lib/src/http_host_validator.dart +++ b/packages/flutter_tools/lib/src/http_host_validator.dart @@ -18,7 +18,8 @@ const String kMaven = 'https://maven.google.com/'; const String kPubDev = 'https://pub.dev/'; // Overridable environment variables. -const String kPubDevOverride = 'PUB_HOSTED_URL'; // https://dart.dev/tools/pub/environment-variables +const String kPubDevOverride = + 'PUB_HOSTED_URL'; // https://dart.dev/tools/pub/environment-variables // Validator that checks all provided hosts are reachable and responsive class HttpHostValidator extends DoctorValidator { @@ -26,10 +27,10 @@ class HttpHostValidator extends DoctorValidator { required Platform platform, required FeatureFlags featureFlags, required HttpClient httpClient, - }) : _platform = platform, - _featureFlags = featureFlags, - _httpClient = httpClient, - super('Network resources'); + }) : _platform = platform, + _featureFlags = featureFlags, + _httpClient = httpClient, + super('Network resources'); final Platform _platform; final FeatureFlags _featureFlags; @@ -82,7 +83,7 @@ class HttpHostValidator extends DoctorValidator { } @override - Future validate() async { + Future validateImpl() async { final List availabilityResults = []; final List requiredHosts = []; @@ -100,7 +101,8 @@ class HttpHostValidator extends DoctorValidator { requiredHosts.add(Uri.parse(kPubDev)); } if (_platform.environment.containsKey(kFlutterStorageBaseUrl)) { - final Uri? url = _parseUrl(_platform.environment[kFlutterStorageBaseUrl]!); + final Uri? url = + _parseUrl(_platform.environment[kFlutterStorageBaseUrl]!); if (url == null) { availabilityResults.add( 'Environment variable $kFlutterStorageBaseUrl does not specify a valid URL: "${_platform.environment[kFlutterStorageBaseUrl]}"\n' @@ -141,9 +143,12 @@ class HttpHostValidator extends DoctorValidator { if (failures == 0) { assert(successes > 0); assert(messages.isEmpty); - return const ValidationResult(ValidationType.success, [ - ValidationMessage('All expected network resources are available.'), - ]); + return ValidationResult( + ValidationType.success, + const [ + ValidationMessage('All expected network resources are available.') + ], + ); } assert(messages.isNotEmpty); return ValidationResult( diff --git a/packages/flutter_tools/lib/src/intellij/intellij_validator.dart b/packages/flutter_tools/lib/src/intellij/intellij_validator.dart index f53146adaa..2ce0709dc8 100644 --- a/packages/flutter_tools/lib/src/intellij/intellij_validator.dart +++ b/packages/flutter_tools/lib/src/intellij/intellij_validator.dart @@ -91,7 +91,7 @@ abstract class IntelliJValidator extends DoctorValidator { } @override - Future validate() async { + Future validateImpl() async { final List messages = []; if (pluginsPath == null) { diff --git a/packages/flutter_tools/lib/src/linux/linux_doctor.dart b/packages/flutter_tools/lib/src/linux/linux_doctor.dart index ae94d6b189..bab8ae7a45 100644 --- a/packages/flutter_tools/lib/src/linux/linux_doctor.dart +++ b/packages/flutter_tools/lib/src/linux/linux_doctor.dart @@ -54,7 +54,7 @@ class LinuxDoctorValidator extends DoctorValidator { final List _requiredGtkLibraries = ['gtk+-3.0', 'glib-2.0', 'gio-2.0']; @override - Future validate() async { + Future validateImpl() async { ValidationType validationType = ValidationType.success; final List messages = []; diff --git a/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart b/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart index 827bf3ec1c..9c139695de 100644 --- a/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart +++ b/packages/flutter_tools/lib/src/macos/cocoapods_validator.dart @@ -20,7 +20,7 @@ class CocoaPodsValidator extends DoctorValidator { final UserMessages _userMessages; @override - Future validate() async { + Future validateImpl() async { final List messages = []; final CocoaPodsStatus cocoaPodsStatus = await _cocoaPods.evaluateCocoaPodsInstallation; diff --git a/packages/flutter_tools/lib/src/macos/xcode_validator.dart b/packages/flutter_tools/lib/src/macos/xcode_validator.dart index 42cd794a8e..bf9b307139 100644 --- a/packages/flutter_tools/lib/src/macos/xcode_validator.dart +++ b/packages/flutter_tools/lib/src/macos/xcode_validator.dart @@ -32,7 +32,7 @@ class XcodeValidator extends DoctorValidator { final UserMessages _userMessages; @override - Future validate() async { + Future validateImpl() async { final List messages = []; ValidationType xcodeStatus = ValidationType.missing; String? xcodeVersionInfo; diff --git a/packages/flutter_tools/lib/src/proxy_validator.dart b/packages/flutter_tools/lib/src/proxy_validator.dart index c338058ed5..ed0ae1ab00 100644 --- a/packages/flutter_tools/lib/src/proxy_validator.dart +++ b/packages/flutter_tools/lib/src/proxy_validator.dart @@ -12,10 +12,10 @@ import 'doctor_validator.dart'; /// validated along with `NO_PROXY`. class ProxyValidator extends DoctorValidator { ProxyValidator({required Platform platform}) - : shouldShow = _getEnv('HTTP_PROXY', platform).isNotEmpty, - _httpProxy = _getEnv('HTTP_PROXY', platform), - _noProxy = _getEnv('NO_PROXY', platform), - super('Proxy Configuration'); + : shouldShow = _getEnv('HTTP_PROXY', platform).isNotEmpty, + _httpProxy = _getEnv('HTTP_PROXY', platform), + _noProxy = _getEnv('NO_PROXY', platform), + super('Proxy Configuration'); final bool shouldShow; final String _httpProxy; @@ -30,9 +30,10 @@ class ProxyValidator extends DoctorValidator { ''; @override - Future validate() async { + Future validateImpl() async { if (_httpProxy.isEmpty) { - return const ValidationResult(ValidationType.success, []); + return ValidationResult( + ValidationType.success, const []); } final List messages = [ @@ -49,13 +50,16 @@ class ProxyValidator extends DoctorValidator { ], ]; - final bool hasIssues = messages.any((ValidationMessage msg) => msg.isHint || msg.isError); + final bool hasIssues = + messages.any((ValidationMessage msg) => msg.isHint || msg.isError); - return ValidationResult(hasIssues ? ValidationType.partial : ValidationType.success, messages); + return ValidationResult( + hasIssues ? ValidationType.partial : ValidationType.success, messages); } Future> _getLoopbackAddresses() async { - final List networkInterfaces = await listNetworkInterfaces( + final List networkInterfaces = + await listNetworkInterfaces( includeLinkLocal: true, includeLoopback: true, ); @@ -63,7 +67,8 @@ class ProxyValidator extends DoctorValidator { return [ 'localhost', for (final NetworkInterface networkInterface in networkInterfaces) - for (final InternetAddress internetAddress in networkInterface.addresses) + for (final InternetAddress internetAddress + in networkInterface.addresses) if (internetAddress.isLoopback) internetAddress.address, ]; } diff --git a/packages/flutter_tools/lib/src/vscode/vscode_validator.dart b/packages/flutter_tools/lib/src/vscode/vscode_validator.dart index 0ea4340172..94986d71e5 100644 --- a/packages/flutter_tools/lib/src/vscode/vscode_validator.dart +++ b/packages/flutter_tools/lib/src/vscode/vscode_validator.dart @@ -27,16 +27,17 @@ class VsCodeValidator extends DoctorValidator { } @override - Future validate() async { - final List validationMessages = List.from( - _vsCode.validationMessages, - ); + Future validateImpl() async { + final List validationMessages = + List.from(_vsCode.validationMessages); - final String vsCodeVersionText = - _vsCode.version == null ? 'version unknown' : 'version ${_vsCode.version}'; + final String vsCodeVersionText = _vsCode.version == null + ? 'version unknown' + : 'version ${_vsCode.version}'; if (_vsCode.version == null) { - validationMessages.add(const ValidationMessage.error('Unable to determine VS Code version.')); + validationMessages.add(const ValidationMessage.error( + 'Unable to determine VS Code version.')); } return ValidationResult( diff --git a/packages/flutter_tools/lib/src/web/web_validator.dart b/packages/flutter_tools/lib/src/web/web_validator.dart index f4268b94a5..3b7ffcc0f5 100644 --- a/packages/flutter_tools/lib/src/web/web_validator.dart +++ b/packages/flutter_tools/lib/src/web/web_validator.dart @@ -8,14 +8,14 @@ import 'chrome.dart'; /// A validator for Chromium-based browsers. abstract class ChromiumValidator extends DoctorValidator { - const ChromiumValidator(super.title); + ChromiumValidator(super.title); Platform get _platform; ChromiumLauncher get _chromiumLauncher; String get _name; @override - Future validate() async { + Future validateImpl() async { final bool canRunChromium = _chromiumLauncher.canFindExecutable(); final String chromiumSearchLocation = _chromiumLauncher.findExecutable(); final List messages = [ @@ -45,10 +45,12 @@ abstract class ChromiumValidator extends DoctorValidator { /// A validator that checks whether Chrome is installed and can run. class ChromeValidator extends ChromiumValidator { - const ChromeValidator({required Platform platform, required ChromiumLauncher chromiumLauncher}) - : _platform = platform, - _chromiumLauncher = chromiumLauncher, - super('Chrome - develop for the web'); + ChromeValidator({ + required Platform platform, + required ChromiumLauncher chromiumLauncher, + }) : _platform = platform, + _chromiumLauncher = chromiumLauncher, + super('Chrome - develop for the web'); @override final Platform _platform; @@ -62,10 +64,12 @@ class ChromeValidator extends ChromiumValidator { /// A validator that checks whether Edge is installed and can run. class EdgeValidator extends ChromiumValidator { - const EdgeValidator({required Platform platform, required ChromiumLauncher chromiumLauncher}) - : _platform = platform, - _chromiumLauncher = chromiumLauncher, - super('Edge - develop for the web'); + EdgeValidator({ + required Platform platform, + required ChromiumLauncher chromiumLauncher, + }) : _platform = platform, + _chromiumLauncher = chromiumLauncher, + super('Edge - develop for the web'); @override final Platform _platform; diff --git a/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart b/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart index 1eeb2922e3..b215125646 100644 --- a/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart +++ b/packages/flutter_tools/lib/src/windows/visual_studio_validator.dart @@ -10,7 +10,7 @@ import 'visual_studio.dart'; VisualStudioValidator? get visualStudioValidator => context.get(); class VisualStudioValidator extends DoctorValidator { - const VisualStudioValidator({ + VisualStudioValidator({ required VisualStudio visualStudio, required UserMessages userMessages, }) : _visualStudio = visualStudio, @@ -21,7 +21,7 @@ class VisualStudioValidator extends DoctorValidator { final UserMessages _userMessages; @override - Future validate() async { + Future validateImpl() async { final List messages = []; ValidationType status = ValidationType.missing; String? versionInfo; diff --git a/packages/flutter_tools/lib/src/windows/windows_version_validator.dart b/packages/flutter_tools/lib/src/windows/windows_version_validator.dart index 9821e0c877..6a9ffd9eac 100644 --- a/packages/flutter_tools/lib/src/windows/windows_version_validator.dart +++ b/packages/flutter_tools/lib/src/windows/windows_version_validator.dart @@ -14,7 +14,8 @@ const List kUnsupportedVersions = ['6', '7', '8']; /// Regex pattern for identifying line from systeminfo stdout with windows version /// (ie. 10.0.22631.4037) -const String kWindowsOSVersionSemVerPattern = r'([0-9]+)\.([0-9]+)\.([0-9]+)\.?([0-9\.]+)?'; +const String kWindowsOSVersionSemVerPattern = + r'([0-9]+)\.([0-9]+)\.([0-9]+)\.?([0-9\.]+)?'; /// Regex pattern for identifying a running instance of the Topaz OFD process. /// This is a known process that interferes with the build toolchain. @@ -23,14 +24,14 @@ const String kCoreProcessPattern = r'Topaz\s+OFD\\Warsaw\\core\.exe'; /// Validator for supported Windows host machine operating system version. class WindowsVersionValidator extends DoctorValidator { - const WindowsVersionValidator({ + WindowsVersionValidator({ required OperatingSystemUtils operatingSystemUtils, required ProcessLister processLister, required WindowsVersionExtractor versionExtractor, - }) : _operatingSystemUtils = operatingSystemUtils, - _processLister = processLister, - _versionExtractor = versionExtractor, - super('Windows Version'); + }) : _operatingSystemUtils = operatingSystemUtils, + _processLister = processLister, + _versionExtractor = versionExtractor, + super('Windows Version'); // See https://learn.microsoft.com/en-us/windows/release-health/windows11-release-information static const int _lowestWindows11BuildNumber = 22000; @@ -41,35 +42,47 @@ class WindowsVersionValidator extends DoctorValidator { Future _topazScan() async { if (!_processLister.canRunPowershell()) { - return const ValidationResult(ValidationType.missing, [ - ValidationMessage.hint( - 'Failed to find ${ProcessLister.powershell} or ${ProcessLister.pwsh} on PATH', - ), - ]); + return ValidationResult( + ValidationType.missing, + const [ + ValidationMessage.hint( + 'Failed to find ${ProcessLister.powershell} or ${ProcessLister.pwsh} on PATH'), + ], + ); } - final ProcessResult getProcessesResult = await _processLister.getProcessesWithPath(); + final ProcessResult getProcessesResult = + await _processLister.getProcessesWithPath(); if (getProcessesResult.exitCode != 0) { - return const ValidationResult(ValidationType.missing, [ - ValidationMessage.hint('Get-Process failed to complete'), - ]); + return ValidationResult( + ValidationType.missing, + const [ + ValidationMessage.hint('Get-Process failed to complete'), + ], + ); } - final RegExp topazRegex = RegExp(kCoreProcessPattern, caseSensitive: false, multiLine: true); + final RegExp topazRegex = + RegExp(kCoreProcessPattern, caseSensitive: false, multiLine: true); final String processes = getProcessesResult.stdout as String; final bool topazFound = topazRegex.hasMatch(processes); if (topazFound) { - return const ValidationResult(ValidationType.missing, [ - ValidationMessage.hint( - 'The Topaz OFD Security Module was detected on your machine. ' - 'You may need to disable it to build Flutter applications.', - ), - ]); + return ValidationResult( + ValidationType.missing, + const [ + ValidationMessage.hint( + 'The Topaz OFD Security Module was detected on your machine. ' + 'You may need to disable it to build Flutter applications.', + ), + ], + ); } - return const ValidationResult(ValidationType.success, []); + return ValidationResult( + ValidationType.success, const []); } @override - Future validate() async { - final RegExp regex = RegExp(kWindowsOSVersionSemVerPattern, multiLine: true); + Future validateImpl() async { + final RegExp regex = + RegExp(kWindowsOSVersionSemVerPattern, multiLine: true); final String commandResult = _operatingSystemUtils.name; final Iterable matches = regex.allMatches(commandResult); @@ -78,13 +91,15 @@ class WindowsVersionValidator extends DoctorValidator { ValidationType windowsVersionStatus; final List messages = []; String statusInfo; - if (matches.length == 1 && !kUnsupportedVersions.contains(matches.elementAt(0).group(1))) { + if (matches.length == 1 && + !kUnsupportedVersions.contains(matches.elementAt(0).group(1))) { windowsVersionStatus = ValidationType.success; - final WindowsVersionExtractionResult details = await _versionExtractor.getDetails(); + final WindowsVersionExtractionResult details = + await _versionExtractor.getDetails(); String? caption = details.caption; if (caption == null || caption.isEmpty) { - final bool isWindows11 = - int.parse(matches.elementAt(0).group(3)!) > _lowestWindows11BuildNumber; + final bool isWindows11 = int.parse(matches.elementAt(0).group(3)!) > + _lowestWindows11BuildNumber; if (isWindows11) { caption = 'Windows 11 or higher'; } else { @@ -99,7 +114,9 @@ class WindowsVersionValidator extends DoctorValidator { // Check if the Topaz OFD security module is running, and warn the user if it is. // See https://github.com/flutter/flutter/issues/121366 - final List subResults = [await _topazScan()]; + final List subResults = [ + await _topazScan() + ]; for (final ValidationResult subResult in subResults) { if (subResult.type != ValidationType.success) { statusInfo = 'Problem detected with Windows installation'; @@ -109,10 +126,12 @@ class WindowsVersionValidator extends DoctorValidator { } } else { windowsVersionStatus = ValidationType.missing; - statusInfo = 'Unable to determine Windows version (command `ver` returned $commandResult)'; + statusInfo = + 'Unable to determine Windows version (command `ver` returned $commandResult)'; } - return ValidationResult(windowsVersionStatus, messages, statusInfo: statusInfo); + return ValidationResult(windowsVersionStatus, messages, + statusInfo: statusInfo); } } @@ -147,9 +166,10 @@ class ProcessLister { /// the edition and the processor architecture), releaseId and displayVersion and are returned via the /// [WindowsVersionExtractionResult] class. class WindowsVersionExtractor { - WindowsVersionExtractor({required ProcessManager processManager, required Logger logger}) - : _logger = logger, - _processManager = processManager; + WindowsVersionExtractor( + {required ProcessManager processManager, required Logger logger}) + : _logger = logger, + _processManager = processManager; final ProcessManager _processManager; final Logger _logger; @@ -169,12 +189,16 @@ class WindowsVersionExtractor { if (output != null) { final List parts = output.split('\n'); if (parts.length >= 2) { - caption = parts[1].replaceAll('Microsoft Windows', '').replaceAll(' ', ' ').trim(); + caption = parts[1] + .replaceAll('Microsoft Windows', '') + .replaceAll(' ', ' ') + .trim(); } } } } on ProcessException catch (e) { - _logger.printTrace('Failed to get Caption and OSArchitecture from WMI: $e'); + _logger + .printTrace('Failed to get Caption and OSArchitecture from WMI: $e'); } try { @@ -190,9 +214,13 @@ class WindowsVersionExtractor { final String? output = osDetails.stdout as String?; if (output != null) { final Map data = Map.fromEntries( - output.split('\n').where((String line) => line.contains('REG_SZ')).map((String line) { + output + .split('\n') + .where((String line) => line.contains('REG_SZ')) + .map((String line) { final List parts = line.split('REG_SZ'); - return MapEntry(parts.first.trim(), parts.last.trim()); + return MapEntry( + parts.first.trim(), parts.last.trim()); }), ); releaseId = data['ReleaseId']; @@ -200,7 +228,8 @@ class WindowsVersionExtractor { } } } on ProcessException catch (e) { - _logger.printTrace('Failed to get ReleaseId and DisplayVersion from registry: $e'); + _logger.printTrace( + 'Failed to get ReleaseId and DisplayVersion from registry: $e'); } return WindowsVersionExtractionResult( @@ -221,7 +250,8 @@ final class WindowsVersionExtractionResult { }); factory WindowsVersionExtractionResult.empty() { - return WindowsVersionExtractionResult(caption: null, releaseId: null, displayVersion: null); + return WindowsVersionExtractionResult( + caption: null, releaseId: null, displayVersion: null); } final String? caption; diff --git a/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart index b8136a9786..371625d84c 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/doctor_test.dart @@ -44,7 +44,8 @@ void main() { fs = MemoryFileSystem.test(); }); - testWithoutContext('ValidationMessage equality and hashCode includes contextUrl', () { + testWithoutContext( + 'ValidationMessage equality and hashCode includes contextUrl', () { const ValidationMessage messageA = ValidationMessage('ab', contextUrl: 'a'); const ValidationMessage messageB = ValidationMessage('ab', contextUrl: 'b'); @@ -65,7 +66,8 @@ void main() { ValidationMessage message = result.messages.firstWhere( (ValidationMessage m) => m.message.startsWith('VS Code '), ); - expect(message.message, 'VS Code at ${VsCodeValidatorTestTargets.validInstall}'); + expect(message.message, + 'VS Code at ${VsCodeValidatorTestTargets.validInstall}'); message = result.messages.firstWhere( (ValidationMessage m) => m.message.startsWith('Flutter '), @@ -74,7 +76,8 @@ void main() { expect(message.isError, isFalse); }); - testUsingContext('No IDE Validator includes expected installation messages', () async { + testUsingContext('No IDE Validator includes expected installation messages', + () async { final ValidationResult result = await NoIdeValidator().validate(); expect(result.type, ValidationType.notAvailable); @@ -89,8 +92,9 @@ void main() { VsCodeValidatorTestTargets.installedWithExtension64bit.title, 'VS Code, 64-bit edition', ); - final ValidationResult result = - await VsCodeValidatorTestTargets.installedWithExtension64bit.validate(); + final ValidationResult result = await VsCodeValidatorTestTargets + .installedWithExtension64bit + .validate(); expect(result.type, ValidationType.success); expect(result.statusInfo, 'version 1.2.3'); expect(result.messages, hasLength(2)); @@ -98,7 +102,8 @@ void main() { ValidationMessage message = result.messages.firstWhere( (ValidationMessage m) => m.message.startsWith('VS Code '), ); - expect(message.message, 'VS Code at ${VsCodeValidatorTestTargets.validInstall}'); + expect(message.message, + 'VS Code at ${VsCodeValidatorTestTargets.validInstall}'); message = result.messages.firstWhere( (ValidationMessage m) => m.message.startsWith('Flutter '), @@ -116,12 +121,14 @@ void main() { ValidationMessage message = result.messages.firstWhere( (ValidationMessage m) => m.message.startsWith('VS Code '), ); - expect(message.message, 'VS Code at ${VsCodeValidatorTestTargets.validInstall}'); + expect(message.message, + 'VS Code at ${VsCodeValidatorTestTargets.validInstall}'); message = result.messages.firstWhere( (ValidationMessage m) => m.message.startsWith('Flutter '), ); - expect(message.message, startsWith('Flutter extension can be installed from')); + expect(message.message, + startsWith('Flutter extension can be installed from')); expect( message.contextUrl, 'https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter', @@ -145,8 +152,8 @@ void main() { }); testWithoutContext('diagnostic message', () async { - final FakeDeviceManager deviceManager = - FakeDeviceManager()..diagnostics = ['Device locked']; + final FakeDeviceManager deviceManager = FakeDeviceManager() + ..diagnostics = ['Device locked']; final DeviceValidator deviceValidator = DeviceValidator( deviceManager: deviceManager, @@ -154,16 +161,16 @@ void main() { ); final ValidationResult result = await deviceValidator.validate(); expect(result.type, ValidationType.notAvailable); - expect(result.messages, const [ValidationMessage.hint('Device locked')]); + expect(result.messages, + const [ValidationMessage.hint('Device locked')]); expect(result.statusInfo, isNull); }); testWithoutContext('diagnostic message and devices', () async { final FakeDevice device = FakeDevice(); - final FakeDeviceManager deviceManager = - FakeDeviceManager() - ..devices = [device] - ..diagnostics = ['Device locked']; + final FakeDeviceManager deviceManager = FakeDeviceManager() + ..devices = [device] + ..diagnostics = ['Device locked']; final DeviceValidator deviceValidator = DeviceValidator( deviceManager: deviceManager, @@ -184,7 +191,8 @@ void main() { testUsingContext( 'validate non-verbose output format for run without issues', () async { - final Doctor doctor = Doctor(logger: logger, clock: const SystemClock()); + final Doctor doctor = + Doctor(logger: logger, clock: const SystemClock()); expect(await doctor.diagnose(verbose: false), isTrue); expect( logger.statusText, @@ -215,13 +223,15 @@ void main() { testUsingContext( 'contains installed', () async { - final Doctor doctor = Doctor(logger: logger, clock: const SystemClock()); + final Doctor doctor = + Doctor(logger: logger, clock: const SystemClock()); await doctor.diagnose(verbose: false); expect(testUsage.events.length, 3); expect( testUsage.events, - contains(const TestUsageEvent('doctor-result', 'PassingValidator', label: 'installed')), + contains(const TestUsageEvent('doctor-result', 'PassingValidator', + label: 'installed')), ); }, overrides: { @@ -236,10 +246,14 @@ void main() { expect( testUsage.events, unorderedEquals([ - const TestUsageEvent('doctor-result', 'PassingValidator', label: 'installed'), - const TestUsageEvent('doctor-result', 'PassingValidator', label: 'installed'), - const TestUsageEvent('doctor-result', 'PartialValidatorWithHintsOnly', label: 'partial'), - const TestUsageEvent('doctor-result', 'PartialValidatorWithErrors', label: 'partial'), + const TestUsageEvent('doctor-result', 'PassingValidator', + label: 'installed'), + const TestUsageEvent('doctor-result', 'PassingValidator', + label: 'installed'), + const TestUsageEvent('doctor-result', 'PartialValidatorWithHintsOnly', + label: 'partial'), + const TestUsageEvent('doctor-result', 'PartialValidatorWithErrors', + label: 'partial'), ]), ); }, overrides: {Usage: () => testUsage}); @@ -250,11 +264,16 @@ void main() { expect( testUsage.events, unorderedEquals([ - const TestUsageEvent('doctor-result', 'PassingValidator', label: 'installed'), - const TestUsageEvent('doctor-result', 'MissingValidator', label: 'missing'), - const TestUsageEvent('doctor-result', 'NotAvailableValidator', label: 'notAvailable'), - const TestUsageEvent('doctor-result', 'PartialValidatorWithHintsOnly', label: 'partial'), - const TestUsageEvent('doctor-result', 'PartialValidatorWithErrors', label: 'partial'), + const TestUsageEvent('doctor-result', 'PassingValidator', + label: 'installed'), + const TestUsageEvent('doctor-result', 'MissingValidator', + label: 'missing'), + const TestUsageEvent('doctor-result', 'NotAvailableValidator', + label: 'notAvailable'), + const TestUsageEvent('doctor-result', 'PartialValidatorWithHintsOnly', + label: 'partial'), + const TestUsageEvent('doctor-result', 'PartialValidatorWithErrors', + label: 'partial'), ]), ); }, overrides: {Usage: () => testUsage}); @@ -267,10 +286,14 @@ void main() { expect( testUsage.events, unorderedEquals([ - const TestUsageEvent('doctor-result', 'PassingGroupedValidator', label: 'installed'), - const TestUsageEvent('doctor-result', 'PassingGroupedValidator', label: 'installed'), - const TestUsageEvent('doctor-result', 'PassingGroupedValidator', label: 'installed'), - const TestUsageEvent('doctor-result', 'MissingGroupedValidator', label: 'missing'), + const TestUsageEvent('doctor-result', 'PassingGroupedValidator', + label: 'installed'), + const TestUsageEvent('doctor-result', 'PassingGroupedValidator', + label: 'installed'), + const TestUsageEvent('doctor-result', 'PassingGroupedValidator', + label: 'installed'), + const TestUsageEvent('doctor-result', 'MissingGroupedValidator', + label: 'missing'), ]), ); }, @@ -278,7 +301,8 @@ void main() { ); testUsingContext('sending events can be skipped', () async { - await FakePassingDoctor(logger).diagnose(verbose: false, sendEvent: false); + await FakePassingDoctor(logger) + .diagnose(verbose: false, sendEvent: false); expect(testUsage.events, isEmpty); }, overrides: {Usage: () => testUsage}); @@ -308,7 +332,8 @@ void main() { testUsingContext( 'validate non-verbose output format for run with crash', () async { - expect(await FakeCrashingDoctor(logger).diagnose(verbose: false), isFalse); + expect( + await FakeCrashingDoctor(logger).diagnose(verbose: false), isFalse); expect( logger.statusText, equals( @@ -329,7 +354,9 @@ void main() { overrides: {AnsiTerminal: () => FakeTerminal()}, ); - testUsingContext('validate verbose output format contains trace for run with crash', () async { + testUsingContext( + 'validate verbose output format contains trace for run with crash', + () async { expect(await FakeCrashingDoctor(logger).diagnose(), isFalse); expect(logger.statusText, contains('#0 CrashingValidator.validate')); }); @@ -346,7 +373,8 @@ void main() { expect( logger.statusText, - contains('Stuck validator that never completes exceeded maximum allowed duration of '), + contains( + 'Stuck validator that never completes exceeded maximum allowed duration of '), ); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -358,7 +386,9 @@ void main() { final Completer completer = Completer(); await FakeAsync().run((FakeAsync time) { unawaited( - FakeAsyncCrashingDoctor(time, logger).diagnose(verbose: false).then((bool r) { + FakeAsyncCrashingDoctor(time, logger) + .diagnose(verbose: false) + .then((bool r) { expect(r, isFalse); completer.complete(); }), @@ -390,7 +420,8 @@ void main() { testUsingContext( 'validate non-verbose output format when only one category fails', () async { - expect(await FakeSinglePassingDoctor(logger).diagnose(verbose: false), isTrue); + expect(await FakeSinglePassingDoctor(logger).diagnose(verbose: false), + isTrue); expect( logger.statusText, equals( @@ -408,7 +439,8 @@ void main() { testUsingContext( 'validate non-verbose output format for a passing run', () async { - expect(await FakePassingDoctor(logger).diagnose(verbose: false), isTrue); + expect( + await FakePassingDoctor(logger).diagnose(verbose: false), isTrue); expect( logger.statusText, equals( @@ -459,60 +491,55 @@ void main() { testUsingContext('validate verbose output format', () async { expect(await FakeDoctor(logger).diagnose(), isFalse); expect( - logger.statusText, - equals( - '[✓] Passing Validator (with statusInfo)\n' - ' • A helpful message\n' - ' • A second, somewhat longer helpful message\n' - '\n' - '[✗] Missing Validator\n' - ' ✗ A useful error message\n' - ' • A message that is not an error\n' - ' ! A hint message\n' - '\n' - '[!] Not Available Validator\n' - ' ✗ A useful error message\n' - ' • A message that is not an error\n' - ' ! A hint message\n' - '\n' - '[!] Partial Validator with only a Hint\n' - ' ! There is a hint here\n' - ' • But there is no error\n' - '\n' - '[!] Partial Validator with Errors\n' - ' ✗ An error message indicating partial installation\n' - ' ! Maybe a hint will help the user\n' - ' • An extra message with some verbose details\n' - '\n' - '! Doctor found issues in 4 categories.\n', - ), - ); - }, overrides: {AnsiTerminal: () => FakeTerminal()}); + logger.statusText, + equals('[✓] Passing Validator (with statusInfo) [0ms]\n' + ' • A helpful message\n' + ' • A second, somewhat longer helpful message\n' + '\n' + '[✗] Missing Validator [0ms]\n' + ' ✗ A useful error message\n' + ' • A message that is not an error\n' + ' ! A hint message\n' + '\n' + '[!] Not Available Validator [0ms]\n' + ' ✗ A useful error message\n' + ' • A message that is not an error\n' + ' ! A hint message\n' + '\n' + '[!] Partial Validator with only a Hint [0ms]\n' + ' ! There is a hint here\n' + ' • But there is no error\n' + '\n' + '[!] Partial Validator with Errors [0ms]\n' + ' ✗ An error message indicating partial installation\n' + ' ! Maybe a hint will help the user\n' + ' • An extra message with some verbose details\n' + '\n' + '! Doctor found issues in 4 categories.\n')); + }, overrides: { + AnsiTerminal: () => FakeTerminal(), + }); testUsingContext('validate PII can be hidden', () async { expect(await FakePiiDoctor(logger).diagnose(showPii: false), isTrue); expect( - logger.statusText, - equals( - '[✓] PII Validator\n' - ' • Does not contain PII\n' - '\n' - '• No issues found!\n', - ), - ); + logger.statusText, + equals('[✓] PII Validator [0ms]\n' + ' • Does not contain PII\n' + '\n' + '• No issues found!\n')); logger.clear(); // PII shown. expect(await FakePiiDoctor(logger).diagnose(), isTrue); expect( - logger.statusText, - equals( - '[✓] PII Validator\n' - ' • Contains PII path/to/username\n' - '\n' - '• No issues found!\n', - ), - ); - }, overrides: {AnsiTerminal: () => FakeTerminal()}); + logger.statusText, + equals('[✓] PII Validator [0ms]\n' + ' • Contains PII path/to/username\n' + '\n' + '• No issues found!\n')); + }, overrides: { + AnsiTerminal: () => FakeTerminal(), + }); }); group('doctor diagnosis wrapper', () { @@ -529,13 +556,11 @@ void main() { () async { final Doctor fakeDoctor = FakePiiDoctor(logger); final DoctorText doctorText = DoctorText(logger, doctor: fakeDoctor); - const String expectedPiiText = - '[✓] PII Validator\n' + const String expectedPiiText = '[✓] PII Validator [0ms]\n' ' • Contains PII path/to/username\n' '\n' '• No issues found!\n'; - const String expectedPiiStrippedText = - '[✓] PII Validator\n' + const String expectedPiiStrippedText = '[✓] PII Validator [0ms]\n' ' • Does not contain PII\n' '\n' '• No issues found!\n'; @@ -550,10 +575,14 @@ void main() { // Only one event sent. expect(testUsage.events, [ - const TestUsageEvent('doctor-result', 'PiiValidator', label: 'installed'), + const TestUsageEvent('doctor-result', 'PiiValidator', + label: 'installed'), ]); }, - overrides: {AnsiTerminal: () => FakeTerminal(), Usage: () => testUsage}, + overrides: { + AnsiTerminal: () => FakeTerminal(), + Usage: () => testUsage + }, ); testUsingContext( @@ -618,30 +647,31 @@ void main() { wrapLogger.statusText, equals( '[✓] Passing Validator (with\n' - ' statusInfo)\n' + ' statusInfo) [0ms]\n' ' • A helpful message\n' ' • A second, somewhat\n' ' longer helpful message\n' '\n' - '[✗] Missing Validator\n' + '[✗] Missing Validator [0ms]\n' ' ✗ A useful error message\n' ' • A message that is not an\n' ' error\n' ' ! A hint message\n' '\n' '[!] Not Available Validator\n' + ' [0ms]\n' ' ✗ A useful error message\n' ' • A message that is not an\n' ' error\n' ' ! A hint message\n' '\n' '[!] Partial Validator with\n' - ' only a Hint\n' + ' only a Hint [0ms]\n' ' ! There is a hint here\n' ' • But there is no error\n' '\n' '[!] Partial Validator with\n' - ' Errors\n' + ' Errors [0ms]\n' ' ✗ An error message\n' ' indicating partial\n' ' installation\n' @@ -657,57 +687,50 @@ void main() { }, overrides: {AnsiTerminal: () => FakeTerminal()}); group('doctor with grouped validators', () { - testUsingContext( - 'validate diagnose combines validator output', - () async { - expect(await FakeGroupedDoctor(logger).diagnose(), isTrue); - expect( + testUsingContext('validate diagnose combines validator output', () async { + expect(await FakeGroupedDoctor(logger).diagnose(), isTrue); + expect( logger.statusText, - equals( - '[✓] Category 1\n' - ' • A helpful message\n' - ' • A helpful message\n' - '\n' - '[!] Category 2\n' - ' • A helpful message\n' - ' ✗ A useful error message\n' - '\n' - '! Doctor found issues in 1 category.\n', - ), - ); - }, - overrides: {AnsiTerminal: () => FakeTerminal()}, - ); + equals('[✓] Category 1 [0ms]\n' + ' • A helpful message\n' + ' • A helpful message\n' + '\n' + '[!] Category 2 [0ms]\n' + ' • A helpful message\n' + ' ✗ A useful error message\n' + '\n' + '! Doctor found issues in 1 category.\n')); + }, overrides: { + AnsiTerminal: () => FakeTerminal(), + }); - testUsingContext( - 'validate merging assigns statusInfo and title', - () async { - // There are two subvalidators. Only the second contains statusInfo. - expect(await FakeGroupedDoctorWithStatus(logger).diagnose(), isTrue); - expect( + testUsingContext('validate merging assigns statusInfo and title', () async { + // There are two subvalidators. Only the second contains statusInfo. + expect(await FakeGroupedDoctorWithStatus(logger).diagnose(), isTrue); + expect( logger.statusText, - equals( - '[✓] First validator title (A status message)\n' - ' • A helpful message\n' - ' • A different message\n' - '\n' - '• No issues found!\n', - ), - ); - }, - overrides: {AnsiTerminal: () => FakeTerminal()}, - ); + equals('[✓] First validator title (A status message) [0ms]\n' + ' • A helpful message\n' + ' • A different message\n' + '\n' + '• No issues found!\n')); + }, overrides: { + AnsiTerminal: () => FakeTerminal(), + }); }); group('grouped validator merging results', () { - final PassingGroupedValidator installed = PassingGroupedValidator('Category'); + final PassingGroupedValidator installed = + PassingGroupedValidator('Category'); final PartialGroupedValidator partial = PartialGroupedValidator('Category'); final MissingGroupedValidator missing = MissingGroupedValidator('Category'); testUsingContext( 'validate installed + installed = installed', () async { - expect(await FakeSmallGroupDoctor(logger, installed, installed).diagnose(), isTrue); + expect( + await FakeSmallGroupDoctor(logger, installed, installed).diagnose(), + isTrue); expect(logger.statusText, startsWith('[✓]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -716,7 +739,9 @@ void main() { testUsingContext( 'validate installed + partial = partial', () async { - expect(await FakeSmallGroupDoctor(logger, installed, partial).diagnose(), isTrue); + expect( + await FakeSmallGroupDoctor(logger, installed, partial).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -725,7 +750,9 @@ void main() { testUsingContext( 'validate installed + missing = partial', () async { - expect(await FakeSmallGroupDoctor(logger, installed, missing).diagnose(), isTrue); + expect( + await FakeSmallGroupDoctor(logger, installed, missing).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -734,7 +761,9 @@ void main() { testUsingContext( 'validate partial + installed = partial', () async { - expect(await FakeSmallGroupDoctor(logger, partial, installed).diagnose(), isTrue); + expect( + await FakeSmallGroupDoctor(logger, partial, installed).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -743,7 +772,8 @@ void main() { testUsingContext( 'validate partial + partial = partial', () async { - expect(await FakeSmallGroupDoctor(logger, partial, partial).diagnose(), isTrue); + expect(await FakeSmallGroupDoctor(logger, partial, partial).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -752,7 +782,8 @@ void main() { testUsingContext( 'validate partial + missing = partial', () async { - expect(await FakeSmallGroupDoctor(logger, partial, missing).diagnose(), isTrue); + expect(await FakeSmallGroupDoctor(logger, partial, missing).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -761,7 +792,9 @@ void main() { testUsingContext( 'validate missing + installed = partial', () async { - expect(await FakeSmallGroupDoctor(logger, missing, installed).diagnose(), isTrue); + expect( + await FakeSmallGroupDoctor(logger, missing, installed).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -770,7 +803,8 @@ void main() { testUsingContext( 'validate missing + partial = partial', () async { - expect(await FakeSmallGroupDoctor(logger, missing, partial).diagnose(), isTrue); + expect(await FakeSmallGroupDoctor(logger, missing, partial).diagnose(), + isTrue); expect(logger.statusText, startsWith('[!]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -779,7 +813,8 @@ void main() { testUsingContext( 'validate missing + missing = missing', () async { - expect(await FakeSmallGroupDoctor(logger, missing, missing).diagnose(), isFalse); + expect(await FakeSmallGroupDoctor(logger, missing, missing).diagnose(), + isFalse); expect(logger.statusText, startsWith('[✗]')); }, overrides: {AnsiTerminal: () => FakeTerminal()}, @@ -789,11 +824,10 @@ void main() { testUsingContext( 'WebWorkflow is a part of validator workflows if enabled', () async { - final List workflows = - DoctorValidatorsProvider.test( - featureFlags: TestFeatureFlags(isWebEnabled: true), - platform: FakePlatform(), - ).workflows; + final List workflows = DoctorValidatorsProvider.test( + featureFlags: TestFeatureFlags(isWebEnabled: true), + platform: FakePlatform(), + ).workflows; expect(workflows, contains(isA())); }, overrides: { @@ -805,14 +839,16 @@ void main() { testUsingContext( 'CustomDevicesWorkflow is a part of validator workflows if enabled', () async { - final List workflows = - DoctorValidatorsProvider.test( - featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), - platform: FakePlatform(), - ).workflows; + final List workflows = DoctorValidatorsProvider.test( + featureFlags: TestFeatureFlags(areCustomDevicesEnabled: true), + platform: FakePlatform(), + ).workflows; expect(workflows, contains(isA())); }, - overrides: {FileSystem: () => fs, ProcessManager: () => fakeProcessManager}, + overrides: { + FileSystem: () => fs, + ProcessManager: () => fakeProcessManager + }, ); group('FlutterValidator', () { @@ -835,17 +871,21 @@ void main() { final Directory devtoolsDir = fs.directory( '/path/to/flutter/bin/cache/dart-sdk/bin/resources/devtools', )..createSync(recursive: true); - fs.directory('/path/to/flutter/bin/cache/artifacts').createSync(recursive: true); - devtoolsDir.childFile('version.json').writeAsStringSync('{"version": "123"}'); + fs + .directory('/path/to/flutter/bin/cache/artifacts') + .createSync(recursive: true); + devtoolsDir + .childFile('version.json') + .writeAsStringSync('{"version": "123"}'); fakeProcessManager.addCommands(const [ FakeCommand(command: ['which', 'java']), ]); - final List validators = - DoctorValidatorsProvider.test( - featureFlags: featureFlags, - platform: FakePlatform(), - ).validators; - final FlutterValidator flutterValidator = validators.whereType().first; + final List validators = DoctorValidatorsProvider.test( + featureFlags: featureFlags, + platform: FakePlatform(), + ).validators; + final FlutterValidator flutterValidator = + validators.whereType().first; final ValidationResult result = await flutterValidator.validate(); expect( result.messages.map((ValidationMessage msg) => msg.message), @@ -853,8 +893,7 @@ void main() { ); }, overrides: { - Cache: - () => Cache.test( + Cache: () => Cache.test( rootOverride: fs.directory('/path/to/flutter'), fileSystem: fs, processManager: fakeProcessManager, @@ -873,8 +912,10 @@ void main() { final DoctorValidatorsProvider provider = DoctorValidatorsProvider.test( featureFlags: TestFeatureFlags(isAndroidEnabled: false), ); - expect(provider.validators, isNot(contains(isA()))); - expect(provider.validators, isNot(contains(isA()))); + expect( + provider.validators, isNot(contains(isA()))); + expect(provider.validators, + isNot(contains(isA()))); }, overrides: { AndroidWorkflow: () => FakeAndroidWorkflow(appliesToHostPlatform: false), @@ -925,13 +966,16 @@ void main() { ); expect(fakeAnalytics.sentEvents, contains(eventToFind)); }, - overrides: {DoctorValidatorsProvider: () => FakeDoctorValidatorsProvider()}, + overrides: { + DoctorValidatorsProvider: () => FakeDoctorValidatorsProvider() + }, ); testUsingContext( 'contains installed and partial', () async { - await FakePassingDoctor(logger, clock: fakeSystemClock).diagnose(verbose: false); + await FakePassingDoctor(logger, clock: fakeSystemClock) + .diagnose(verbose: false); expect(fakeAnalytics.sentEvents, hasLength(4)); expect( @@ -975,7 +1019,8 @@ void main() { testUsingContext( 'contains installed, missing and partial', () async { - await FakeDoctor(logger, clock: fakeSystemClock).diagnose(verbose: false); + await FakeDoctor(logger, clock: fakeSystemClock) + .diagnose(verbose: false); expect(fakeAnalytics.sentEvents, hasLength(5)); expect( @@ -1024,7 +1069,8 @@ void main() { testUsingContext( 'events for grouped validators are properly decomposed', () async { - await FakeGroupedDoctor(logger, clock: fakeSystemClock).diagnose(verbose: false); + await FakeGroupedDoctor(logger, clock: fakeSystemClock) + .diagnose(verbose: false); expect(fakeAnalytics.sentEvents, hasLength(4)); expect( @@ -1066,14 +1112,18 @@ void main() { testUsingContext( 'grouped validator subresult and subvalidators different lengths', () async { - final FakeGroupedDoctorWithCrash fakeDoctor = FakeGroupedDoctorWithCrash( + final FakeGroupedDoctorWithCrash fakeDoctor = + FakeGroupedDoctorWithCrash( logger, clock: fakeSystemClock, ); await fakeDoctor.diagnose(verbose: false); expect(fakeDoctor.validators, hasLength(1)); - expect(fakeDoctor.validators.first.runtimeType == FakeGroupedValidatorWithCrash, true); + expect( + fakeDoctor.validators.first.runtimeType == + FakeGroupedValidatorWithCrash, + true); expect(fakeAnalytics.sentEvents, hasLength(0)); // Attempt to send a random event to ensure that the @@ -1088,14 +1138,16 @@ void main() { ); testUsingContext('sending events can be skipped', () async { - await FakePassingDoctor(logger).diagnose(verbose: false, sendEvent: false); + await FakePassingDoctor(logger) + .diagnose(verbose: false, sendEvent: false); expect(fakeAnalytics.sentEvents, isEmpty); }, overrides: {Analytics: () => fakeAnalytics}); }); } class FakeAndroidWorkflow extends Fake implements AndroidWorkflow { - FakeAndroidWorkflow({this.canListDevices = true, this.appliesToHostPlatform = true}); + FakeAndroidWorkflow( + {this.canListDevices = true, this.appliesToHostPlatform = true}); @override final bool canListDevices; @@ -1108,12 +1160,13 @@ class PassingValidator extends DoctorValidator { PassingValidator(super.title); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage('A helpful message'), ValidationMessage('A second, somewhat longer helpful message'), ]; - return const ValidationResult(ValidationType.success, messages, statusInfo: 'with statusInfo'); + return ZeroExecutionTimeValidationResult(ValidationType.success, messages, + statusInfo: 'with statusInfo'); } } @@ -1121,14 +1174,14 @@ class PiiValidator extends DoctorValidator { PiiValidator() : super('PII Validator'); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage( 'Contains PII path/to/username', piiStrippedMessage: 'Does not contain PII', ), ]; - return const ValidationResult(ValidationType.success, messages); + return ZeroExecutionTimeValidationResult(ValidationType.success, messages); } } @@ -1136,13 +1189,13 @@ class MissingValidator extends DoctorValidator { MissingValidator() : super('Missing Validator'); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage.error('A useful error message'), ValidationMessage('A message that is not an error'), ValidationMessage.hint('A hint message'), ]; - return const ValidationResult(ValidationType.missing, messages); + return ZeroExecutionTimeValidationResult(ValidationType.missing, messages); } } @@ -1150,13 +1203,14 @@ class NotAvailableValidator extends DoctorValidator { NotAvailableValidator() : super('Not Available Validator'); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage.error('A useful error message'), ValidationMessage('A message that is not an error'), ValidationMessage.hint('A hint message'), ]; - return const ValidationResult(ValidationType.notAvailable, messages); + return ZeroExecutionTimeValidationResult( + ValidationType.notAvailable, messages); } } @@ -1164,7 +1218,7 @@ class StuckValidator extends DoctorValidator { StuckValidator() : super('Stuck validator that never completes'); @override - Future validate() { + Future validateImpl() { final Completer completer = Completer(); // This future will never complete @@ -1176,13 +1230,14 @@ class PartialValidatorWithErrors extends DoctorValidator { PartialValidatorWithErrors() : super('Partial Validator with Errors'); @override - Future validate() async { + Future validateImpl() async { const List messages = [ - ValidationMessage.error('An error message indicating partial installation'), + ValidationMessage.error( + 'An error message indicating partial installation'), ValidationMessage.hint('Maybe a hint will help the user'), ValidationMessage('An extra message with some verbose details'), ]; - return const ValidationResult(ValidationType.partial, messages); + return ZeroExecutionTimeValidationResult(ValidationType.partial, messages); } } @@ -1190,12 +1245,12 @@ class PartialValidatorWithHintsOnly extends DoctorValidator { PartialValidatorWithHintsOnly() : super('Partial Validator with only a Hint'); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage.hint('There is a hint here'), ValidationMessage('But there is no error'), ]; - return const ValidationResult(ValidationType.partial, messages); + return ZeroExecutionTimeValidationResult(ValidationType.partial, messages); } } @@ -1203,7 +1258,7 @@ class CrashingValidator extends DoctorValidator { CrashingValidator() : super('Crashing validator'); @override - Future validate() async { + Future validateImpl() async { throw StateError('fatal error'); } } @@ -1214,7 +1269,7 @@ class AsyncCrashingValidator extends DoctorValidator { final FakeAsync _time; @override - Future validate() { + Future validateImpl() { const Duration delay = Duration(seconds: 1); final Future result = Future.delayed( delay, @@ -1228,7 +1283,8 @@ class AsyncCrashingValidator extends DoctorValidator { /// A doctor that fails with a missing [ValidationResult]. class FakeDoctor extends Doctor { - FakeDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakeDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1242,7 +1298,8 @@ class FakeDoctor extends Doctor { /// A doctor that should pass, but still has issues in some categories. class FakePassingDoctor extends Doctor { - FakePassingDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakePassingDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1257,15 +1314,18 @@ class FakePassingDoctor extends Doctor { /// categories. class FakeSinglePassingDoctor extends Doctor { FakeSinglePassingDoctor(Logger logger, {super.clock = const SystemClock()}) - : super(logger: logger); + : super(logger: logger); @override - late final List validators = [PartialValidatorWithHintsOnly()]; + late final List validators = [ + PartialValidatorWithHintsOnly() + ]; } /// A doctor that passes and has no issues anywhere. class FakeQuietDoctor extends Doctor { - FakeQuietDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakeQuietDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1278,15 +1338,19 @@ class FakeQuietDoctor extends Doctor { /// A doctor that passes and contains PII that can be hidden. class FakePiiDoctor extends Doctor { - FakePiiDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakePiiDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override - late final List validators = [PiiValidator()]; + late final List validators = [ + PiiValidator() + ]; } /// A doctor with a validator that throws an exception. class FakeCrashingDoctor extends Doctor { - FakeCrashingDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakeCrashingDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1300,7 +1364,8 @@ class FakeCrashingDoctor extends Doctor { /// A doctor with a validator that will never finish. class FakeAsyncStuckDoctor extends Doctor { - FakeAsyncStuckDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakeAsyncStuckDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1314,8 +1379,9 @@ class FakeAsyncStuckDoctor extends Doctor { /// A doctor with a validator that throws an exception. class FakeAsyncCrashingDoctor extends Doctor { - FakeAsyncCrashingDoctor(this._time, Logger logger, {super.clock = const SystemClock()}) - : super(logger: logger); + FakeAsyncCrashingDoctor(this._time, Logger logger, + {super.clock = const SystemClock()}) + : super(logger: logger); final FakeAsync _time; @@ -1334,10 +1400,10 @@ class FakeAsyncCrashingDoctor extends Doctor { class FakeDoctorValidatorsProvider implements DoctorValidatorsProvider { @override List get validators => [ - PassingValidator('Passing Validator'), - PassingValidator('Another Passing Validator'), - PassingValidator('Providing validators is fun'), - ]; + PassingValidator('Passing Validator'), + PassingValidator('Another Passing Validator'), + PassingValidator('Providing validators is fun'), + ]; @override List get workflows => []; @@ -1347,11 +1413,11 @@ class PassingGroupedValidator extends DoctorValidator { PassingGroupedValidator(super.title); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage('A helpful message'), ]; - return const ValidationResult(ValidationType.success, messages); + return ZeroExecutionTimeValidationResult(ValidationType.success, messages); } } @@ -1359,11 +1425,11 @@ class MissingGroupedValidator extends DoctorValidator { MissingGroupedValidator(super.title); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage.error('A useful error message'), ]; - return const ValidationResult(ValidationType.missing, messages); + return ZeroExecutionTimeValidationResult(ValidationType.missing, messages); } } @@ -1371,11 +1437,11 @@ class PartialGroupedValidator extends DoctorValidator { PartialGroupedValidator(super.title); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage.error('An error message for partial installation'), ]; - return const ValidationResult(ValidationType.partial, messages); + return ZeroExecutionTimeValidationResult(ValidationType.partial, messages); } } @@ -1383,17 +1449,19 @@ class PassingGroupedValidatorWithStatus extends DoctorValidator { PassingGroupedValidatorWithStatus(super.title); @override - Future validate() async { + Future validateImpl() async { const List messages = [ ValidationMessage('A different message'), ]; - return const ValidationResult(ValidationType.success, messages, statusInfo: 'A status message'); + return ZeroExecutionTimeValidationResult(ValidationType.success, messages, + statusInfo: 'A status message'); } } /// A doctor that has two groups of two validators each. class FakeGroupedDoctor extends Doctor { - FakeGroupedDoctor(Logger logger, {super.clock = const SystemClock()}) : super(logger: logger); + FakeGroupedDoctor(Logger logger, {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1411,7 +1479,7 @@ class FakeGroupedDoctor extends Doctor { /// Fake grouped doctor that is intended to be used with [FakeGroupedValidatorWithCrash]. class FakeGroupedDoctorWithCrash extends Doctor { FakeGroupedDoctorWithCrash(Logger logger, {super.clock = const SystemClock()}) - : super(logger: logger); + : super(logger: logger); @override late final List validators = [ @@ -1438,8 +1506,9 @@ class FakeGroupedValidatorWithCrash extends GroupedValidator { } class FakeGroupedDoctorWithStatus extends Doctor { - FakeGroupedDoctorWithStatus(Logger logger, {super.clock = const SystemClock()}) - : super(logger: logger); + FakeGroupedDoctorWithStatus(Logger logger, + {super.clock = const SystemClock()}) + : super(logger: logger); @override late final List validators = [ @@ -1458,10 +1527,10 @@ class FakeSmallGroupDoctor extends Doctor { DoctorValidator val1, DoctorValidator val2, { super.clock = const SystemClock(), - }) : validators = [ - GroupedValidator([val1, val2]), - ], - super(logger: logger); + }) : validators = [ + GroupedValidator([val1, val2]), + ], + super(logger: logger); @override final List validators; @@ -1473,24 +1542,26 @@ class VsCodeValidatorTestTargets extends VsCodeValidator { String extensionDirectory, { String? edition, }) : super( - VsCode.fromDirectory( - installDirectory, - extensionDirectory, - edition: edition, - fileSystem: globals.fs, - ), - ); + VsCode.fromDirectory( + installDirectory, + extensionDirectory, + edition: edition, + fileSystem: globals.fs, + ), + ); static VsCodeValidatorTestTargets get installedWithExtension => VsCodeValidatorTestTargets._(validInstall, validExtensions); static VsCodeValidatorTestTargets get installedWithExtension64bit => - VsCodeValidatorTestTargets._(validInstall, validExtensions, edition: '64-bit edition'); + VsCodeValidatorTestTargets._(validInstall, validExtensions, + edition: '64-bit edition'); static VsCodeValidatorTestTargets get installedWithoutExtension => VsCodeValidatorTestTargets._(validInstall, missingExtensions); - static final String validInstall = globals.fs.path.join('test', 'data', 'vscode', 'application'); + static final String validInstall = + globals.fs.path.join('test', 'data', 'vscode', 'application'); static final String validExtensions = globals.fs.path.join( 'test', 'data', @@ -1510,13 +1581,15 @@ class FakeDeviceManager extends Fake implements DeviceManager { List devices = []; @override - Future> getAllDevices({DeviceDiscoveryFilter? filter}) async => devices; + Future> getAllDevices({DeviceDiscoveryFilter? filter}) async => + devices; @override Future> refreshAllDevices({ Duration? timeout, DeviceDiscoveryFilter? filter, - }) async => devices; + }) async => + devices; @override Future> getDeviceDiagnostics() async => diagnostics; @@ -1548,7 +1621,8 @@ class FakeDevice extends Fake implements Device { Future get sdkNameAndVersion async => '1.2.3'; @override - Future get targetPlatform => Future.value(TargetPlatform.android); + Future get targetPlatform => + Future.value(TargetPlatform.android); } class FakeTerminal extends Fake implements AnsiTerminal { @@ -1558,3 +1632,11 @@ class FakeTerminal extends Fake implements AnsiTerminal { @override bool get isCliAnimationEnabled => supportsColor; } + +class ZeroExecutionTimeValidationResult extends ValidationResult { + ZeroExecutionTimeValidationResult(super.type, super.messages, + {super.statusInfo}); + + @override + Duration? get executionTime => Duration.zero; +} diff --git a/packages/flutter_tools/test/general.shard/reporting/events_test.dart b/packages/flutter_tools/test/general.shard/reporting/events_test.dart index 77eb002c0a..b5fd733f53 100644 --- a/packages/flutter_tools/test/general.shard/reporting/events_test.dart +++ b/packages/flutter_tools/test/general.shard/reporting/events_test.dart @@ -162,7 +162,7 @@ class FakeDoctorValidator extends DoctorValidator { FakeDoctorValidator(super.title); @override - Future validate() async { + Future validateImpl() async { return ValidationResult.crash(Object()); } } diff --git a/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart b/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart index 745a9e3773..acf6537dc8 100644 --- a/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart +++ b/packages/flutter_tools/test/general.shard/windows_version_validator_test.dart @@ -14,15 +14,20 @@ import '../src/context.dart'; import '../src/fake_process_manager.dart'; /// Fake [_WindowsUtils] to use for testing -class FakeValidOperatingSystemUtils extends Fake implements OperatingSystemUtils { - FakeValidOperatingSystemUtils([this.name = 'Microsoft Windows [Version 11.0.22621.963]']); +class FakeValidOperatingSystemUtils extends Fake + implements OperatingSystemUtils { + FakeValidOperatingSystemUtils( + [this.name = 'Microsoft Windows [Version 11.0.22621.963]']); @override final String name; } class FakeProcessLister extends Fake implements ProcessLister { - FakeProcessLister({required this.result, this.exitCode = 0, this.powershellAvailable = true}); + FakeProcessLister( + {required this.result, + this.exitCode = 0, + this.powershellAvailable = true}); final String result; final int exitCode; final bool powershellAvailable; @@ -37,11 +42,13 @@ class FakeProcessLister extends Fake implements ProcessLister { } FakeProcessLister ofdRunning() { - return FakeProcessLister(result: r'Path: "C:\Program Files\Topaz OFD\Warsaw\core.exe"'); + return FakeProcessLister( + result: r'Path: "C:\Program Files\Topaz OFD\Warsaw\core.exe"'); } FakeProcessLister ofdNotRunning() { - return FakeProcessLister(result: r'Path: "C:\Program Files\Google\Chrome\Application\chrome.exe'); + return FakeProcessLister( + result: r'Path: "C:\Program Files\Google\Chrome\Application\chrome.exe'); } FakeProcessLister failure() { @@ -57,51 +64,58 @@ FakeProcessLister powershellUnavailable() { /// The expected validation result object for /// a passing windows version test -const ValidationResult validWindows11ValidationResult = ValidationResult( +ValidationResult validWindows11ValidationResult = ValidationResult( ValidationType.success, - [], + const [], statusInfo: '11 Pro 64-bit, 23H2, 2009', ); /// The expected validation result object for /// a passing windows version test -const ValidationResult invalidWindowsValidationResult = ValidationResult( +ValidationResult invalidWindowsValidationResult = ValidationResult( ValidationType.missing, - [], + const [], statusInfo: 'Unable to confirm if installed Windows version is 10 or greater', ); -const ValidationResult ofdFoundRunning = - ValidationResult(ValidationType.partial, [ - ValidationMessage.hint( - 'The Topaz OFD Security Module was detected on your machine. ' - 'You may need to disable it to build Flutter applications.', - ), - ], statusInfo: 'Problem detected with Windows installation'); - -const ValidationResult powershellUnavailableResult = - ValidationResult(ValidationType.partial, [ - ValidationMessage.hint( - 'Failed to find ${ProcessLister.powershell} or ${ProcessLister.pwsh} on PATH', - ), - ], statusInfo: 'Problem detected with Windows installation'); - -const ValidationResult getProcessFailed = ValidationResult( +ValidationResult ofdFoundRunning = ValidationResult( ValidationType.partial, - [ValidationMessage.hint('Get-Process failed to complete')], + const [ + ValidationMessage.hint( + 'The Topaz OFD Security Module was detected on your machine. ' + 'You may need to disable it to build Flutter applications.', + ), + ], + statusInfo: 'Problem detected with Windows installation', +); + +ValidationResult powershellUnavailableResult = ValidationResult( + ValidationType.partial, + const [ + ValidationMessage.hint( + 'Failed to find ${ProcessLister.powershell} or ${ProcessLister.pwsh} on PATH'), + ], + statusInfo: 'Problem detected with Windows installation', +); + +ValidationResult getProcessFailed = ValidationResult( + ValidationType.partial, + const [ + ValidationMessage.hint('Get-Process failed to complete'), + ], statusInfo: 'Problem detected with Windows installation', ); class FakeVersionExtractor extends Fake implements WindowsVersionExtractor { FakeVersionExtractor({required this.mockData}); FakeVersionExtractor.win11ProX64() - : this( - mockData: WindowsVersionExtractionResult( - caption: '11 Pro 64-bit', - releaseId: '2009', - displayVersion: '23H2', - ), - ); + : this( + mockData: WindowsVersionExtractionResult( + caption: '11 Pro 64-bit', + releaseId: '2009', + displayVersion: '23H2', + ), + ); final WindowsVersionExtractionResult mockData; @override @@ -111,8 +125,10 @@ class FakeVersionExtractor extends Fake implements WindowsVersionExtractor { } void main() { - testWithoutContext('Successfully running windows version check on windows 10', () async { - final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( + testWithoutContext('Successfully running windows version check on windows 10', + () async { + final WindowsVersionValidator windowsVersionValidator = + WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), processLister: ofdNotRunning(), versionExtractor: FakeVersionExtractor.win11ProX64(), @@ -132,8 +148,11 @@ void main() { ); }); - testWithoutContext('Successfully running windows version check on windows 10 for BR', () async { - final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( + testWithoutContext( + 'Successfully running windows version check on windows 10 for BR', + () async { + final WindowsVersionValidator windowsVersionValidator = + WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils( 'Microsoft Windows [versão 10.0.22621.1105]', ), @@ -156,7 +175,8 @@ void main() { }); testWithoutContext('Identifying a windows version before 10', () async { - final WindowsVersionValidator windowsVersionValidator = WindowsVersionValidator( + final WindowsVersionValidator windowsVersionValidator = + WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils( 'Microsoft Windows [Version 8.0.22621.1105]', ), @@ -185,13 +205,16 @@ OS Version: .0.19044 N/A Build 19044 OS 版本: 10.0.22621 暂缺 Build 22621 '''; - final RegExp regex = RegExp(kWindowsOSVersionSemVerPattern, multiLine: true); + final RegExp regex = + RegExp(kWindowsOSVersionSemVerPattern, multiLine: true); final Iterable matches = regex.allMatches(testStr); - expect(matches.length, 5, reason: 'There should be only 5 matches for the pattern provided'); + expect(matches.length, 5, + reason: 'There should be only 5 matches for the pattern provided'); }); - testWithoutContext('Successfully checks for Topaz OFD when it is running', () async { + testWithoutContext('Successfully checks for Topaz OFD when it is running', + () async { final WindowsVersionValidator validator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), processLister: ofdRunning(), @@ -253,7 +276,8 @@ OS 版本: 10.0.22621 暂缺 Build 22621 final WindowsVersionValidator validator = WindowsVersionValidator( operatingSystemUtils: FakeValidOperatingSystemUtils(), processLister: failure(), - versionExtractor: FakeVersionExtractor(mockData: WindowsVersionExtractionResult.empty()), + versionExtractor: FakeVersionExtractor( + mockData: WindowsVersionExtractionResult.empty()), ); final ValidationResult result = await validator.validate(); expect( @@ -278,14 +302,20 @@ OS 版本: 10.0.22621 暂缺 Build 22621 ); }); - testWithoutContext('getProcessesWithPath successfully runs with powershell', () async { + testWithoutContext('getProcessesWithPath successfully runs with powershell', + () async { final ProcessLister processLister = ProcessLister( FakeProcessManager.list([ const FakeCommand( - command: [ProcessLister.powershell, '-command', 'Get-Process | Format-List Path'], + command: [ + ProcessLister.powershell, + '-command', + 'Get-Process | Format-List Path' + ], stdout: ProcessLister.powershell, ), - ])..excludedExecutables.add(ProcessLister.pwsh), + ]) + ..excludedExecutables.add(ProcessLister.pwsh), ); try { @@ -303,10 +333,15 @@ OS 版本: 10.0.22621 暂缺 Build 22621 final ProcessLister processLister = ProcessLister( FakeProcessManager.list([ const FakeCommand( - command: [ProcessLister.pwsh, '-command', 'Get-Process | Format-List Path'], + command: [ + ProcessLister.pwsh, + '-command', + 'Get-Process | Format-List Path' + ], stdout: ProcessLister.pwsh, ), - ])..excludedExecutables.add(ProcessLister.powershell), + ]) + ..excludedExecutables.add(ProcessLister.powershell), ); try { @@ -324,7 +359,8 @@ OS 版本: 10.0.22621 暂缺 Build 22621 () async { final ProcessLister processLister = ProcessLister( FakeProcessManager.empty() - ..excludedExecutables.addAll([ProcessLister.powershell, ProcessLister.pwsh]), + ..excludedExecutables + .addAll([ProcessLister.powershell, ProcessLister.pwsh]), ); try { @@ -342,7 +378,8 @@ OS 版本: 10.0.22621 暂缺 Build 22621 testWithoutContext( 'Parses Caption, OSArchitecture, releaseId, and CurrentVersion from the OS', () async { - final FakeProcessManager processManager = FakeProcessManager.list([ + final FakeProcessManager processManager = + FakeProcessManager.list([ const FakeCommand( command: ['wmic', 'os', 'get', 'Caption,OSArchitecture'], stdout: ''' @@ -402,7 +439,8 @@ End of search: 22 match(es) found. }, ); - testWithoutContext('Differentiates Windows 11 from 10 when wmic call fails', () async { + testWithoutContext('Differentiates Windows 11 from 10 when wmic call fails', + () async { const String windows10RegQueryOutput = r''' HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion SystemRoot REG_SZ C:\WINDOWS @@ -430,8 +468,14 @@ HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion End of search: 21 match(es) found. '''; - const List wmicCommand = ['wmic', 'os', 'get', 'Caption,OSArchitecture']; - final FakeProcessManager processManager = FakeProcessManager.list([ + const List wmicCommand = [ + 'wmic', + 'os', + 'get', + 'Caption,OSArchitecture' + ]; + final FakeProcessManager processManager = + FakeProcessManager.list([ FakeCommand( command: wmicCommand, exception: ProcessException(wmicCommand[0], wmicCommand.sublist(1)), @@ -462,7 +506,12 @@ End of search: 21 match(es) found. }); testWithoutContext('Handles reg call failing', () async { - const List wmicCommand = ['wmic', 'os', 'get', 'Caption,OSArchitecture']; + const List wmicCommand = [ + 'wmic', + 'os', + 'get', + 'Caption,OSArchitecture' + ]; const List regCommand = [ 'reg', 'query', @@ -470,7 +519,8 @@ End of search: 21 match(es) found. '/t', 'REG_SZ', ]; - final FakeProcessManager processManager = FakeProcessManager.list([ + final FakeProcessManager processManager = + FakeProcessManager.list([ const FakeCommand( command: wmicCommand, stdout: r'''