mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
1219 lines
44 KiB
Dart
1219 lines
44 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:fake_async/fake_async.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/android/android_studio_validator.dart';
|
|
import 'package:flutter_tools/src/android/android_workflow.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/base/terminal.dart';
|
|
import 'package:flutter_tools/src/base/user_messages.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/cache.dart';
|
|
import 'package:flutter_tools/src/commands/doctor.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/doctor.dart';
|
|
import 'package:flutter_tools/src/doctor_validator.dart';
|
|
import 'package:flutter_tools/src/globals.dart' as globals;
|
|
import 'package:flutter_tools/src/reporting/reporting.dart';
|
|
import 'package:flutter_tools/src/version.dart';
|
|
import 'package:flutter_tools/src/vscode/vscode.dart';
|
|
import 'package:flutter_tools/src/vscode/vscode_validator.dart';
|
|
import 'package:flutter_tools/src/web/workflow.dart';
|
|
import 'package:test/fake.dart';
|
|
|
|
import '../../src/common.dart';
|
|
import '../../src/context.dart';
|
|
import '../../src/fakes.dart';
|
|
import '../../src/test_flutter_command_runner.dart';
|
|
|
|
void main() {
|
|
late FakeFlutterVersion flutterVersion;
|
|
late BufferLogger logger;
|
|
late FakeProcessManager fakeProcessManager;
|
|
|
|
setUp(() {
|
|
flutterVersion = FakeFlutterVersion();
|
|
logger = BufferLogger.test();
|
|
fakeProcessManager = FakeProcessManager.empty();
|
|
});
|
|
|
|
testWithoutContext('ValidationMessage equality and hashCode includes contextUrl', () {
|
|
const ValidationMessage messageA = ValidationMessage('ab', contextUrl: 'a');
|
|
const ValidationMessage messageB = ValidationMessage('ab', contextUrl: 'b');
|
|
|
|
expect(messageB, isNot(messageA));
|
|
expect(messageB.hashCode, isNot(messageA.hashCode));
|
|
expect(messageA, isNot(messageB));
|
|
expect(messageA.hashCode, isNot(messageB.hashCode));
|
|
});
|
|
|
|
group('doctor', () {
|
|
testUsingContext('vs code validator when both installed', () async {
|
|
final ValidationResult result = await VsCodeValidatorTestTargets.installedWithExtension.validate();
|
|
expect(result.type, ValidationType.installed);
|
|
expect(result.statusInfo, 'version 1.2.3');
|
|
expect(result.messages, hasLength(2));
|
|
|
|
ValidationMessage message = result.messages
|
|
.firstWhere((ValidationMessage m) => m.message.startsWith('VS Code '));
|
|
expect(message.message, 'VS Code at ${VsCodeValidatorTestTargets.validInstall}');
|
|
|
|
message = result.messages
|
|
.firstWhere((ValidationMessage m) => m.message.startsWith('Flutter '));
|
|
expect(message.message, 'Flutter extension version 4.5.6');
|
|
expect(message.isError, isFalse);
|
|
});
|
|
|
|
testUsingContext('No IDE Validator includes expected installation messages', () async {
|
|
final ValidationResult result = await NoIdeValidator().validate();
|
|
expect(result.type, ValidationType.notAvailable);
|
|
|
|
expect(
|
|
result.messages.map((ValidationMessage vm) => vm.message),
|
|
UserMessages().noIdeInstallationInfo,
|
|
);
|
|
});
|
|
|
|
testUsingContext('vs code validator when 64bit installed', () async {
|
|
expect(VsCodeValidatorTestTargets.installedWithExtension64bit.title, 'VS Code, 64-bit edition');
|
|
final ValidationResult result = await VsCodeValidatorTestTargets.installedWithExtension64bit.validate();
|
|
expect(result.type, ValidationType.installed);
|
|
expect(result.statusInfo, 'version 1.2.3');
|
|
expect(result.messages, hasLength(2));
|
|
|
|
ValidationMessage message = result.messages
|
|
.firstWhere((ValidationMessage m) => m.message.startsWith('VS Code '));
|
|
expect(message.message, 'VS Code at ${VsCodeValidatorTestTargets.validInstall}');
|
|
|
|
message = result.messages
|
|
.firstWhere((ValidationMessage m) => m.message.startsWith('Flutter '));
|
|
expect(message.message, 'Flutter extension version 4.5.6');
|
|
});
|
|
|
|
testUsingContext('vs code validator when extension missing', () async {
|
|
final ValidationResult result = await VsCodeValidatorTestTargets.installedWithoutExtension.validate();
|
|
expect(result.type, ValidationType.installed);
|
|
expect(result.statusInfo, 'version 1.2.3');
|
|
expect(result.messages, hasLength(2));
|
|
|
|
ValidationMessage message = result.messages
|
|
.firstWhere((ValidationMessage m) => m.message.startsWith('VS Code '));
|
|
expect(message.message, 'VS Code at ${VsCodeValidatorTestTargets.validInstall}');
|
|
|
|
message = result.messages
|
|
.firstWhere((ValidationMessage m) => m.message.startsWith('Flutter '));
|
|
expect(message.message, startsWith('Flutter extension can be installed from'));
|
|
expect(message.contextUrl, 'https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter');
|
|
expect(message.isError, false);
|
|
});
|
|
|
|
group('device validator', () {
|
|
testWithoutContext('no devices', () async {
|
|
final FakeDeviceManager deviceManager = FakeDeviceManager();
|
|
final DeviceValidator deviceValidator = DeviceValidator(
|
|
deviceManager: deviceManager,
|
|
userMessages: UserMessages(),
|
|
);
|
|
final ValidationResult result = await deviceValidator.validate();
|
|
expect(result.type, ValidationType.notAvailable);
|
|
expect(result.messages, const <ValidationMessage>[
|
|
ValidationMessage.hint('No devices available'),
|
|
]);
|
|
expect(result.statusInfo, isNull);
|
|
});
|
|
|
|
testWithoutContext('diagnostic message', () async {
|
|
final FakeDeviceManager deviceManager = FakeDeviceManager()
|
|
..diagnostics = <String>['Device locked'];
|
|
|
|
final DeviceValidator deviceValidator = DeviceValidator(
|
|
deviceManager: deviceManager,
|
|
userMessages: UserMessages(),
|
|
);
|
|
final ValidationResult result = await deviceValidator.validate();
|
|
expect(result.type, ValidationType.notAvailable);
|
|
expect(result.messages, const <ValidationMessage>[
|
|
ValidationMessage.hint('Device locked'),
|
|
]);
|
|
expect(result.statusInfo, isNull);
|
|
});
|
|
|
|
testWithoutContext('diagnostic message and devices', () async {
|
|
final FakeDevice device = FakeDevice();
|
|
final FakeDeviceManager deviceManager = FakeDeviceManager()
|
|
..devices = <Device>[device]
|
|
..diagnostics = <String>['Device locked'];
|
|
|
|
final DeviceValidator deviceValidator = DeviceValidator(
|
|
deviceManager: deviceManager,
|
|
userMessages: UserMessages(),
|
|
);
|
|
final ValidationResult result = await deviceValidator.validate();
|
|
expect(result.type, ValidationType.installed);
|
|
expect(result.messages, const <ValidationMessage>[
|
|
ValidationMessage('name (mobile) • device-id • android • 1.2.3'),
|
|
ValidationMessage.hint('Device locked'),
|
|
]);
|
|
expect(result.statusInfo, '1 available');
|
|
});
|
|
});
|
|
});
|
|
|
|
group('doctor with overridden validators', () {
|
|
testUsingContext('validate non-verbose output format for run without issues', () async {
|
|
final Doctor doctor = Doctor(logger: logger);
|
|
expect(await doctor.diagnose(verbose: false), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
'[✓] Another Passing Validator (with statusInfo)\n'
|
|
'[✓] Providing validators is fun (with statusInfo)\n'
|
|
'\n'
|
|
'• No issues found!\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
DoctorValidatorsProvider: () => FakeDoctorValidatorsProvider(),
|
|
});
|
|
});
|
|
|
|
group('doctor usage params', () {
|
|
late TestUsage testUsage;
|
|
|
|
setUp(() {
|
|
testUsage = TestUsage();
|
|
});
|
|
|
|
testUsingContext('contains installed', () async {
|
|
final Doctor doctor = Doctor(logger: logger);
|
|
await doctor.diagnose(verbose: false);
|
|
|
|
expect(testUsage.events.length, 3);
|
|
expect(testUsage.events, contains(
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingValidator',
|
|
label: 'installed',
|
|
),
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
DoctorValidatorsProvider: () => FakeDoctorValidatorsProvider(),
|
|
Usage: () => testUsage,
|
|
});
|
|
|
|
testUsingContext('contains installed and partial', () async {
|
|
await FakePassingDoctor(logger).diagnose(verbose: false);
|
|
|
|
expect(testUsage.events, unorderedEquals(<TestUsageEvent>[
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingValidator',
|
|
label: 'installed',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingValidator',
|
|
label: 'installed',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PartialValidatorWithHintsOnly',
|
|
label: 'partial',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PartialValidatorWithErrors',
|
|
label: 'partial',
|
|
),
|
|
]));
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
});
|
|
|
|
testUsingContext('contains installed, missing and partial', () async {
|
|
await FakeDoctor(logger).diagnose(verbose: false);
|
|
|
|
expect(testUsage.events, unorderedEquals(<TestUsageEvent>[
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingValidator',
|
|
label: 'installed',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'MissingValidator',
|
|
label: 'missing',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'NotAvailableValidator',
|
|
label: 'notAvailable',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PartialValidatorWithHintsOnly',
|
|
label: 'partial',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PartialValidatorWithErrors',
|
|
label: 'partial',
|
|
),
|
|
]));
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
});
|
|
|
|
testUsingContext('events for grouped validators are properly decomposed', () async {
|
|
await FakeGroupedDoctor(logger).diagnose(verbose: false);
|
|
|
|
expect(testUsage.events, unorderedEquals(<TestUsageEvent>[
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingGroupedValidator',
|
|
label: 'installed',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingGroupedValidator',
|
|
label: 'installed',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PassingGroupedValidator',
|
|
label: 'installed',
|
|
),
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'MissingGroupedValidator',
|
|
label: 'missing',
|
|
),
|
|
]));
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
});
|
|
|
|
testUsingContext('sending events can be skipped', () async {
|
|
await FakePassingDoctor(logger).diagnose(verbose: false, sendEvent: false);
|
|
|
|
expect(testUsage.events, isEmpty);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
});
|
|
});
|
|
|
|
group('doctor with fake validators', () {
|
|
testUsingContext('validate non-verbose output format for run without issues', () async {
|
|
expect(await FakeQuietDoctor(logger).diagnose(verbose: false), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
'[✓] Another Passing Validator (with statusInfo)\n'
|
|
'[✓] Validators are fun (with statusInfo)\n'
|
|
'[✓] Four score and seven validators ago (with statusInfo)\n'
|
|
'\n'
|
|
'• No issues found!\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate non-verbose output format for run with crash', () async {
|
|
expect(await FakeCrashingDoctor(logger).diagnose(verbose: false), isFalse);
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
'[✓] Another Passing Validator (with statusInfo)\n'
|
|
'[☠] Crashing validator (the doctor check crashed)\n'
|
|
' ✗ Due to an error, the doctor check did not complete. If the error message below is not helpful, '
|
|
'please let us know about this issue at https://github.com/flutter/flutter/issues.\n'
|
|
' ✗ Bad state: fatal error\n'
|
|
'[✓] Validators are fun (with statusInfo)\n'
|
|
'[✓] Four score and seven validators ago (with statusInfo)\n'
|
|
'\n'
|
|
'! Doctor found issues in 1 category.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate verbose output format contains trace for run with crash', () async {
|
|
expect(await FakeCrashingDoctor(logger).diagnose(), isFalse);
|
|
expect(logger.statusText, contains('#0 CrashingValidator.validate'));
|
|
});
|
|
|
|
testUsingContext('validate tool exit when exceeding timeout', () async {
|
|
FakeAsync().run<void>((FakeAsync time) {
|
|
final Doctor doctor = FakeAsyncStuckDoctor(logger);
|
|
doctor.diagnose(verbose: false);
|
|
time.elapse(Doctor.doctorDuration + const Duration(seconds: 1));
|
|
time.flushMicrotasks();
|
|
});
|
|
|
|
expect(logger.statusText, contains('Stuck validator that never completes exceeded maximum allowed duration of '));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate non-verbose output format for run with an async crash', () async {
|
|
final Completer<void> completer = Completer<void>();
|
|
await FakeAsync().run((FakeAsync time) {
|
|
unawaited(FakeAsyncCrashingDoctor(time, logger).diagnose(verbose: false).then((bool r) {
|
|
expect(r, isFalse);
|
|
completer.complete(null);
|
|
}));
|
|
time.elapse(const Duration(seconds: 1));
|
|
time.flushMicrotasks();
|
|
return completer.future;
|
|
});
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
'[✓] Another Passing Validator (with statusInfo)\n'
|
|
'[☠] Async crashing validator (the doctor check crashed)\n'
|
|
' ✗ Due to an error, the doctor check did not complete. If the error message below is not helpful, '
|
|
'please let us know about this issue at https://github.com/flutter/flutter/issues.\n'
|
|
' ✗ Bad state: fatal error\n'
|
|
'[✓] Validators are fun (with statusInfo)\n'
|
|
'[✓] Four score and seven validators ago (with statusInfo)\n'
|
|
'\n'
|
|
'! Doctor found issues in 1 category.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
|
|
testUsingContext('validate non-verbose output format when only one category fails', () async {
|
|
expect(await FakeSinglePassingDoctor(logger).diagnose(verbose: false), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[!] Partial Validator with only a Hint\n'
|
|
' ! There is a hint here\n'
|
|
'\n'
|
|
'! Doctor found issues in 1 category.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate non-verbose output format for a passing run', () async {
|
|
expect(await FakePassingDoctor(logger).diagnose(verbose: false), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
'[!] Partial Validator with only a Hint\n'
|
|
' ! There is a hint here\n'
|
|
'[!] Partial Validator with Errors\n'
|
|
' ✗ An error message indicating partial installation\n'
|
|
' ! Maybe a hint will help the user\n'
|
|
'[✓] Another Passing Validator (with statusInfo)\n'
|
|
'\n'
|
|
'! Doctor found issues in 2 categories.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate non-verbose output format', () async {
|
|
expect(await FakeDoctor(logger).diagnose(verbose: false), isFalse);
|
|
expect(logger.statusText, equals(
|
|
'Doctor summary (to see all details, run flutter doctor -v):\n'
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
'[✗] Missing Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' ! A hint message\n'
|
|
'[!] Not Available Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' ! A hint message\n'
|
|
'[!] Partial Validator with only a Hint\n'
|
|
' ! There is a hint here\n'
|
|
'[!] Partial Validator with Errors\n'
|
|
' ✗ An error message indicating partial installation\n'
|
|
' ! Maybe a hint will help the user\n'
|
|
'\n'
|
|
'! Doctor found issues in 4 categories.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate verbose output format', () async {
|
|
expect(await FakeDoctor(logger).diagnose(), isFalse);
|
|
expect(logger.statusText, equals(
|
|
'[✓] Passing Validator (with statusInfo)\n'
|
|
' • A helpful message\n'
|
|
' • A second, somewhat longer helpful message\n'
|
|
'\n'
|
|
'[✗] Missing Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' • A message that is not an error\n'
|
|
' ! A hint message\n'
|
|
'\n'
|
|
'[!] Not Available Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' • A message that is not an error\n'
|
|
' ! A hint message\n'
|
|
'\n'
|
|
'[!] Partial Validator with only a Hint\n'
|
|
' ! There is a hint here\n'
|
|
' • But there is no error\n'
|
|
'\n'
|
|
'[!] Partial Validator with Errors\n'
|
|
' ✗ An error message indicating partial installation\n'
|
|
' ! Maybe a hint will help the user\n'
|
|
' • An extra message with some verbose details\n'
|
|
'\n'
|
|
'! Doctor found issues in 4 categories.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate PII can be hidden', () async {
|
|
expect(await FakePiiDoctor(logger).diagnose(showPii: false), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'[✓] PII Validator\n'
|
|
' • Does not contain PII\n'
|
|
'\n'
|
|
'• No issues found!\n'
|
|
));
|
|
logger.clear();
|
|
// PII shown.
|
|
expect(await FakePiiDoctor(logger).diagnose(), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'[✓] PII Validator\n'
|
|
' • Contains PII path/to/username\n'
|
|
'\n'
|
|
'• No issues found!\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
});
|
|
|
|
group('doctor diagnosis wrapper', () {
|
|
late TestUsage testUsage;
|
|
late BufferLogger logger;
|
|
|
|
setUp(() {
|
|
testUsage = TestUsage();
|
|
logger = BufferLogger.test();
|
|
});
|
|
|
|
testUsingContext('PII separated, events only sent once', () async {
|
|
final Doctor fakeDoctor = FakePiiDoctor(logger);
|
|
final DoctorText doctorText = DoctorText(logger, doctor: fakeDoctor);
|
|
const String expectedPiiText = '[✓] PII Validator\n'
|
|
' • Contains PII path/to/username\n'
|
|
'\n'
|
|
'• No issues found!\n';
|
|
const String expectedPiiStrippedText =
|
|
'[✓] PII Validator\n'
|
|
' • Does not contain PII\n'
|
|
'\n'
|
|
'• No issues found!\n';
|
|
|
|
// Run each multiple times to make sure the logger buffer is being cleared,
|
|
// and that events are only sent once.
|
|
expect(await doctorText.text, expectedPiiText);
|
|
expect(await doctorText.text, expectedPiiText);
|
|
|
|
expect(await doctorText.piiStrippedText, expectedPiiStrippedText);
|
|
expect(await doctorText.piiStrippedText, expectedPiiStrippedText);
|
|
|
|
// Only one event sent.
|
|
expect(testUsage.events, <TestUsageEvent>[
|
|
const TestUsageEvent(
|
|
'doctor-result',
|
|
'PiiValidator',
|
|
label: 'installed',
|
|
),
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
Usage: () => testUsage,
|
|
});
|
|
|
|
testUsingContext('without PII has same text and PII-stripped text', () async {
|
|
final Doctor fakeDoctor = FakePassingDoctor(logger);
|
|
final DoctorText doctorText = DoctorText(logger, doctor: fakeDoctor);
|
|
final String piiText = await doctorText.text;
|
|
expect(piiText, isNotEmpty);
|
|
expect(piiText, await doctorText.piiStrippedText);
|
|
}, overrides: <Type, Generator>{
|
|
Usage: () => testUsage,
|
|
});
|
|
});
|
|
|
|
testUsingContext('validate non-verbose output wrapping', () async {
|
|
final BufferLogger wrapLogger = BufferLogger.test(
|
|
outputPreferences: OutputPreferences(wrapText: true, wrapColumn: 30),
|
|
);
|
|
expect(await FakeDoctor(wrapLogger).diagnose(verbose: false), isFalse);
|
|
expect(wrapLogger.statusText, equals(
|
|
'Doctor summary (to see all\n'
|
|
'details, run flutter doctor\n'
|
|
'-v):\n'
|
|
'[✓] Passing Validator (with\n'
|
|
' statusInfo)\n'
|
|
'[✗] Missing Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' ! A hint message\n'
|
|
'[!] Not Available Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' ! A hint message\n'
|
|
'[!] Partial Validator with\n'
|
|
' only a Hint\n'
|
|
' ! There is a hint here\n'
|
|
'[!] Partial Validator with\n'
|
|
' Errors\n'
|
|
' ✗ An error message\n'
|
|
' indicating partial\n'
|
|
' installation\n'
|
|
' ! Maybe a hint will help\n'
|
|
' the user\n'
|
|
'\n'
|
|
'! Doctor found issues in 4\n'
|
|
' categories.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate verbose output wrapping', () async {
|
|
final BufferLogger wrapLogger = BufferLogger.test(
|
|
outputPreferences: OutputPreferences(wrapText: true, wrapColumn: 30),
|
|
);
|
|
expect(await FakeDoctor(wrapLogger).diagnose(), isFalse);
|
|
expect(wrapLogger.statusText, equals(
|
|
'[✓] Passing Validator (with\n'
|
|
' statusInfo)\n'
|
|
' • A helpful message\n'
|
|
' • A second, somewhat\n'
|
|
' longer helpful message\n'
|
|
'\n'
|
|
'[✗] Missing Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' • A message that is not an\n'
|
|
' error\n'
|
|
' ! A hint message\n'
|
|
'\n'
|
|
'[!] Not Available Validator\n'
|
|
' ✗ A useful error message\n'
|
|
' • A message that is not an\n'
|
|
' error\n'
|
|
' ! A hint message\n'
|
|
'\n'
|
|
'[!] Partial Validator with\n'
|
|
' only a Hint\n'
|
|
' ! There is a hint here\n'
|
|
' • But there is no error\n'
|
|
'\n'
|
|
'[!] Partial Validator with\n'
|
|
' Errors\n'
|
|
' ✗ An error message\n'
|
|
' indicating partial\n'
|
|
' installation\n'
|
|
' ! Maybe a hint will help\n'
|
|
' the user\n'
|
|
' • An extra message with\n'
|
|
' some verbose details\n'
|
|
'\n'
|
|
'! Doctor found issues in 4\n'
|
|
' categories.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
group('doctor with grouped validators', () {
|
|
testUsingContext('validate diagnose combines validator output', () async {
|
|
expect(await FakeGroupedDoctor(logger).diagnose(), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'[✓] Category 1\n'
|
|
' • A helpful message\n'
|
|
' • A helpful message\n'
|
|
'\n'
|
|
'[!] Category 2\n'
|
|
' • A helpful message\n'
|
|
' ✗ A useful error message\n'
|
|
'\n'
|
|
'! Doctor found issues in 1 category.\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate merging assigns statusInfo and title', () async {
|
|
// There are two subvalidators. Only the second contains statusInfo.
|
|
expect(await FakeGroupedDoctorWithStatus(logger).diagnose(), isTrue);
|
|
expect(logger.statusText, equals(
|
|
'[✓] First validator title (A status message)\n'
|
|
' • A helpful message\n'
|
|
' • A different message\n'
|
|
'\n'
|
|
'• No issues found!\n'
|
|
));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
});
|
|
|
|
group('grouped validator merging results', () {
|
|
final PassingGroupedValidator installed = PassingGroupedValidator('Category');
|
|
final PartialGroupedValidator partial = PartialGroupedValidator('Category');
|
|
final MissingGroupedValidator missing = MissingGroupedValidator('Category');
|
|
|
|
testUsingContext('validate installed + installed = installed', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, installed, installed).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[✓]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate installed + partial = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, installed, partial).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate installed + missing = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, installed, missing).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate partial + installed = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, partial, installed).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate partial + partial = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, partial, partial).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate partial + missing = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, partial, missing).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate missing + installed = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, missing, installed).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate missing + partial = partial', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, missing, partial).diagnose(), isTrue);
|
|
expect(logger.statusText, startsWith('[!]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
|
|
testUsingContext('validate missing + missing = missing', () async {
|
|
expect(await FakeSmallGroupDoctor(logger, missing, missing).diagnose(), isFalse);
|
|
expect(logger.statusText, startsWith('[✗]'));
|
|
}, overrides: <Type, Generator>{
|
|
AnsiTerminal: () => FakeTerminal(),
|
|
});
|
|
});
|
|
|
|
testUsingContext('WebWorkflow is a part of validator workflows if enabled', () async {
|
|
final List<Workflow> workflows = DoctorValidatorsProvider.test(
|
|
featureFlags: TestFeatureFlags(isWebEnabled: true),
|
|
platform: FakePlatform(),
|
|
).workflows;
|
|
expect(
|
|
workflows,
|
|
contains(isA<WebWorkflow>()),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
ProcessManager: () => fakeProcessManager,
|
|
});
|
|
|
|
testUsingContext('Fetches tags to get the right version', () async {
|
|
Cache.disableLocking();
|
|
|
|
final DoctorCommand doctorCommand = DoctorCommand();
|
|
final CommandRunner<void> commandRunner = createTestCommandRunner(doctorCommand);
|
|
|
|
await commandRunner.run(<String>['doctor']);
|
|
|
|
expect(flutterVersion.didFetchTagsAndUpdate, true);
|
|
Cache.enableLocking();
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => FakeProcessManager.any(),
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
FlutterVersion: () => flutterVersion,
|
|
Doctor: () => NoOpDoctor(),
|
|
}, initializeFlutterRoot: false);
|
|
|
|
testUsingContext('If android workflow is disabled, AndroidStudio validator is not included', () {
|
|
final DoctorValidatorsProvider provider = DoctorValidatorsProvider.test(
|
|
featureFlags: TestFeatureFlags(isAndroidEnabled: false),
|
|
);
|
|
expect(provider.validators, isNot(contains(isA<AndroidStudioValidator>())));
|
|
expect(provider.validators, isNot(contains(isA<NoAndroidStudioValidator>())));
|
|
}, overrides: <Type, Generator>{
|
|
AndroidWorkflow: () => FakeAndroidWorkflow(appliesToHostPlatform: false),
|
|
});
|
|
}
|
|
|
|
class FakeAndroidWorkflow extends Fake implements AndroidWorkflow {
|
|
FakeAndroidWorkflow({
|
|
this.canListDevices = true,
|
|
this.appliesToHostPlatform = true,
|
|
});
|
|
|
|
@override
|
|
final bool canListDevices;
|
|
|
|
@override
|
|
final bool appliesToHostPlatform;
|
|
}
|
|
|
|
|
|
class NoOpDoctor implements Doctor {
|
|
@override
|
|
bool get canLaunchAnything => true;
|
|
|
|
@override
|
|
bool get canListAnything => true;
|
|
|
|
@override
|
|
Future<bool> checkRemoteArtifacts(String engineRevision) async => true;
|
|
|
|
@override
|
|
Future<bool> diagnose({
|
|
bool androidLicenses = false,
|
|
bool verbose = true,
|
|
bool showColor = true,
|
|
AndroidLicenseValidator? androidLicenseValidator,
|
|
bool showPii = true,
|
|
List<ValidatorTask>? startedValidatorTasks,
|
|
bool sendEvent = true,
|
|
}) async => true;
|
|
|
|
@override
|
|
List<ValidatorTask> startValidatorTasks() => <ValidatorTask>[];
|
|
|
|
@override
|
|
Future<void> summary() async { }
|
|
|
|
@override
|
|
List<DoctorValidator> get validators => <DoctorValidator>[];
|
|
|
|
@override
|
|
List<Workflow> get workflows => <Workflow>[];
|
|
}
|
|
|
|
class PassingValidator extends DoctorValidator {
|
|
PassingValidator(super.name);
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage('A helpful message'),
|
|
ValidationMessage('A second, somewhat longer helpful message'),
|
|
];
|
|
return const ValidationResult(ValidationType.installed, messages, statusInfo: 'with statusInfo');
|
|
}
|
|
}
|
|
|
|
class PiiValidator extends DoctorValidator {
|
|
PiiValidator() : super('PII Validator');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage('Contains PII path/to/username', piiStrippedMessage: 'Does not contain PII'),
|
|
];
|
|
return const ValidationResult(ValidationType.installed, messages);
|
|
}
|
|
}
|
|
|
|
class MissingValidator extends DoctorValidator {
|
|
MissingValidator() : super('Missing Validator');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage.error('A useful error message'),
|
|
ValidationMessage('A message that is not an error'),
|
|
ValidationMessage.hint('A hint message'),
|
|
];
|
|
return const ValidationResult(ValidationType.missing, messages);
|
|
}
|
|
}
|
|
|
|
class NotAvailableValidator extends DoctorValidator {
|
|
NotAvailableValidator() : super('Not Available Validator');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage.error('A useful error message'),
|
|
ValidationMessage('A message that is not an error'),
|
|
ValidationMessage.hint('A hint message'),
|
|
];
|
|
return const ValidationResult(ValidationType.notAvailable, messages);
|
|
}
|
|
}
|
|
|
|
class StuckValidator extends DoctorValidator {
|
|
StuckValidator() : super('Stuck validator that never completes');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() {
|
|
final Completer<ValidationResult> completer = Completer<ValidationResult>();
|
|
|
|
// This future will never complete
|
|
return completer.future;
|
|
}
|
|
}
|
|
|
|
class PartialValidatorWithErrors extends DoctorValidator {
|
|
PartialValidatorWithErrors() : super('Partial Validator with Errors');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage.error('An error message indicating partial installation'),
|
|
ValidationMessage.hint('Maybe a hint will help the user'),
|
|
ValidationMessage('An extra message with some verbose details'),
|
|
];
|
|
return const ValidationResult(ValidationType.partial, messages);
|
|
}
|
|
}
|
|
|
|
class PartialValidatorWithHintsOnly extends DoctorValidator {
|
|
PartialValidatorWithHintsOnly() : super('Partial Validator with only a Hint');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage.hint('There is a hint here'),
|
|
ValidationMessage('But there is no error'),
|
|
];
|
|
return const ValidationResult(ValidationType.partial, messages);
|
|
}
|
|
}
|
|
|
|
class CrashingValidator extends DoctorValidator {
|
|
CrashingValidator() : super('Crashing validator');
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
throw StateError('fatal error');
|
|
}
|
|
}
|
|
|
|
class AsyncCrashingValidator extends DoctorValidator {
|
|
AsyncCrashingValidator(this._time) : super('Async crashing validator');
|
|
|
|
final FakeAsync _time;
|
|
|
|
@override
|
|
Future<ValidationResult> validate() {
|
|
const Duration delay = Duration(seconds: 1);
|
|
final Future<ValidationResult> result = Future<ValidationResult>.delayed(
|
|
delay,
|
|
() => throw StateError('fatal error'),
|
|
);
|
|
_time.elapse(const Duration(seconds: 1));
|
|
_time.flushMicrotasks();
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/// A doctor that fails with a missing [ValidationResult].
|
|
class FakeDoctor extends Doctor {
|
|
FakeDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
MissingValidator(),
|
|
NotAvailableValidator(),
|
|
PartialValidatorWithHintsOnly(),
|
|
PartialValidatorWithErrors(),
|
|
];
|
|
}
|
|
|
|
/// A doctor that should pass, but still has issues in some categories.
|
|
class FakePassingDoctor extends Doctor {
|
|
FakePassingDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
PartialValidatorWithHintsOnly(),
|
|
PartialValidatorWithErrors(),
|
|
PassingValidator('Another Passing Validator'),
|
|
];
|
|
}
|
|
|
|
/// A doctor that should pass, but still has 1 issue to test the singular of
|
|
/// categories.
|
|
class FakeSinglePassingDoctor extends Doctor {
|
|
FakeSinglePassingDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PartialValidatorWithHintsOnly(),
|
|
];
|
|
}
|
|
|
|
/// A doctor that passes and has no issues anywhere.
|
|
class FakeQuietDoctor extends Doctor {
|
|
FakeQuietDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
PassingValidator('Another Passing Validator'),
|
|
PassingValidator('Validators are fun'),
|
|
PassingValidator('Four score and seven validators ago'),
|
|
];
|
|
}
|
|
|
|
/// A doctor that passes and contains PII that can be hidden.
|
|
class FakePiiDoctor extends Doctor {
|
|
FakePiiDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PiiValidator(),
|
|
];
|
|
}
|
|
|
|
/// A doctor with a validator that throws an exception.
|
|
class FakeCrashingDoctor extends Doctor {
|
|
FakeCrashingDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
PassingValidator('Another Passing Validator'),
|
|
CrashingValidator(),
|
|
PassingValidator('Validators are fun'),
|
|
PassingValidator('Four score and seven validators ago'),
|
|
];
|
|
}
|
|
|
|
/// A doctor with a validator that will never finish.
|
|
class FakeAsyncStuckDoctor extends Doctor {
|
|
FakeAsyncStuckDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
PassingValidator('Another Passing Validator'),
|
|
StuckValidator(),
|
|
PassingValidator('Validators are fun'),
|
|
PassingValidator('Four score and seven validators ago'),
|
|
];
|
|
}
|
|
|
|
/// A doctor with a validator that throws an exception.
|
|
class FakeAsyncCrashingDoctor extends Doctor {
|
|
FakeAsyncCrashingDoctor(this._time, Logger logger) : super(logger: logger);
|
|
|
|
final FakeAsync _time;
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
PassingValidator('Another Passing Validator'),
|
|
AsyncCrashingValidator(_time),
|
|
PassingValidator('Validators are fun'),
|
|
PassingValidator('Four score and seven validators ago'),
|
|
];
|
|
}
|
|
|
|
/// A DoctorValidatorsProvider that overrides the default validators without
|
|
/// overriding the doctor.
|
|
class FakeDoctorValidatorsProvider implements DoctorValidatorsProvider {
|
|
@override
|
|
List<DoctorValidator> get validators {
|
|
return <DoctorValidator>[
|
|
PassingValidator('Passing Validator'),
|
|
PassingValidator('Another Passing Validator'),
|
|
PassingValidator('Providing validators is fun'),
|
|
];
|
|
}
|
|
|
|
@override
|
|
List<Workflow> get workflows => <Workflow>[];
|
|
}
|
|
|
|
class PassingGroupedValidator extends DoctorValidator {
|
|
PassingGroupedValidator(super.name);
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage('A helpful message'),
|
|
];
|
|
return const ValidationResult(ValidationType.installed, messages);
|
|
}
|
|
}
|
|
|
|
class MissingGroupedValidator extends DoctorValidator {
|
|
MissingGroupedValidator(super.name);
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage.error('A useful error message'),
|
|
];
|
|
return const ValidationResult(ValidationType.missing, messages);
|
|
}
|
|
}
|
|
|
|
class PartialGroupedValidator extends DoctorValidator {
|
|
PartialGroupedValidator(super.name);
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage.error('An error message for partial installation'),
|
|
];
|
|
return const ValidationResult(ValidationType.partial, messages);
|
|
}
|
|
}
|
|
|
|
class PassingGroupedValidatorWithStatus extends DoctorValidator {
|
|
PassingGroupedValidatorWithStatus(super.name);
|
|
|
|
@override
|
|
Future<ValidationResult> validate() async {
|
|
const List<ValidationMessage> messages = <ValidationMessage>[
|
|
ValidationMessage('A different message'),
|
|
];
|
|
return const ValidationResult(ValidationType.installed, messages, statusInfo: 'A status message');
|
|
}
|
|
}
|
|
|
|
/// A doctor that has two groups of two validators each.
|
|
class FakeGroupedDoctor extends Doctor {
|
|
FakeGroupedDoctor(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
GroupedValidator(<DoctorValidator>[
|
|
PassingGroupedValidator('Category 1'),
|
|
PassingGroupedValidator('Category 1'),
|
|
]),
|
|
GroupedValidator(<DoctorValidator>[
|
|
PassingGroupedValidator('Category 2'),
|
|
MissingGroupedValidator('Category 2'),
|
|
]),
|
|
];
|
|
}
|
|
|
|
class FakeGroupedDoctorWithStatus extends Doctor {
|
|
FakeGroupedDoctorWithStatus(Logger logger) : super(logger: logger);
|
|
|
|
@override
|
|
late final List<DoctorValidator> validators = <DoctorValidator>[
|
|
GroupedValidator(<DoctorValidator>[
|
|
PassingGroupedValidator('First validator title'),
|
|
PassingGroupedValidatorWithStatus('Second validator title'),
|
|
]),
|
|
];
|
|
}
|
|
|
|
/// A doctor that takes any two validators. Used to check behavior when
|
|
/// merging ValidationTypes (installed, missing, partial).
|
|
class FakeSmallGroupDoctor extends Doctor {
|
|
FakeSmallGroupDoctor(Logger logger, DoctorValidator val1, DoctorValidator val2)
|
|
: validators = <DoctorValidator>[GroupedValidator(<DoctorValidator>[val1, val2])],
|
|
super(logger: logger);
|
|
|
|
@override
|
|
final List<DoctorValidator> validators;
|
|
}
|
|
|
|
class VsCodeValidatorTestTargets extends VsCodeValidator {
|
|
VsCodeValidatorTestTargets._(String installDirectory, String extensionDirectory, {String? edition})
|
|
: super(VsCode.fromDirectory(installDirectory, extensionDirectory, edition: edition, fileSystem: globals.fs));
|
|
|
|
static VsCodeValidatorTestTargets get installedWithExtension =>
|
|
VsCodeValidatorTestTargets._(validInstall, validExtensions);
|
|
|
|
static VsCodeValidatorTestTargets get installedWithExtension64bit =>
|
|
VsCodeValidatorTestTargets._(validInstall, validExtensions, edition: '64-bit edition');
|
|
|
|
static VsCodeValidatorTestTargets get installedWithoutExtension =>
|
|
VsCodeValidatorTestTargets._(validInstall, missingExtensions);
|
|
|
|
static final String validInstall = globals.fs.path.join('test', 'data', 'vscode', 'application');
|
|
static final String validExtensions = globals.fs.path.join('test', 'data', 'vscode', 'extensions');
|
|
static final String missingExtensions = globals.fs.path.join('test', 'data', 'vscode', 'notExtensions');
|
|
}
|
|
|
|
class FakeDeviceManager extends Fake implements DeviceManager {
|
|
List<String> diagnostics = <String>[];
|
|
List<Device> devices = <Device>[];
|
|
|
|
@override
|
|
Future<List<Device>> getAllConnectedDevices() async => devices;
|
|
|
|
@override
|
|
Future<List<String>> getDeviceDiagnostics() async => diagnostics;
|
|
}
|
|
|
|
// Unfortunately Device, despite not being immutable, has an `operator ==`.
|
|
// Until we fix that, we have to also ignore related lints here.
|
|
// ignore: avoid_implementing_value_types
|
|
class FakeDevice extends Fake implements Device {
|
|
@override
|
|
String get name => 'name';
|
|
|
|
@override
|
|
String get id => 'device-id';
|
|
|
|
@override
|
|
Category get category => Category.mobile;
|
|
|
|
@override
|
|
bool isSupported() => true;
|
|
|
|
@override
|
|
Future<bool> get isLocalEmulator async => false;
|
|
|
|
@override
|
|
Future<String> get targetPlatformDisplayName async => 'android';
|
|
|
|
@override
|
|
Future<String> get sdkNameAndVersion async => '1.2.3';
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform => Future<TargetPlatform>.value(TargetPlatform.android);
|
|
}
|
|
|
|
class FakeTerminal extends Fake implements AnsiTerminal {
|
|
@override
|
|
final bool supportsColor = false;
|
|
}
|