Commit a6a3f212 authored by Ian Fischer's avatar Ian Fischer

IOSSimulator implementation.

Also fixes some minor bugs with iOS and Android interactions.
parent 4191ed49
......@@ -74,6 +74,7 @@ class ApplicationPackageFactory {
static List<BuildPlatform> defaultBuildPlatforms = [
BuildPlatform.android,
BuildPlatform.iOS,
BuildPlatform.iOSSimulator,
];
static Map<BuildPlatform, ApplicationPackage> getAvailableApplicationPackages(
......@@ -95,6 +96,9 @@ class ApplicationPackageFactory {
case BuildPlatform.iOS:
packages[platform] = new IOSApp(buildPath);
break;
case BuildPlatform.iOSSimulator:
packages[platform] = new IOSApp(buildPath);
break;
default:
// TODO(iansf): Add other platforms
assert(false);
......
......@@ -15,27 +15,38 @@ class InstallCommand extends Command {
final name = 'install';
final description = 'Install your Flutter app on attached devices.';
AndroidDevice android = null;
AndroidDevice android;
IOSDevice ios;
IOSSimulator iosSim;
InstallCommand({this.android, this.ios});
InstallCommand({this.android, this.ios, this.iosSim}) {
argParser.addFlag('boot',
help: 'Boot the iOS Simulator if it isn\'t already running.');
}
@override
Future<int> run() async {
if (install()) {
if (install(argResults['boot'])) {
return 0;
} else {
return 2;
}
}
bool install() {
bool install([bool boot = false]) {
if (android == null) {
android = new AndroidDevice();
}
if (ios == null) {
ios = new IOSDevice();
}
if (iosSim == null) {
iosSim = new IOSSimulator();
}
if (boot) {
iosSim.boot();
}
bool installedSomewhere = false;
......@@ -43,6 +54,7 @@ class InstallCommand extends Command {
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
ApplicationPackage iosApp = packages[BuildPlatform.iOS];
ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator];
if (androidApp != null && android.isConnected()) {
installedSomewhere = android.installApp(androidApp) || installedSomewhere;
......@@ -52,6 +64,10 @@ class InstallCommand extends Command {
installedSomewhere = ios.installApp(iosApp) || installedSomewhere;
}
if (iosSimApp != null && iosSim.isConnected()) {
installedSomewhere = iosSim.installApp(iosSimApp) || installedSomewhere;
}
return installedSomewhere;
}
}
......@@ -18,8 +18,9 @@ class ListCommand extends Command {
final description = 'List all connected devices.';
AndroidDevice android;
IOSDevice ios;
IOSSimulator iosSim;
ListCommand({this.android, this.ios}) {
ListCommand({this.android, this.ios, this.iosSim}) {
argParser.addFlag('details',
abbr: 'd',
negatable: false,
......@@ -54,6 +55,17 @@ class ListCommand extends Command {
}
}
if (details) {
print('iOS Simulators:');
}
for (IOSSimulator device in IOSSimulator.getAttachedDevices(iosSim)) {
if (details) {
print('${device.id}\t${device.name}');
} else {
print(device.id);
}
}
return 0;
}
}
......@@ -22,22 +22,25 @@ class ListenCommand extends Command {
'on all connected devices.';
AndroidDevice android;
IOSDevice ios;
IOSSimulator iosSim;
List<String> watchCommand;
/// Only run once. Used for testing.
bool singleRun;
ListenCommand({this.android, this.ios, this.singleRun: false}) {}
ListenCommand({this.android, this.ios, this.iosSim, this.singleRun: false}) {}
@override
Future<int> run() async {
if (android == null) {
android = new AndroidDevice();
}
if (ios == null) {
ios = new IOSDevice();
}
if (iosSim == null) {
iosSim = new IOSSimulator();
}
if (argResults.rest.length > 0) {
watchCommand = _initWatchCommand(argResults.rest);
......@@ -49,6 +52,7 @@ class ListenCommand extends Command {
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
ApplicationPackage iosApp = packages[BuildPlatform.iOS];
ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator];
while (true) {
_logging.info('Updating running Sky apps...');
......@@ -66,7 +70,7 @@ class ListenCommand extends Command {
// TODO(iansf): Don't rely on sky-src-path for the snapshotter.
'--compiler',
'${globalResults['sky-src-path']}'
'/out/ios_Debug/clang_x64/sky_snapshot'
'/out/ios_sim_Debug/clang_x64/sky_snapshot'
]);
}
} catch (e) {}
......@@ -79,6 +83,10 @@ class ListenCommand extends Command {
await ios.pushFile(iosApp, localFLXPath, remoteFLXPath);
}
if (iosSim.isConnected()) {
await iosSim.pushFile(iosSimApp, localFLXPath, remoteFLXPath);
}
if (android.isConnected()) {
await android.startServer(
argResults['target'], true, argResults['checked'], androidApp);
......
......@@ -18,8 +18,9 @@ class LogsCommand extends Command {
final description = 'Show logs for running Sky apps.';
AndroidDevice android;
IOSDevice ios;
IOSSimulator iosSim;
LogsCommand({this.android, this.ios}) {
LogsCommand({this.android, this.ios, this.iosSim}) {
argParser.addFlag('clear',
negatable: false,
help: 'Clear log history before reading from logs (Android only).');
......@@ -33,6 +34,9 @@ class LogsCommand extends Command {
if (ios == null) {
ios = new IOSDevice();
}
if (iosSim == null) {
iosSim = new IOSSimulator();
}
Future<int> androidLogProcess = null;
if (android.isConnected()) {
......@@ -44,6 +48,11 @@ class LogsCommand extends Command {
iosLogProcess = ios.logs(clear: argResults['clear']);
}
Future<int> iosSimLogProcess = null;
if (iosSim.isConnected()) {
iosSimLogProcess = iosSim.logs(clear: argResults['clear']);
}
if (androidLogProcess != null) {
await androidLogProcess;
}
......@@ -52,6 +61,10 @@ class LogsCommand extends Command {
await iosLogProcess;
}
if (iosSimLogProcess != null) {
await iosSimLogProcess;
}
return 0;
}
}
......@@ -20,10 +20,11 @@ final Logger _logging = new Logger('sky_tools.start');
class StartCommand extends Command {
final name = 'start';
final description = 'Start your Flutter app on attached devices.';
AndroidDevice android = null;
IOSDevice ios = null;
AndroidDevice android;
IOSDevice ios;
IOSSimulator iosSim;
StartCommand({this.android, this.ios}) {
StartCommand({this.android, this.ios, this.iosSim}) {
argParser.addFlag('poke',
negatable: false,
help: 'Restart the connection to the server (Android only).');
......@@ -35,6 +36,8 @@ class StartCommand extends Command {
defaultsTo: '.',
abbr: 't',
help: 'Target app path or filename to start.');
argParser.addFlag('boot',
help: 'Boot the iOS Simulator if it isn\'t already running.');
}
@override
......@@ -45,6 +48,9 @@ class StartCommand extends Command {
if (ios == null) {
ios = new IOSDevice();
}
if (iosSim == null) {
iosSim = new IOSSimulator();
}
bool startedSomewhere = false;
bool poke = argResults['poke'];
......@@ -53,28 +59,32 @@ class StartCommand extends Command {
stopper.stop();
// Only install if the user did not specify a poke
InstallCommand installer = new InstallCommand(android: android, ios: ios);
installer.install();
InstallCommand installer =
new InstallCommand(android: android, ios: ios, iosSim: iosSim);
installer.install(argResults['boot']);
}
Map<BuildPlatform, ApplicationPackage> packages =
ApplicationPackageFactory.getAvailableApplicationPackages();
ApplicationPackage androidApp = packages[BuildPlatform.android];
ApplicationPackage iosApp = packages[BuildPlatform.iOS];
ApplicationPackage iosSimApp = packages[BuildPlatform.iOSSimulator];
bool startedOnAndroid = false;
if (android.isConnected()) {
ApplicationPackage androidApp = packages[BuildPlatform.android];
if (androidApp != null && android.isConnected()) {
String target = path.absolute(argResults['target']);
startedOnAndroid = await android.startServer(
target, poke, argResults['checked'], androidApp);
}
if (ios.isConnected()) {
ApplicationPackage iosApp = packages[BuildPlatform.iOS];
if (iosApp != null && ios.isConnected()) {
startedSomewhere = await ios.startApp(iosApp) || startedSomewhere;
}
if (iosSimApp != null && iosSim.isConnected()) {
startedSomewhere = await iosSim.startApp(iosSimApp) || startedSomewhere;
}
if (startedSomewhere || startedOnAndroid) {
return 0;
} else {
......
......@@ -17,10 +17,11 @@ final Logger _logging = new Logger('sky_tools.stop');
class StopCommand extends Command {
final name = 'stop';
final description = 'Stop your Flutter app on all attached devices.';
AndroidDevice android = null;
IOSDevice ios = null;
AndroidDevice android;
IOSDevice ios;
IOSSimulator iosSim;
StopCommand({this.android, this.ios});
StopCommand({this.android, this.ios, this.iosSim});
@override
Future<int> run() async {
......@@ -38,6 +39,9 @@ class StopCommand extends Command {
if (ios == null) {
ios = new IOSDevice();
}
if (iosSim == null) {
iosSim = new IOSSimulator();
}
bool stoppedSomething = false;
Map<BuildPlatform, ApplicationPackage> packages =
......@@ -53,6 +57,11 @@ class StopCommand extends Command {
stoppedSomething = await ios.stopApp(iosApp) || stoppedSomething;
}
if (iosSim.isConnected()) {
ApplicationPackage iosApp = packages[BuildPlatform.iOSSimulator];
stoppedSomething = await iosSim.stopApp(iosApp) || stoppedSomething;
}
return stoppedSomething;
}
}
......@@ -21,7 +21,7 @@ class TraceCommand extends Command {
'To start a trace, wait, and then stop the trace, don\'t set any flags '
'except (optionally) duration.\n'
'Otherwise, specify either start or stop to manually control the trace.';
AndroidDevice android = null;
AndroidDevice android;
TraceCommand([this.android]) {
argParser.addFlag('start', negatable: false, help: 'Start tracing.');
......
......@@ -26,6 +26,8 @@ abstract class _Device {
id = AndroidDevice.defaultDeviceID;
} else if (className == IOSDevice.className) {
id = IOSDevice.defaultDeviceID;
} else if (className == IOSSimulator.className) {
id = IOSSimulator.defaultDeviceID;
} else {
throw 'Attempted to create a Device of unknown type $className';
}
......@@ -40,6 +42,10 @@ abstract class _Device {
final device = new IOSDevice._(id);
_deviceCache[id] = device;
return device;
} else if (className == IOSSimulator.className) {
final device = new IOSSimulator._(id);
_deviceCache[id] = device;
return device;
} else {
throw 'Attempted to create a Device of unknown type $className';
}
......@@ -128,10 +134,13 @@ class IOSDevice extends _Device {
return devices;
}
static List<String> _getAttachedDeviceIDs([IOSDevice mockIOS]) {
static Iterable<String> _getAttachedDeviceIDs([IOSDevice mockIOS]) {
String listerPath =
(mockIOS != null) ? mockIOS.listerPath : _checkForCommand('idevice_id');
return runSync([listerPath, '-l']).trim().split('\n');
return runSync([listerPath, '-l'])
.trim()
.split('\n')
.where((String s) => s != null && s.length > 0);
}
static String _getDeviceName(String deviceID, [IOSDevice mockIOS]) {
......@@ -163,17 +172,22 @@ class IOSDevice extends _Device {
@override
bool installApp(ApplicationPackage app) {
if (id == defaultDeviceID) {
runCheckedSync([installerPath, '-i', app.appPath]);
} else {
runCheckedSync([installerPath, '-u', id, '-i', app.appPath]);
try {
if (id == defaultDeviceID) {
runCheckedSync([installerPath, '-i', app.appPath]);
} else {
runCheckedSync([installerPath, '-u', id, '-i', app.appPath]);
}
return true;
} catch (e) {
return false;
}
return false;
}
@override
bool isConnected() {
List<String> ids = _getAttachedDeviceIDs();
Iterable<String> ids = _getAttachedDeviceIDs();
for (String id in ids) {
if (id == this.id || this.id == defaultDeviceID) {
return true;
......@@ -245,12 +259,234 @@ class IOSDevice extends _Device {
}
/// Note that clear is not supported on iOS at this time.
Future<int> logs({bool clear: false}) {
Future<int> logs({bool clear: false}) async {
if (!isConnected()) {
return 2;
}
return runCommandAndStreamOutput([loggerPath],
prefix: 'IOS DEV: ', filter: new RegExp(r'.*SkyShell.*'));
}
}
class IOSSimulator extends _Device {
static const String className = 'IOSSimulator';
static final String defaultDeviceID = 'default_ios_sim_id';
static const String _macInstructions =
'To work with iOS devices, please install ideviceinstaller. '
'If you use homebrew, you can install it with '
'"\$ brew install ideviceinstaller".';
static String _xcrunPath = path.join('/usr', 'bin', 'xcrun');
String _iOSSimPath;
String get iOSSimPath => _iOSSimPath;
String get xcrunPath => _xcrunPath;
String _name;
String get name => _name;
factory IOSSimulator({String id, String name, String iOSSimulatorPath}) {
IOSSimulator device = new _Device(className, id);
device._name = name;
if (iOSSimulatorPath == null) {
iOSSimulatorPath = path.join('/Applications', 'iOS Simulator.app',
'Contents', 'MacOS', 'iOS Simulator');
}
device._iOSSimPath = iOSSimulatorPath;
return device;
}
IOSSimulator._(String id) : super._(id) {}
static String _getRunningSimulatorID([IOSSimulator mockIOS]) {
String xcrunPath = mockIOS != null ? mockIOS.xcrunPath : _xcrunPath;
String output = runCheckedSync([xcrunPath, 'simctl', 'list', 'devices']);
Match match;
Iterable<Match> matches = new RegExp(r'[^\(]+\(([^\)]+)\) \(Booted\)',
multiLine: true).allMatches(output);
if (matches.length > 1) {
// More than one simulator is listed as booted, which is not allowed but
// sometimes happens erroneously. Kill them all because we don't know
// which one is actually running.
_logging.warning('Multiple running simulators were detected, '
'which is not supposed to happen.');
for (Match m in matches) {
if (m.groupCount > 0) {
_logging.warning('Killing simulator ${m.group(1)}');
runSync([xcrunPath, 'simctl', 'shutdown', m.group(1)]);
}
}
} else if (matches.length == 1) {
match = matches.first;
}
if (match != null && match.groupCount > 0) {
return match.group(1);
} else {
_logging.info('No running simulators found');
return null;
}
}
String _getSimulatorPath() {
String deviceID = id == defaultDeviceID ? _getRunningSimulatorID() : id;
String homeDirectory = path.absolute(Platform.environment['HOME']);
if (deviceID == null) {
return null;
}
return path.join(homeDirectory, 'Library', 'Developer', 'CoreSimulator',
'Devices', deviceID);
}
String _getSimulatorAppHomeDirectory(ApplicationPackage app) {
String simulatorPath = _getSimulatorPath();
if (simulatorPath == null) {
return null;
}
return path.join(simulatorPath, 'data');
}
static List<IOSSimulator> getAttachedDevices([IOSSimulator mockIOS]) {
List<IOSSimulator> devices = [];
String id = _getRunningSimulatorID(mockIOS);
if (id != null) {
// TODO(iansf): get the simulator's name
// String name = _getDeviceName(id, mockIOS);
devices.add(new IOSSimulator(id: id));
}
return devices;
}
Future<bool> boot() async {
if (!Platform.isMacOS) {
return false;
}
if (isConnected()) {
return true;
}
if (id == defaultDeviceID) {
runDetached([iOSSimPath]);
Future<bool> checkConnection([int attempts = 20]) async {
if (attempts == 0) {
_logging.info('Timed out waiting for iOS Simulator $id to boot.');
return false;
}
if (!isConnected()) {
_logging.info('Waiting for iOS Simulator $id to boot...');
return new Future.delayed(new Duration(milliseconds: 500),
() => checkConnection(attempts - 1));
}
return true;
}
return checkConnection();
} else {
try {
runCheckedSync([xcrunPath, 'simctl', 'boot', id]);
} catch (e) {
_logging.warning('Unable to boot iOS Simulator $id: ', e);
return false;
}
}
return false;
}
@override
bool installApp(ApplicationPackage app) {
if (!isConnected()) {
return false;
}
try {
if (id == defaultDeviceID) {
runCheckedSync([xcrunPath, 'simctl', 'install', 'booted', app.appPath]);
} else {
runCheckedSync([xcrunPath, 'simctl', 'install', id, app.appPath]);
}
return true;
} catch (e) {
return false;
}
}
@override
bool isConnected() {
if (!Platform.isMacOS) {
return false;
}
String simulatorID = _getRunningSimulatorID();
if (simulatorID == null) {
return false;
} else if (id == defaultDeviceID) {
return true;
} else {
return _getRunningSimulatorID() == id;
}
}
@override
bool isAppInstalled(ApplicationPackage app) {
try {
String simulatorHomeDirectory = _getSimulatorAppHomeDirectory(app);
return FileSystemEntity.isDirectorySync(simulatorHomeDirectory);
} catch (e) {
return false;
}
}
@override
Future<bool> startApp(ApplicationPackage app) async {
if (!isAppInstalled(app)) {
return false;
}
try {
if (id == defaultDeviceID) {
runCheckedSync(
[xcrunPath, 'simctl', 'launch', 'booted', app.appPackageID]);
} else {
runCheckedSync([xcrunPath, 'simctl', 'launch', id, app.appPackageID]);
}
return true;
} catch (e) {
return false;
}
}
@override
Future<bool> stopApp(ApplicationPackage app) async {
// Currently we don't have a way to stop an app running on iOS.
return false;
}
Future<bool> pushFile(
ApplicationPackage app, String localFile, String targetFile) async {
if (Platform.isMacOS) {
String simulatorHomeDirectory = _getSimulatorAppHomeDirectory(app);
runCheckedSync(
['cp', localFile, path.join(simulatorHomeDirectory, targetFile)]);
return true;
}
return false;
}
Future<int> logs({bool clear: false}) async {
if (!isConnected()) {
return 2;
}
String homeDirectory = path.absolute(Platform.environment['HOME']);
String simulatorDeviceID = _getRunningSimulatorID();
String logFilePath = path.join(homeDirectory, 'Library', 'Logs',
'CoreSimulator', simulatorDeviceID, 'system.log');
if (clear) {
runSync(['rm', logFilePath]);
}
return runCommandAndStreamOutput(['tail', '-f', logFilePath],
prefix: 'IOS SIM: ', filter: new RegExp(r'.*SkyShell.*'));
}
}
class AndroidDevice extends _Device {
static const String _ADB_PATH = 'adb';
static const String _observatoryPort = '8181';
......@@ -576,7 +812,11 @@ class AndroidDevice extends _Device {
runSync([adbPath, 'logcat', '-c']);
}
Future<int> logs({bool clear: false}) {
Future<int> logs({bool clear: false}) async {
if (!isConnected()) {
return 2;
}
if (clear) {
clearLogs();
}
......
......@@ -22,7 +22,7 @@ Future<int> runCommandAndStreamOutput(List<String> cmd,
proc.stdout.transform(UTF8.decoder).listen((String data) {
List<String> dataLines = data.trimRight().split('\n');
if (filter != null) {
dataLines = dataLines.where((String s) => filter.hasMatch(s));
dataLines = dataLines.where((String s) => filter.hasMatch(s)).toList();
}
if (dataLines.length > 0) {
stdout.write('$prefix${dataLines.join('\n$prefix')}\n');
......@@ -41,17 +41,21 @@ Future<int> runCommandAndStreamOutput(List<String> cmd,
}
Future runAndKill(List<String> cmd, Duration timeout) async {
_logging.info(cmd.join(' '));
Future<Process> proc = Process.start(
cmd[0], cmd.getRange(1, cmd.length).toList(),
mode: ProcessStartMode.DETACHED);
Future<Process> proc = runDetached(cmd);
return new Future.delayed(timeout, () async {
_logging.info('Intentionally killing ${cmd[0]}');
Process.killPid((await proc).pid);
});
}
Future<Process> runDetached(List<String> cmd) async {
_logging.info(cmd.join(' '));
Future<Process> proc = Process.start(
cmd[0], cmd.getRange(1, cmd.length).toList(),
mode: ProcessStartMode.DETACHED);
return proc;
}
/// Run cmd and return stdout.
/// Throws an error if cmd exits with a non-zero value.
String runCheckedSync(List<String> cmd) =>
......
......@@ -26,6 +26,10 @@ defineTests() {
when(ios.isConnected()).thenReturn(false);
when(ios.installApp(any)).thenReturn(false);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
when(iosSim.installApp(any)).thenReturn(false);
InstallCommand command = new InstallCommand(android: android, ios: ios);
CommandRunner runner = new CommandRunner('test_flutter', '')
......@@ -44,7 +48,12 @@ defineTests() {
when(ios.isConnected()).thenReturn(true);
when(ios.installApp(any)).thenReturn(true);
InstallCommand command = new InstallCommand(android: android, ios: ios);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
when(iosSim.installApp(any)).thenReturn(false);
InstallCommand command =
new InstallCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......
......@@ -30,7 +30,13 @@ defineTests() {
when(ios.installerPath).thenReturn('echo');
when(ios.listerPath).thenReturn('echo');
ListCommand command = new ListCommand(android: android, ios: ios);
MockIOSSimulator iosSim = new MockIOSSimulator();
// Avoid relying on xcrun being installed on the test system.
// Instead, cause the test to run the echo command.
when(iosSim.xcrunPath).thenReturn('echo');
ListCommand command =
new ListCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
runner.run(['list']).then((int code) => expect(code, equals(0)));
......
......@@ -22,8 +22,10 @@ defineTests() {
when(android.isConnected()).thenReturn(false);
MockIOSDevice ios = new MockIOSDevice();
when(ios.isConnected()).thenReturn(false);
ListenCommand command =
new ListenCommand(android: android, ios: ios, singleRun: true);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
ListenCommand command = new ListenCommand(
android: android, ios: ios, iosSim: iosSim, singleRun: true);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......
......@@ -22,8 +22,11 @@ defineTests() {
when(android.isConnected()).thenReturn(false);
MockIOSDevice ios = new MockIOSDevice();
when(ios.isConnected()).thenReturn(false);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
LogsCommand command = new LogsCommand(android: android, ios: ios);
LogsCommand command =
new LogsCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......
......@@ -16,10 +16,17 @@ class MockIOSDevice extends Mock implements IOSDevice {
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
class MockIOSSimulator extends Mock implements IOSSimulator {
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
void applicationPackageSetup() {
ApplicationPackageFactory.srcPath = './';
ApplicationPackageFactory.setBuildPath(
BuildType.prebuilt, BuildPlatform.android, './');
ApplicationPackageFactory.setBuildPath(
BuildType.prebuilt, BuildPlatform.iOS, './');
ApplicationPackageFactory.setBuildPath(
BuildType.prebuilt, BuildPlatform.iOSSimulator, './');
}
......@@ -28,9 +28,16 @@ defineTests() {
when(ios.isConnected()).thenReturn(false);
when(ios.installApp(any)).thenReturn(false);
when(ios.startApp(any)).thenReturn(false);
when(ios.startApp(any)).thenReturn(false);
when(ios.stopApp(any)).thenReturn(false);
StartCommand command = new StartCommand(android: android, ios: ios);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
when(iosSim.installApp(any)).thenReturn(false);
when(iosSim.startApp(any)).thenReturn(false);
when(iosSim.stopApp(any)).thenReturn(false);
StartCommand command =
new StartCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......@@ -52,7 +59,14 @@ defineTests() {
when(ios.startApp(any)).thenReturn(true);
when(ios.stopApp(any)).thenReturn(false);
StartCommand command = new StartCommand(android: android, ios: ios);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
when(iosSim.installApp(any)).thenReturn(false);
when(iosSim.startApp(any)).thenReturn(false);
when(iosSim.stopApp(any)).thenReturn(false);
StartCommand command =
new StartCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......
......@@ -26,7 +26,12 @@ defineTests() {
when(ios.isConnected()).thenReturn(false);
when(ios.stopApp(any)).thenReturn(false);
StopCommand command = new StopCommand(android: android, ios: ios);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
when(iosSim.stopApp(any)).thenReturn(false);
StopCommand command =
new StopCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......@@ -44,7 +49,12 @@ defineTests() {
when(ios.isConnected()).thenReturn(true);
when(ios.stopApp(any)).thenReturn(true);
StopCommand command = new StopCommand(android: android, ios: ios);
MockIOSSimulator iosSim = new MockIOSSimulator();
when(iosSim.isConnected()).thenReturn(false);
when(iosSim.stopApp(any)).thenReturn(false);
StopCommand command =
new StopCommand(android: android, ios: ios, iosSim: iosSim);
CommandRunner runner = new CommandRunner('test_flutter', '')
..addCommand(command);
......
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