Commit 677e63b7 authored by Yegor Jbanov's avatar Yegor Jbanov

decouple `flutter drive` from `flutter start`

flutter start's method of finding devices to run the app on is not suitable for flutter drive.

This commit also refactors several tool services to allow mocking in unit tests.
parent 0c05666e
...@@ -54,6 +54,8 @@ class AndroidDevice extends Device { ...@@ -54,6 +54,8 @@ class AndroidDevice extends Device {
bool _connected; bool _connected;
bool get isLocalEmulator => false;
List<String> adbCommandForDevice(List<String> args) { List<String> adbCommandForDevice(List<String> args) {
return <String>[androidSdk.adbPath, '-s', id]..addAll(args); return <String>[androidSdk.adbPath, '-s', id]..addAll(args);
} }
......
...@@ -5,7 +5,10 @@ ...@@ -5,7 +5,10 @@
import 'dart:async'; import 'dart:async';
import 'dart:io'; import 'dart:io';
final OperatingSystemUtils os = new OperatingSystemUtils._(); import 'context.dart';
/// Returns [OperatingSystemUtils] active in the current app context (i.e. zone).
OperatingSystemUtils get os => context[OperatingSystemUtils] ?? (context[OperatingSystemUtils] = new OperatingSystemUtils._());
abstract class OperatingSystemUtils { abstract class OperatingSystemUtils {
factory OperatingSystemUtils._() { factory OperatingSystemUtils._() {
...@@ -16,6 +19,14 @@ abstract class OperatingSystemUtils { ...@@ -16,6 +19,14 @@ abstract class OperatingSystemUtils {
} }
} }
OperatingSystemUtils._private();
String get operatingSystem => Platform.operatingSystem;
bool get isMacOS => operatingSystem == 'macos';
bool get isWindows => operatingSystem == 'windows';
bool get isLinux => operatingSystem == 'linux';
/// Make the given file executable. This may be a no-op on some platforms. /// Make the given file executable. This may be a no-op on some platforms.
ProcessResult makeExecutable(File file); ProcessResult makeExecutable(File file);
...@@ -24,7 +35,9 @@ abstract class OperatingSystemUtils { ...@@ -24,7 +35,9 @@ abstract class OperatingSystemUtils {
File which(String execName); File which(String execName);
} }
class _PosixUtils implements OperatingSystemUtils { class _PosixUtils extends OperatingSystemUtils {
_PosixUtils() : super._private();
ProcessResult makeExecutable(File file) { ProcessResult makeExecutable(File file) {
return Process.runSync('chmod', ['u+x', file.path]); return Process.runSync('chmod', ['u+x', file.path]);
} }
...@@ -40,7 +53,9 @@ class _PosixUtils implements OperatingSystemUtils { ...@@ -40,7 +53,9 @@ class _PosixUtils implements OperatingSystemUtils {
} }
} }
class _WindowsUtils implements OperatingSystemUtils { class _WindowsUtils extends OperatingSystemUtils {
_WindowsUtils() : super._private();
// This is a no-op. // This is a no-op.
ProcessResult makeExecutable(File file) { ProcessResult makeExecutable(File file) {
return new ProcessResult(0, 0, null, null); return new ProcessResult(0, 0, null, null);
......
...@@ -420,7 +420,7 @@ Future<int> buildAndroid({ ...@@ -420,7 +420,7 @@ Future<int> buildAndroid({
// TODO(mpcomplete): move this to Device? // TODO(mpcomplete): move this to Device?
/// This is currently Android specific. /// This is currently Android specific.
Future buildAll( Future<int> buildAll(
DeviceStore devices, DeviceStore devices,
ApplicationPackageStore applicationPackages, ApplicationPackageStore applicationPackages,
Toolchain toolchain, Toolchain toolchain,
...@@ -434,31 +434,44 @@ Future buildAll( ...@@ -434,31 +434,44 @@ Future buildAll(
continue; continue;
// TODO(mpcomplete): Temporary hack. We only support the apk builder atm. // TODO(mpcomplete): Temporary hack. We only support the apk builder atm.
if (package == applicationPackages.android) { if (package != applicationPackages.android)
// TODO(devoncarew): Remove this warning after a few releases. continue;
if (FileSystemEntity.isDirectorySync('apk') && !FileSystemEntity.isDirectorySync('android')) {
// Tell people the android directory location changed. // TODO(devoncarew): Remove this warning after a few releases.
printStatus( if (FileSystemEntity.isDirectorySync('apk') && !FileSystemEntity.isDirectorySync('android')) {
"Warning: Flutter now looks for Android resources in the android/ directory; " // Tell people the android directory location changed.
"consider renaming your 'apk/' directory to 'android/'."); printStatus(
} "Warning: Flutter now looks for Android resources in the android/ directory; "
"consider renaming your 'apk/' directory to 'android/'.");
if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) {
printStatus('Using pre-built SkyShell.apk.');
continue;
}
int result = await buildAndroid(
toolchain: toolchain,
configs: configs,
enginePath: enginePath,
force: false,
target: target
);
if (result != 0)
return result;
} }
int result = await build(toolchain, configs, enginePath: enginePath,
target: target);
if (result != 0)
return result;
} }
return 0; return 0;
} }
Future<int> build(
Toolchain toolchain,
List<BuildConfiguration> configs, {
String enginePath,
String target: ''
}) async {
if (!FileSystemEntity.isFileSync(_kDefaultAndroidManifestPath)) {
printStatus('Using pre-built SkyShell.apk.');
return 0;
}
int result = await buildAndroid(
toolchain: toolchain,
configs: configs,
enginePath: enginePath,
force: false,
target: target
);
return result;
}
...@@ -51,6 +51,11 @@ abstract class RunCommandBase extends FlutterCommand { ...@@ -51,6 +51,11 @@ abstract class RunCommandBase extends FlutterCommand {
argParser.addOption('route', argParser.addOption('route',
help: 'Which route to load when starting the app.'); help: 'Which route to load when starting the app.');
} }
bool get checked => argResults['checked'];
bool get traceStartup => argResults['trace-startup'];
String get target => argResults['target'];
String get route => argResults['route'];
} }
class RunCommand extends RunCommandBase { class RunCommand extends RunCommandBase {
...@@ -219,7 +224,7 @@ Future<int> startApp( ...@@ -219,7 +224,7 @@ Future<int> startApp(
// wait for the observatory port to become available before returning from // wait for the observatory port to become available before returning from
// `startApp()`. // `startApp()`.
if (startPaused && device.supportsStartPaused) { if (startPaused && device.supportsStartPaused) {
await _delayUntilObservatoryAvailable('localhost', debugPort); await delayUntilObservatoryAvailable('localhost', debugPort);
} }
} }
} }
...@@ -242,7 +247,7 @@ Future<int> startApp( ...@@ -242,7 +247,7 @@ Future<int> startApp(
/// ///
/// This does not fail if we're unable to connect, and times out after the given /// This does not fail if we're unable to connect, and times out after the given
/// [timeout]. /// [timeout].
Future _delayUntilObservatoryAvailable(String host, int port, { Future delayUntilObservatoryAvailable(String host, int port, {
Duration timeout: const Duration(seconds: 10) Duration timeout: const Duration(seconds: 10)
}) async { }) async {
Stopwatch stopwatch = new Stopwatch()..start(); Stopwatch stopwatch = new Stopwatch()..start();
......
...@@ -130,6 +130,9 @@ abstract class Device { ...@@ -130,6 +130,9 @@ abstract class Device {
bool get supportsStartPaused => true; bool get supportsStartPaused => true;
/// Whether it is an emulated device running on localhost.
bool get isLocalEmulator;
/// Install an app package on the current device /// Install an app package on the current device
bool installApp(ApplicationPackage app); bool installApp(ApplicationPackage app);
...@@ -259,7 +262,7 @@ class DeviceStore { ...@@ -259,7 +262,7 @@ class DeviceStore {
break; break;
case TargetPlatform.iOSSimulator: case TargetPlatform.iOSSimulator:
assert(iOSSimulator == null); assert(iOSSimulator == null);
iOSSimulator = _deviceForConfig(config, IOSSimulator.getAttachedDevices()); iOSSimulator = _deviceForConfig(config, IOSSimulatorUtils.instance.getAttachedDevices());
break; break;
case TargetPlatform.mac: case TargetPlatform.mac:
case TargetPlatform.linux: case TargetPlatform.linux:
......
...@@ -62,6 +62,8 @@ class IOSDevice extends Device { ...@@ -62,6 +62,8 @@ class IOSDevice extends Device {
final String name; final String name;
bool get isLocalEmulator => false;
bool get supportsStartPaused => false; bool get supportsStartPaused => false;
static List<IOSDevice> getAttachedDevices([IOSDevice mockIOS]) { static List<IOSDevice> getAttachedDevices([IOSDevice mockIOS]) {
......
...@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path; ...@@ -10,6 +10,7 @@ import 'package:path/path.dart' as path;
import '../application_package.dart'; import '../application_package.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../build_configuration.dart'; import '../build_configuration.dart';
import '../device.dart'; import '../device.dart';
...@@ -26,12 +27,29 @@ class IOSSimulators extends PollingDeviceDiscovery { ...@@ -26,12 +27,29 @@ class IOSSimulators extends PollingDeviceDiscovery {
IOSSimulators() : super('IOSSimulators'); IOSSimulators() : super('IOSSimulators');
bool get supportsPlatform => Platform.isMacOS; bool get supportsPlatform => Platform.isMacOS;
List<Device> pollingGetDevices() => IOSSimulator.getAttachedDevices(); List<Device> pollingGetDevices() => IOSSimulatorUtils.instance.getAttachedDevices();
}
class IOSSimulatorUtils {
/// Returns [IOSSimulatorUtils] active in the current app context (i.e. zone).
static IOSSimulatorUtils get instance => context[IOSSimulatorUtils] ?? (context[IOSSimulatorUtils] = new IOSSimulatorUtils());
List<IOSSimulator> getAttachedDevices() {
if (!xcode.isInstalledAndMeetsVersionCheck)
return <IOSSimulator>[];
return SimControl.instance.getConnectedDevices().map((SimDevice device) {
return new IOSSimulator(device.udid, name: device.name);
}).toList();
}
} }
/// A wrapper around the `simctl` command line tool. /// A wrapper around the `simctl` command line tool.
class SimControl { class SimControl {
static Future<bool> boot({String deviceId}) async { /// Returns [SimControl] active in the current app context (i.e. zone).
static SimControl get instance => context[SimControl] ?? (context[SimControl] = new SimControl());
Future<bool> boot({String deviceId}) async {
if (_isAnyConnected()) if (_isAnyConnected())
return true; return true;
...@@ -65,7 +83,7 @@ class SimControl { ...@@ -65,7 +83,7 @@ class SimControl {
} }
/// Returns a list of all available devices, both potential and connected. /// Returns a list of all available devices, both potential and connected.
static List<SimDevice> getDevices() { List<SimDevice> getDevices() {
// { // {
// "devices" : { // "devices" : {
// "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [ // "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
...@@ -102,18 +120,18 @@ class SimControl { ...@@ -102,18 +120,18 @@ class SimControl {
} }
/// Returns all the connected simulator devices. /// Returns all the connected simulator devices.
static List<SimDevice> getConnectedDevices() { List<SimDevice> getConnectedDevices() {
return getDevices().where((SimDevice device) => device.isBooted).toList(); return getDevices().where((SimDevice device) => device.isBooted).toList();
} }
static StreamController<List<SimDevice>> _trackDevicesControler; StreamController<List<SimDevice>> _trackDevicesControler;
/// Listens to changes in the set of connected devices. The implementation /// Listens to changes in the set of connected devices. The implementation
/// currently uses polling. Callers should be careful to call cancel() on any /// currently uses polling. Callers should be careful to call cancel() on any
/// stream subscription when finished. /// stream subscription when finished.
/// ///
/// TODO(devoncarew): We could investigate using the usbmuxd protocol directly. /// TODO(devoncarew): We could investigate using the usbmuxd protocol directly.
static Stream<List<SimDevice>> trackDevices() { Stream<List<SimDevice>> trackDevices() {
if (_trackDevicesControler == null) { if (_trackDevicesControler == null) {
Timer timer; Timer timer;
Set<String> deviceIds = new Set<String>(); Set<String> deviceIds = new Set<String>();
...@@ -138,7 +156,7 @@ class SimControl { ...@@ -138,7 +156,7 @@ class SimControl {
} }
/// Update the cached set of device IDs and return whether there were any changes. /// Update the cached set of device IDs and return whether there were any changes.
static bool _updateDeviceIds(List<SimDevice> devices, Set<String> deviceIds) { bool _updateDeviceIds(List<SimDevice> devices, Set<String> deviceIds) {
Set<String> newIds = new Set<String>.from(devices.map((SimDevice device) => device.udid)); Set<String> newIds = new Set<String>.from(devices.map((SimDevice device) => device.udid));
bool changed = false; bool changed = false;
...@@ -159,13 +177,13 @@ class SimControl { ...@@ -159,13 +177,13 @@ class SimControl {
return changed; return changed;
} }
static bool _isAnyConnected() => getConnectedDevices().isNotEmpty; bool _isAnyConnected() => getConnectedDevices().isNotEmpty;
static void install(String deviceId, String appPath) { void install(String deviceId, String appPath) {
runCheckedSync([_xcrunPath, 'simctl', 'install', deviceId, appPath]); runCheckedSync([_xcrunPath, 'simctl', 'install', deviceId, appPath]);
} }
static void launch(String deviceId, String appIdentifier, [List<String> launchArgs]) { void launch(String deviceId, String appIdentifier, [List<String> launchArgs]) {
List<String> args = [_xcrunPath, 'simctl', 'launch', deviceId, appIdentifier]; List<String> args = [_xcrunPath, 'simctl', 'launch', deviceId, appIdentifier];
if (launchArgs != null) if (launchArgs != null)
args.addAll(launchArgs); args.addAll(launchArgs);
...@@ -190,17 +208,10 @@ class SimDevice { ...@@ -190,17 +208,10 @@ class SimDevice {
class IOSSimulator extends Device { class IOSSimulator extends Device {
IOSSimulator(String id, { this.name }) : super(id); IOSSimulator(String id, { this.name }) : super(id);
static List<IOSSimulator> getAttachedDevices() {
if (!xcode.isInstalledAndMeetsVersionCheck)
return <IOSSimulator>[];
return SimControl.getConnectedDevices().map((SimDevice device) {
return new IOSSimulator(device.udid, name: device.name);
}).toList();
}
final String name; final String name;
bool get isLocalEmulator => true;
String get xcrunPath => path.join('/usr', 'bin', 'xcrun'); String get xcrunPath => path.join('/usr', 'bin', 'xcrun');
String _getSimulatorPath() { String _getSimulatorPath() {
...@@ -220,7 +231,7 @@ class IOSSimulator extends Device { ...@@ -220,7 +231,7 @@ class IOSSimulator extends Device {
return false; return false;
try { try {
SimControl.install(id, app.localPath); SimControl.instance.install(id, app.localPath);
return true; return true;
} catch (e) { } catch (e) {
return false; return false;
...@@ -231,7 +242,7 @@ class IOSSimulator extends Device { ...@@ -231,7 +242,7 @@ class IOSSimulator extends Device {
bool isConnected() { bool isConnected() {
if (!Platform.isMacOS) if (!Platform.isMacOS)
return false; return false;
return SimControl.getConnectedDevices().any((SimDevice device) => device.udid == id); return SimControl.instance.getConnectedDevices().any((SimDevice device) => device.udid == id);
} }
@override @override
...@@ -333,7 +344,7 @@ class IOSSimulator extends Device { ...@@ -333,7 +344,7 @@ class IOSSimulator extends Device {
} }
// Step 3: Install the updated bundle to the simulator. // Step 3: Install the updated bundle to the simulator.
SimControl.install(id, path.absolute(bundle.path)); SimControl.instance.install(id, path.absolute(bundle.path));
// Step 4: Prepare launch arguments. // Step 4: Prepare launch arguments.
List<String> args = <String>[]; List<String> args = <String>[];
...@@ -349,7 +360,7 @@ class IOSSimulator extends Device { ...@@ -349,7 +360,7 @@ class IOSSimulator extends Device {
// Step 5: Launch the updated application in the simulator. // Step 5: Launch the updated application in the simulator.
try { try {
SimControl.launch(id, app.id, args); SimControl.instance.launch(id, app.id, args);
} catch (error) { } catch (error) {
printError('$error'); printError('$error');
return false; return false;
......
...@@ -5,10 +5,15 @@ ...@@ -5,10 +5,15 @@
import 'dart:async'; import 'dart:async';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:flutter_tools/src/commands/drive.dart'; import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/commands/drive.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart'; import 'package:flutter_tools/src/globals.dart';
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'src/common.dart'; import 'src/common.dart';
...@@ -19,18 +24,45 @@ main() => defineTests(); ...@@ -19,18 +24,45 @@ main() => defineTests();
defineTests() { defineTests() {
group('drive', () { group('drive', () {
DriveCommand command;
Device mockDevice;
void withMockDevice([Device mock]) {
mockDevice = mock ?? new MockDevice();
targetDeviceFinder = () async => mockDevice;
testDeviceManager.addDevice(mockDevice);
}
setUp(() { setUp(() {
command = new DriveCommand();
applyMocksToCommand(command);
useInMemoryFileSystem(cwd: '/some/app'); useInMemoryFileSystem(cwd: '/some/app');
toolchainDownloader = (_) async { };
targetDeviceFinder = () {
throw 'Unexpected call to targetDeviceFinder';
};
appStarter = (_) {
throw 'Unexpected call to appStarter';
};
testRunner = (_) {
throw 'Unexpected call to testRunner';
};
appStopper = (_) {
throw 'Unexpected call to appStopper';
};
}); });
tearDown(() { tearDown(() {
command = null;
restoreFileSystem(); restoreFileSystem();
restoreAppStarter();
restoreAppStopper();
restoreTestRunner();
restoreTargetDeviceFinder();
}); });
testUsingContext('returns 1 when test file is not found', () { testUsingContext('returns 1 when test file is not found', () {
DriveCommand command = new DriveCommand(); withMockDevice();
applyMocksToCommand(command);
List<String> args = [ List<String> args = [
'drive', 'drive',
'--target=/some/app/test/e2e.dart', '--target=/some/app/test/e2e.dart',
...@@ -44,10 +76,8 @@ defineTests() { ...@@ -44,10 +76,8 @@ defineTests() {
}); });
testUsingContext('returns 1 when app fails to run', () async { testUsingContext('returns 1 when app fails to run', () async {
DriveCommand command = new DriveCommand.custom(runAppFn: expectAsync(() { withMockDevice();
return new Future.value(1); appStarter = expectAsync((_) async => 1);
}));
applyMocksToCommand(command);
String testApp = '/some/app/test_driver/e2e.dart'; String testApp = '/some/app/test_driver/e2e.dart';
String testFile = '/some/app/test_driver/e2e_test.dart'; String testFile = '/some/app/test_driver/e2e_test.dart';
...@@ -72,8 +102,6 @@ defineTests() { ...@@ -72,8 +102,6 @@ defineTests() {
testUsingContext('returns 1 when app file is outside package', () async { testUsingContext('returns 1 when app file is outside package', () async {
String packageDir = '/my/app'; String packageDir = '/my/app';
useInMemoryFileSystem(cwd: packageDir); useInMemoryFileSystem(cwd: packageDir);
DriveCommand command = new DriveCommand();
applyMocksToCommand(command);
String appFile = '/not/in/my/app.dart'; String appFile = '/not/in/my/app.dart';
List<String> args = [ List<String> args = [
...@@ -92,8 +120,6 @@ defineTests() { ...@@ -92,8 +120,6 @@ defineTests() {
testUsingContext('returns 1 when app file is in the root dir', () async { testUsingContext('returns 1 when app file is in the root dir', () async {
String packageDir = '/my/app'; String packageDir = '/my/app';
useInMemoryFileSystem(cwd: packageDir); useInMemoryFileSystem(cwd: packageDir);
DriveCommand command = new DriveCommand();
applyMocksToCommand(command);
String appFile = '/my/app/main.dart'; String appFile = '/my/app/main.dart';
List<String> args = [ List<String> args = [
...@@ -111,22 +137,21 @@ defineTests() { ...@@ -111,22 +137,21 @@ defineTests() {
}); });
testUsingContext('returns 0 when test ends successfully', () async { testUsingContext('returns 0 when test ends successfully', () async {
withMockDevice();
String testApp = '/some/app/test/e2e.dart'; String testApp = '/some/app/test/e2e.dart';
String testFile = '/some/app/test_driver/e2e_test.dart'; String testFile = '/some/app/test_driver/e2e_test.dart';
DriveCommand command = new DriveCommand.custom( appStarter = expectAsync((_) {
runAppFn: expectAsync(() { return new Future<int>.value(0);
return new Future<int>.value(0); });
}), testRunner = expectAsync((List<String> testArgs) {
runTestsFn: expectAsync((List<String> testArgs) { expect(testArgs, [testFile]);
expect(testArgs, [testFile]); return new Future<Null>.value();
return new Future<Null>.value(); });
}), appStopper = expectAsync((_) {
stopAppFn: expectAsync(() { return new Future<int>.value(0);
return new Future<int>.value(0); });
})
);
applyMocksToCommand(command);
MemoryFileSystem memFs = fs; MemoryFileSystem memFs = fs;
await memFs.file(testApp).writeAsString('main() {}'); await memFs.file(testApp).writeAsString('main() {}');
...@@ -142,5 +167,88 @@ defineTests() { ...@@ -142,5 +167,88 @@ defineTests() {
expect(buffer.errorText, isEmpty); expect(buffer.errorText, isEmpty);
}); });
}); });
group('findTargetDevice', () {
testUsingContext('uses specified device', () async {
testDeviceManager.specifiedDeviceId = '123';
withMockDevice();
when(mockDevice.name).thenReturn('specified-device');
when(mockDevice.id).thenReturn('123');
Device device = await findTargetDevice();
expect(device.name, 'specified-device');
});
});
group('findTargetDevice on iOS', () {
setOs() {
when(os.isMacOS).thenReturn(true);
when(os.isLinux).thenReturn(false);
}
testUsingContext('uses existing emulator', () async {
setOs();
withMockDevice();
when(mockDevice.name).thenReturn('mock-simulator');
when(mockDevice.isLocalEmulator).thenReturn(true);
Device device = await findTargetDevice();
expect(device.name, 'mock-simulator');
});
testUsingContext('uses existing Android device if and there are no simulators', () async {
setOs();
mockDevice = new MockAndroidDevice();
when(mockDevice.name).thenReturn('mock-android-device');
when(mockDevice.isLocalEmulator).thenReturn(false);
withMockDevice(mockDevice);
Device device = await findTargetDevice();
expect(device.name, 'mock-android-device');
});
testUsingContext('launches emulator', () async {
setOs();
when(SimControl.instance.boot()).thenReturn(true);
Device emulator = new MockDevice();
when(emulator.name).thenReturn('new-simulator');
when(IOSSimulatorUtils.instance.getAttachedDevices())
.thenReturn([emulator]);
Device device = await findTargetDevice();
expect(device.name, 'new-simulator');
});
});
group('findTargetDevice on Linux', () {
setOs() {
when(os.isMacOS).thenReturn(false);
when(os.isLinux).thenReturn(true);
}
testUsingContext('returns null if no devices found', () async {
setOs();
expect(await findTargetDevice(), isNull);
});
testUsingContext('uses existing Android device', () async {
setOs();
mockDevice = new MockAndroidDevice();
when(mockDevice.name).thenReturn('mock-android-device');
withMockDevice(mockDevice);
Device device = await findTargetDevice();
expect(device.name, 'mock-android-device');
});
});
}); });
} }
class MockDevice extends Mock implements Device {
MockDevice() {
when(this.isSupported()).thenReturn(true);
}
}
class MockAndroidDevice extends Mock implements AndroidDevice { }
...@@ -15,7 +15,7 @@ defineTests() { ...@@ -15,7 +15,7 @@ defineTests() {
group('listen', () { group('listen', () {
testUsingContext('returns 1 when no device is connected', () { testUsingContext('returns 1 when no device is connected', () {
ListenCommand command = new ListenCommand(singleRun: true); ListenCommand command = new ListenCommand(singleRun: true);
applyMocksToCommand(command, noDevices: true); applyMocksToCommand(command);
return createTestCommandRunner(command).run(['listen']).then((int code) { return createTestCommandRunner(command).run(['listen']).then((int code) {
expect(code, equals(1)); expect(code, equals(1));
}); });
......
...@@ -7,9 +7,13 @@ import 'dart:io'; ...@@ -7,9 +7,13 @@ import 'dart:io';
import 'package:flutter_tools/src/base/context.dart'; import 'package:flutter_tools/src/base/context.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/os.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/doctor.dart'; import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
/// Return the test logger. This assumes that the current Logger is a BufferLogger. /// Return the test logger. This assumes that the current Logger is a BufferLogger.
...@@ -38,6 +42,15 @@ void testUsingContext(String description, dynamic testMethod(), { ...@@ -38,6 +42,15 @@ void testUsingContext(String description, dynamic testMethod(), {
if (!overrides.containsKey(Doctor)) if (!overrides.containsKey(Doctor))
testContext[Doctor] = new MockDoctor(); testContext[Doctor] = new MockDoctor();
if (!overrides.containsKey(SimControl))
testContext[SimControl] = new MockSimControl();
if (!overrides.containsKey(OperatingSystemUtils))
testContext[OperatingSystemUtils] = new MockOperatingSystemUtils();
if (!overrides.containsKey(IOSSimulatorUtils))
testContext[IOSSimulatorUtils] = new MockIOSSimulatorUtils();
if (Platform.isMacOS) { if (Platform.isMacOS) {
if (!overrides.containsKey(XCode)) if (!overrides.containsKey(XCode))
testContext[XCode] = new XCode(); testContext[XCode] = new XCode();
...@@ -76,3 +89,13 @@ class MockDoctor extends Doctor { ...@@ -76,3 +89,13 @@ class MockDoctor extends Doctor {
// True for testing. // True for testing.
bool get canLaunchAnything => true; bool get canLaunchAnything => true;
} }
class MockSimControl extends Mock implements SimControl {
MockSimControl() {
when(this.getConnectedDevices()).thenReturn([]);
}
}
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
class MockIOSSimulatorUtils extends Mock implements IOSSimulatorUtils {}
...@@ -12,8 +12,6 @@ import 'package:flutter_tools/src/runner/flutter_command.dart'; ...@@ -12,8 +12,6 @@ import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/toolchain.dart'; import 'package:flutter_tools/src/toolchain.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'context.dart';
class MockApplicationPackageStore extends ApplicationPackageStore { class MockApplicationPackageStore extends ApplicationPackageStore {
MockApplicationPackageStore() : super( MockApplicationPackageStore() : super(
android: new AndroidApk(localPath: '/mock/path/to/android/SkyShell.apk'), android: new AndroidApk(localPath: '/mock/path/to/android/SkyShell.apk'),
...@@ -53,13 +51,10 @@ class MockDeviceStore extends DeviceStore { ...@@ -53,13 +51,10 @@ class MockDeviceStore extends DeviceStore {
iOSSimulator: new MockIOSSimulator()); iOSSimulator: new MockIOSSimulator());
} }
void applyMocksToCommand(FlutterCommand command, { bool noDevices: false }) { void applyMocksToCommand(FlutterCommand command) {
command command
..applicationPackages = new MockApplicationPackageStore() ..applicationPackages = new MockApplicationPackageStore()
..toolchain = new MockToolchain() ..toolchain = new MockToolchain()
..devices = new MockDeviceStore() ..devices = new MockDeviceStore()
..projectRootValidator = () => true; ..projectRootValidator = () => true;
if (!noDevices)
testDeviceManager.addDevice(command.devices.android);
} }
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