Commit 8b5af14f authored by tonyzhao1's avatar tonyzhao1 Committed by Mehmet Fidanboylu

Use grouped validator instead of categories (#21577)

This is a cleanup PR.
parent 36b735ee
......@@ -13,7 +13,7 @@ import 'android_studio.dart';
class AndroidStudioValidator extends DoctorValidator {
final AndroidStudio _studio;
AndroidStudioValidator(this._studio) : super('Android Studio', ValidatorCategory.androidStudio);
AndroidStudioValidator(this._studio) : super('Android Studio');
static List<DoctorValidator> get allValidators {
final List<DoctorValidator> validators = <DoctorValidator>[];
......
......@@ -46,8 +46,7 @@ class AndroidWorkflow implements Workflow {
}
class AndroidValidator extends DoctorValidator {
AndroidValidator(): super('Android toolchain - develop for Android devices',
ValidatorCategory.androidToolchain);
AndroidValidator(): super('Android toolchain - develop for Android devices',);
static const String _jdkDownload = 'https://www.oracle.com/technetwork/java/javase/downloads/';
......
......@@ -54,6 +54,7 @@ Future<T> runInContext<T>(
Cache: () => Cache(),
Clock: () => const Clock(),
CocoaPods: () => CocoaPods(),
CocoaPodsValidator: () => const CocoaPodsValidator(),
Config: () => Config(),
DevFSConfig: () => DevFSConfig(),
DeviceManager: () => DeviceManager(),
......
......@@ -51,7 +51,7 @@ class _DefaultDoctorValidatorsProvider implements DoctorValidatorsProvider {
_validators.add(androidValidator);
if (iosWorkflow.appliesToHostPlatform)
_validators.add(iosValidator);
_validators.add(GroupedValidator(<DoctorValidator>[iosValidator, cocoapodsValidator]));
final List<DoctorValidator> ideValidators = <DoctorValidator>[];
ideValidators.addAll(AndroidStudioValidator.allValidators);
......@@ -121,28 +121,8 @@ class Doctor {
bool allGood = true;
final Set<ValidatorCategory> finishedGroups = Set<ValidatorCategory>();
for (DoctorValidator validator in validators) {
final ValidatorCategory currentCategory = validator.category;
ValidationResult result;
if (currentCategory.isGrouped) {
if (finishedGroups.contains(currentCategory)) {
// We already handled this category via a previous validator.
continue;
}
// Skip ahead and get results for the other validators in this category.
final List<ValidationResult> results = <ValidationResult>[];
for (DoctorValidator subValidator in validators.where(
(DoctorValidator v) => v.category == currentCategory)) {
results.add(await subValidator.validate());
}
result = _mergeValidationResults(results);
finishedGroups.add(currentCategory);
} else {
result = await validator.validate();
}
final ValidationResult result = await validator.validate();
buffer.write('${result.leadingBox} ${validator.title} is ');
if (result.type == ValidationType.missing)
buffer.write('not installed.');
......@@ -158,7 +138,6 @@ class Doctor {
if (result.type != ValidationType.installed)
allGood = false;
}
if (!allGood) {
......@@ -180,39 +159,15 @@ class Doctor {
bool doctorResult = true;
int issues = 0;
final List<ValidatorTask> taskList = startValidatorTasks();
final Set<ValidatorCategory> finishedGroups = Set<ValidatorCategory>();
for (ValidatorTask validatorTask in taskList) {
for (ValidatorTask validatorTask in startValidatorTasks()) {
final DoctorValidator validator = validatorTask.validator;
final ValidatorCategory currentCategory = validator.category;
final Status status = Status.withSpinner();
ValidationResult result;
if (currentCategory.isGrouped) {
if (finishedGroups.contains(currentCategory)) {
continue;
}
final List<ValidationResult> results = <ValidationResult>[];
for (ValidatorTask subValidator in taskList.where(
(ValidatorTask t) => t.validator.category == currentCategory)) {
try {
results.add(await subValidator.result);
} catch (exception) {
status.cancel();
rethrow;
}
}
result = _mergeValidationResults(results);
finishedGroups.add(currentCategory);
} else {
try {
result = await validatorTask.result;
} catch (exception) {
status.cancel();
rethrow;
}
try {
result = await validatorTask.result;
} catch (exception) {
status.cancel();
rethrow;
}
status.stop();
......@@ -256,35 +211,6 @@ class Doctor {
return doctorResult;
}
ValidationResult _mergeValidationResults(List<ValidationResult> results) {
ValidationType mergedType = results[0].type;
final List<ValidationMessage> mergedMessages = <ValidationMessage>[];
for (ValidationResult result in results) {
switch (result.type) {
case ValidationType.installed:
if (mergedType == ValidationType.missing) {
mergedType = ValidationType.partial;
}
break;
case ValidationType.partial:
mergedType = ValidationType.partial;
break;
case ValidationType.missing:
if (mergedType == ValidationType.installed) {
mergedType = ValidationType.partial;
}
break;
default:
throw 'Unrecognized validation type: ' + result.type.toString();
}
mergedMessages.addAll(result.messages);
}
return ValidationResult(mergedType, mergedMessages,
statusInfo: results[0].statusInfo);
}
bool get canListAnything => workflows.any((Workflow workflow) => workflow.canListDevices);
bool get canLaunchAnything {
......@@ -292,11 +218,8 @@ class Doctor {
return true;
return workflows.any((Workflow workflow) => workflow.canLaunchDevices);
}
}
/// A series of tools and required install steps for a target platform (iOS or Android).
abstract class Workflow {
/// Whether the workflow applies to this platform (as in, should we ever try and use it).
......@@ -318,32 +241,69 @@ enum ValidationType {
installed
}
/// Validator output is grouped by category.
class ValidatorCategory {
final String name;
// Whether we should bundle results for validators sharing this cateogry,
// or let each stand alone.
final bool isGrouped;
const ValidatorCategory(this.name, this.isGrouped);
static const ValidatorCategory androidToolchain = ValidatorCategory('androidToolchain', true);
static const ValidatorCategory androidStudio = ValidatorCategory('androidStudio', false);
static const ValidatorCategory ios = ValidatorCategory('ios', true);
static const ValidatorCategory flutter = ValidatorCategory('flutter', false);
static const ValidatorCategory ide = ValidatorCategory('ide', false);
static const ValidatorCategory device = ValidatorCategory('device', false);
static const ValidatorCategory other = ValidatorCategory('other', false);
}
abstract class DoctorValidator {
const DoctorValidator(this.title, [this.category = ValidatorCategory.other]);
const DoctorValidator(this.title);
final String title;
final ValidatorCategory category;
Future<ValidationResult> validate();
}
/// A validator that runs other [DoctorValidator]s and combines their output
/// into a single [ValidationResult]. It uses the title of the first validator
/// passed to the constructor and reports the statusInfo of the first validator
/// that provides one. Other titles and statusInfo strings are discarded.
class GroupedValidator extends DoctorValidator {
GroupedValidator(this.subValidators) : super(subValidators[0].title);
final List<DoctorValidator> subValidators;
@override
Future<ValidationResult> validate() async {
final List<ValidatorTask> tasks = <ValidatorTask>[];
for (DoctorValidator validator in subValidators) {
tasks.add(ValidatorTask(validator, validator.validate()));
}
final List<ValidationResult> results = <ValidationResult>[];
for (ValidatorTask subValidator in tasks) {
results.add(await subValidator.result);
}
return _mergeValidationResults(results);
}
ValidationResult _mergeValidationResults(List<ValidationResult> results) {
assert(results.isNotEmpty, 'Validation results should not be empty');
ValidationType mergedType = results[0].type;
final List<ValidationMessage> mergedMessages = <ValidationMessage>[];
String statusInfo;
for (ValidationResult result in results) {
statusInfo ??= result.statusInfo;
switch (result.type) {
case ValidationType.installed:
if (mergedType == ValidationType.missing) {
mergedType = ValidationType.partial;
}
break;
case ValidationType.partial:
mergedType = ValidationType.partial;
break;
case ValidationType.missing:
if (mergedType == ValidationType.installed) {
mergedType = ValidationType.partial;
}
break;
default:
throw 'Unrecognized validation type: ' + result.type.toString();
}
mergedMessages.addAll(result.messages);
}
return ValidationResult(mergedType, mergedMessages,
statusInfo: statusInfo);
}
}
class ValidationResult {
/// [ValidationResult.type] should only equal [ValidationResult.installed]
......@@ -383,7 +343,7 @@ class ValidationMessage {
}
class _FlutterValidator extends DoctorValidator {
_FlutterValidator() : super('Flutter', ValidatorCategory.flutter);
_FlutterValidator() : super('Flutter');
@override
Future<ValidationResult> validate() async {
......@@ -432,7 +392,7 @@ bool _genSnapshotRuns(String genSnapshotPath) {
}
class NoIdeValidator extends DoctorValidator {
NoIdeValidator() : super('Flutter IDE Support',ValidatorCategory.ide);
NoIdeValidator() : super('Flutter IDE Support');
@override
Future<ValidationResult> validate() async {
......@@ -445,7 +405,7 @@ class NoIdeValidator extends DoctorValidator {
abstract class IntelliJValidator extends DoctorValidator {
final String installPath;
IntelliJValidator(String title, this.installPath) : super(title, ValidatorCategory.ide);
IntelliJValidator(String title, this.installPath) : super(title);
String get version;
String get pluginsPath;
......@@ -640,7 +600,7 @@ class IntelliJValidatorOnMac extends IntelliJValidator {
}
class DeviceValidator extends DoctorValidator {
DeviceValidator() : super('Connected devices', ValidatorCategory.device);
DeviceValidator() : super('Connected devices');
@override
Future<ValidationResult> validate() async {
......
......@@ -16,6 +16,7 @@ import 'plist_utils.dart' as plist;
IOSWorkflow get iosWorkflow => context[IOSWorkflow];
IOSValidator get iosValidator => context[IOSValidator];
CocoaPodsValidator get cocoapodsValidator => context[CocoaPodsValidator];
class IOSWorkflow implements Workflow {
const IOSWorkflow();
......@@ -42,8 +43,7 @@ class IOSWorkflow implements Workflow {
class IOSValidator extends DoctorValidator {
const IOSValidator() : super('iOS toolchain - develop for iOS devices', ValidatorCategory.ios);
const IOSValidator() : super('iOS toolchain - develop for iOS devices');
Future<bool> get hasIDeviceInstaller => exitsHappyAsync(<String>['ideviceinstaller', '-h']);
......@@ -175,13 +175,45 @@ class IOSValidator extends DoctorValidator {
}
}
final CocoaPodsStatus cocoaPodsStatus = await cocoaPods.evaluateCocoaPodsInstallation;
} else {
brewStatus = ValidationType.missing;
messages.add(ValidationMessage.error(
'Brew not installed; use this to install tools for iOS device development.\n'
'Download brew at https://brew.sh/.'
));
}
return ValidationResult(
<ValidationType>[xcodeStatus, brewStatus].reduce(_mergeValidationTypes),
messages,
statusInfo: xcodeVersionInfo
);
}
ValidationType _mergeValidationTypes(ValidationType t1, ValidationType t2) {
return t1 == t2 ? t1 : ValidationType.partial;
}
}
class CocoaPodsValidator extends DoctorValidator {
const CocoaPodsValidator() : super('CocoaPods subvalidator');
bool get hasHomebrew => os.which('brew') != null;
@override
Future<ValidationResult> validate() async {
final List<ValidationMessage> messages = <ValidationMessage>[];
ValidationType status = ValidationType.installed;
if (hasHomebrew) {
final CocoaPodsStatus cocoaPodsStatus = await cocoaPods
.evaluateCocoaPodsInstallation;
if (cocoaPodsStatus == CocoaPodsStatus.recommended) {
if (await cocoaPods.isCocoaPodsInitialized) {
messages.add(ValidationMessage('CocoaPods version ${await cocoaPods.cocoaPodsVersionText}'));
} else {
brewStatus = ValidationType.partial;
status = ValidationType.partial;
messages.add(ValidationMessage.error(
'CocoaPods installed but not initialized.\n'
'$noCocoaPodsConsequence\n'
......@@ -191,8 +223,8 @@ class IOSValidator extends DoctorValidator {
));
}
} else {
brewStatus = ValidationType.partial;
if (cocoaPodsStatus == CocoaPodsStatus.notInstalled) {
status = ValidationType.missing;
messages.add(ValidationMessage.error(
'CocoaPods not installed.\n'
'$noCocoaPodsConsequence\n'
......@@ -200,6 +232,7 @@ class IOSValidator extends DoctorValidator {
'$cocoaPodsInstallInstructions'
));
} else {
status = ValidationType.partial;
messages.add(ValidationMessage.hint(
'CocoaPods out of date (${cocoaPods.cocoaPodsRecommendedVersion} is recommended).\n'
'$noCocoaPodsConsequence\n'
......@@ -209,21 +242,9 @@ class IOSValidator extends DoctorValidator {
}
}
} else {
brewStatus = ValidationType.missing;
messages.add(ValidationMessage.error(
'Brew not installed; use this to install tools for iOS device development.\n'
'Download brew at https://brew.sh/.'
));
// Only set status. The main validator handles messages for missing brew.
status = ValidationType.missing;
}
return ValidationResult(
<ValidationType>[xcodeStatus, brewStatus].reduce(_mergeValidationTypes),
messages,
statusInfo: xcodeVersionInfo
);
}
ValidationType _mergeValidationTypes(ValidationType t1, ValidationType t2) {
return t1 == t2 ? t1 : ValidationType.partial;
return ValidationResult(status, messages);
}
}
......@@ -13,7 +13,7 @@ class VsCodeValidator extends DoctorValidator {
'https://marketplace.visualstudio.com/items?itemName=${VsCode.extensionIdentifier}';
final VsCode _vsCode;
VsCodeValidator(this._vsCode) : super(_vsCode.productName, ValidatorCategory.ide);
VsCodeValidator(this._vsCode) : super(_vsCode.productName);
static Iterable<DoctorValidator> get installedValidators {
return VsCode
......
......@@ -184,7 +184,6 @@ void main() {
});
});
group('doctor with grouped validators', () {
testUsingContext('validate diagnose combines validator output', () async {
expect(await FakeGroupedDoctor().diagnose(), isTrue);
......@@ -201,22 +200,24 @@ void main() {
));
});
testUsingContext('validate summary combines validator output', () async {
expect(await FakeGroupedDoctor().summaryText, equals(
'[✓] Category 1 is fully installed.\n'
'[!] Category 2 is partially installed; more components are available.\n'
testUsingContext('validate merging assigns statusInfo and title', () async {
// There are two subvalidators. Only the second contains statusInfo.
expect(await FakeGroupedDoctorWithStatus().diagnose(), isTrue);
expect(testLogger.statusText, equals(
'[✓] First validator title (A status message)\n'
' • A helpful message\n'
' • A different message\n'
'\n'
'Run "flutter doctor" for information about installing additional components.\n'
'• No issues found!\n'
));
});
});
group('doctor merging validator results', () {
final PassingGroupedValidator installed = PassingGroupedValidator('Category', groupedCategory1);
final PartialGroupedValidator partial = PartialGroupedValidator('Category', groupedCategory1);
final MissingGroupedValidator missing = MissingGroupedValidator('Category', groupedCategory1);
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(installed, installed).diagnose(), isTrue);
......@@ -405,11 +406,9 @@ class FakeDoctorValidatorsProvider implements DoctorValidatorsProvider {
}
ValidatorCategory groupedCategory1 = const ValidatorCategory('group 1', true);
ValidatorCategory groupedCategory2 = const ValidatorCategory('group 2', true);
class PassingGroupedValidator extends DoctorValidator {
PassingGroupedValidator(String name, ValidatorCategory group) : super(name, group);
PassingGroupedValidator(String name) : super(name);
@override
Future<ValidationResult> validate() async {
......@@ -421,7 +420,7 @@ class PassingGroupedValidator extends DoctorValidator {
}
class MissingGroupedValidator extends DoctorValidator {
MissingGroupedValidator(String name, ValidatorCategory group): super(name, group);
MissingGroupedValidator(String name): super(name);
@override
Future<ValidationResult> validate() async {
......@@ -432,7 +431,7 @@ class MissingGroupedValidator extends DoctorValidator {
}
class PartialGroupedValidator extends DoctorValidator {
PartialGroupedValidator(String name, ValidatorCategory group): super(name, group);
PartialGroupedValidator(String name): super(name);
@override
Future<ValidationResult> validate() async {
......@@ -442,28 +441,56 @@ class PartialGroupedValidator extends DoctorValidator {
}
}
/// A doctor that has two category groups of two validators each.
class PassingGroupedValidatorWithStatus extends DoctorValidator {
PassingGroupedValidatorWithStatus(String name) : super(name);
@override
Future<ValidationResult> validate() async {
final List<ValidationMessage> messages = <ValidationMessage>[];
messages.add(ValidationMessage('A different message'));
return ValidationResult(ValidationType.installed, messages, statusInfo: 'A status message');
}
}
/// A doctor that has two groups of two validators each.
class FakeGroupedDoctor extends Doctor {
List<DoctorValidator> _validators;
@override
List<DoctorValidator> get validators {
if (_validators == null) {
_validators = <DoctorValidator>[];
_validators.add(PassingGroupedValidator('Category 1', groupedCategory1));
_validators.add(PassingGroupedValidator('Category 1', groupedCategory1));
_validators.add(PassingGroupedValidator('Category 2', groupedCategory2));
_validators.add(MissingGroupedValidator('Category 2', groupedCategory2));
_validators.add(GroupedValidator(<DoctorValidator>[
PassingGroupedValidator('Category 1'),
PassingGroupedValidator('Category 1')
]));
_validators.add(GroupedValidator(<DoctorValidator>[
PassingGroupedValidator('Category 2'),
MissingGroupedValidator('Category 2')
]));
}
return _validators;
}
}
class FakeGroupedDoctorWithStatus extends Doctor {
List<DoctorValidator> _validators;
@override
List<DoctorValidator> get validators {
_validators ??= <DoctorValidator>[
GroupedValidator(<DoctorValidator>[
PassingGroupedValidator('First validator title'),
PassingGroupedValidatorWithStatus('Second validator title'),
])];
return _validators;
}
}
/// A doctor that takes any two validators. Used to check behavior when
/// merging ValidationTypes (installed, missing, partial).
class FakeSmallGroupDoctor extends Doctor {
List<DoctorValidator> _validators;
FakeSmallGroupDoctor(DoctorValidator val1, DoctorValidator val2) {
_validators = <DoctorValidator>[val1, val2];
_validators = <DoctorValidator>[GroupedValidator(<DoctorValidator>[val1, val2])];
}
@override
List<DoctorValidator> get validators => _validators;
......
......@@ -190,15 +190,13 @@ void main() {
CocoaPods: () => cocoaPods,
});
testUsingContext('Emits partial status when CocoaPods is not installed', () async {
testUsingContext('Emits partial status when simctl is not installed', () async {
when(xcode.isInstalled).thenReturn(true);
when(xcode.versionText)
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true);
when(cocoaPods.evaluateCocoaPodsInstallation)
.thenAnswer((_) async => CocoaPodsStatus.notInstalled);
when(xcode.isSimctlInstalled).thenReturn(true);
when(xcode.isSimctlInstalled).thenReturn(false);
final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget();
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial);
......@@ -208,35 +206,19 @@ void main() {
CocoaPods: () => cocoaPods,
});
testUsingContext('Emits partial status when CocoaPods version is too low', () async {
when(xcode.isInstalled).thenReturn(true);
when(xcode.versionText)
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true);
when(cocoaPods.evaluateCocoaPodsInstallation)
.thenAnswer((_) async => CocoaPodsStatus.belowRecommendedVersion);
when(xcode.isSimctlInstalled).thenReturn(true);
final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget();
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
IMobileDevice: () => iMobileDevice,
Xcode: () => xcode,
CocoaPods: () => cocoaPods,
});
testUsingContext('Emits partial status when CocoaPods is not initialized', () async {
testUsingContext('Succeeds when all checks pass', () async {
when(xcode.isInstalled).thenReturn(true);
when(xcode.versionText)
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true);
when(cocoaPods.isCocoaPodsInitialized).thenAnswer((_) async => false);
when(xcode.isSimctlInstalled).thenReturn(true);
ensureDirectoryExists(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master', 'README.md'));
final ValidationResult result = await IOSWorkflowTestTarget().validate();
expect(result.type, ValidationType.partial);
expect(result.type, ValidationType.installed);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
IMobileDevice: () => iMobileDevice,
......@@ -244,42 +226,62 @@ void main() {
CocoaPods: () => cocoaPods,
ProcessManager: () => processManager,
});
});
testUsingContext('Emits partial status when simctl is not installed', () async {
when(xcode.isInstalled).thenReturn(true);
when(xcode.versionText)
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true);
when(xcode.isSimctlInstalled).thenReturn(false);
final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget();
group('iOS CocoaPods validation', () {
MockCocoaPods cocoaPods;
setUp(() {
cocoaPods = MockCocoaPods();
when(cocoaPods.evaluateCocoaPodsInstallation)
.thenAnswer((_) async => CocoaPodsStatus.recommended);
when(cocoaPods.isCocoaPodsInitialized).thenAnswer((_) async => true);
when(cocoaPods.cocoaPodsVersionText).thenAnswer((_) async => '1.8.0');
});
testUsingContext('Emits installed status when CocoaPods is installed', () async {
final CocoaPodsTestTarget workflow = CocoaPodsTestTarget();
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial);
expect(result.type, ValidationType.installed);
}, overrides: <Type, Generator>{
IMobileDevice: () => iMobileDevice,
Xcode: () => xcode,
CocoaPods: () => cocoaPods,
});
testUsingContext('Emits missing status when CocoaPods is not installed', () async {
when(cocoaPods.evaluateCocoaPodsInstallation)
.thenAnswer((_) async => CocoaPodsStatus.notInstalled);
final CocoaPodsTestTarget workflow = CocoaPodsTestTarget();
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.missing);
}, overrides: <Type, Generator>{
CocoaPods: () => cocoaPods,
});
testUsingContext('Succeeds when all checks pass', () async {
when(xcode.isInstalled).thenReturn(true);
when(xcode.versionText)
.thenReturn('Xcode 8.2.1\nBuild version 8C1002\n');
when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(xcode.eulaSigned).thenReturn(true);
when(xcode.isSimctlInstalled).thenReturn(true);
testUsingContext('Emits partial status when CocoaPods is not initialized', () async {
when(cocoaPods.isCocoaPodsInitialized).thenAnswer((_) async => false);
final CocoaPodsTestTarget workflow = CocoaPodsTestTarget();
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
CocoaPods: () => cocoaPods,
});
ensureDirectoryExists(fs.path.join(homeDirPath, '.cocoapods', 'repos', 'master', 'README.md'));
testUsingContext('Emits partial status when CocoaPods version is too low', () async {
when(cocoaPods.evaluateCocoaPodsInstallation)
.thenAnswer((_) async => CocoaPodsStatus.belowRecommendedVersion);
final CocoaPodsTestTarget workflow = CocoaPodsTestTarget();
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.partial);
}, overrides: <Type, Generator>{
CocoaPods: () => cocoaPods,
});
final ValidationResult result = await IOSWorkflowTestTarget().validate();
expect(result.type, ValidationType.installed);
testUsingContext('Emits missing status when homebrew is not installed', () async {
final CocoaPodsTestTarget workflow = CocoaPodsTestTarget(hasHomebrew: false);
final ValidationResult result = await workflow.validate();
expect(result.type, ValidationType.missing);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
IMobileDevice: () => iMobileDevice,
Xcode: () => xcode,
CocoaPods: () => cocoaPods,
ProcessManager: () => processManager,
});
});
}
......@@ -330,3 +332,12 @@ class IOSWorkflowTestTarget extends IOSValidator {
@override
final Future<bool> hasIDeviceInstaller;
}
class CocoaPodsTestTarget extends CocoaPodsValidator {
CocoaPodsTestTarget({
this.hasHomebrew = true
});
@override
final bool hasHomebrew;
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment