// Copyright 2017 The Chromium 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:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/doctor.dart'; import 'package:flutter_tools/src/ios/ios_workflow.dart'; import 'package:flutter_tools/src/ios/mac.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; void main() { group('iOS Workflow validation', () { MockIMobileDevice iMobileDevice; MockIMobileDevice iMobileDeviceUninstalled; MockProcessManager processManager; FileSystem fs; setUp(() { iMobileDevice = MockIMobileDevice(); iMobileDeviceUninstalled = MockIMobileDevice(isInstalled: false); processManager = MockProcessManager(); fs = MemoryFileSystem(); }); testUsingContext('Emit missing status when nothing is installed', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget( hasHomebrew: false, hasIosDeploy: false, hasIDeviceInstaller: false, iosDeployVersionText: '0.0.0', ); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.missing); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDeviceUninstalled, }); testUsingContext('Emits installed status when homebrew not installed, but not needed', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(hasHomebrew: false); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.installed); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, }); testUsingContext('Emits partial status when libimobiledevice is not installed', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => MockIMobileDevice(isInstalled: false, isWorking: false), }); testUsingContext('Emits partial status when libimobiledevice is installed but not working', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => MockIMobileDevice(isWorking: false), }); testUsingContext('Emits partial status when libimobiledevice is installed but not working', () async { when(processManager.run( <String>['ideviceinfo', '-u', '00008020-001C2D903C42002E'], workingDirectory: anyNamed('workingDirectory'), environment: anyNamed('environment')), ).thenAnswer((Invocation _) async { final MockProcessResult result = MockProcessResult(); when<String>(result.stdout).thenReturn(r''' Usage: ideviceinfo [OPTIONS] Show information about a connected device. -d, --debug enable communication debugging -s, --simple use a simple connection to avoid auto-pairing with the device -u, --udid UDID target specific device by its 40-digit device UDID -q, --domain NAME set domain of query to NAME. Default: None -k, --key NAME only query key specified by NAME. Default: All keys. -x, --xml output information as xml plist instead of key/value pairs -h, --help prints usage information '''); return null; }); final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ ProcessManager: () => processManager, }); testUsingContext('Emits partial status when ios-deploy is not installed', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(hasIosDeploy: false); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, }); testUsingContext('Emits partial status when ios-deploy version is too low', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(iosDeployVersionText: '1.8.0'); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, }); testUsingContext('Emits partial status when ios-deploy version is a known bad version', () async { final IOSWorkflowTestTarget workflow = IOSWorkflowTestTarget(iosDeployVersionText: '2.0.0'); final ValidationResult result = await workflow.validate(); expect(result.type, ValidationType.partial); }, overrides: <Type, Generator>{ IMobileDevice: () => iMobileDevice, }); testUsingContext('Succeeds when all checks pass', () async { final ValidationResult result = await IOSWorkflowTestTarget().validate(); expect(result.type, ValidationType.installed); }, overrides: <Type, Generator>{ FileSystem: () => fs, IMobileDevice: () => iMobileDevice, ProcessManager: () => processManager, }); }); } final ProcessResult exitsHappy = ProcessResult( 1, // pid 0, // exitCode '', // stdout '', // stderr ); class MockIMobileDevice extends IMobileDevice { MockIMobileDevice({ this.isInstalled = true, bool isWorking = true, }) : isWorking = Future<bool>.value(isWorking); @override final bool isInstalled; @override final Future<bool> isWorking; } class MockProcessManager extends Mock implements ProcessManager {} class MockProcessResult extends Mock implements ProcessResult {} class IOSWorkflowTestTarget extends IOSValidator { IOSWorkflowTestTarget({ this.hasHomebrew = true, bool hasIosDeploy = true, String iosDeployVersionText = '1.9.4', bool hasIDeviceInstaller = true, }) : hasIosDeploy = Future<bool>.value(hasIosDeploy), iosDeployVersionText = Future<String>.value(iosDeployVersionText), hasIDeviceInstaller = Future<bool>.value(hasIDeviceInstaller); @override final bool hasHomebrew; @override final Future<bool> hasIosDeploy; @override final Future<String> iosDeployVersionText; @override final Future<bool> hasIDeviceInstaller; }