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