2018-04-18 10:37:59 +00:00
|
|
|
// Copyright 2018 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';
|
2018-06-28 07:07:40 +00:00
|
|
|
import 'dart:convert';
|
2018-04-18 10:37:59 +00:00
|
|
|
|
2018-06-28 07:07:40 +00:00
|
|
|
import 'package:collection/collection.dart' show ListEquality;
|
|
|
|
import 'package:flutter_tools/src/android/android_sdk.dart';
|
|
|
|
import 'package:flutter_tools/src/base/config.dart';
|
|
|
|
import 'package:flutter_tools/src/base/io.dart';
|
2018-04-18 10:37:59 +00:00
|
|
|
import 'package:flutter_tools/src/emulator.dart';
|
2018-07-19 09:32:44 +00:00
|
|
|
import 'package:flutter_tools/src/ios/ios_emulators.dart';
|
|
|
|
import 'package:flutter_tools/src/ios/mac.dart';
|
2018-06-28 07:07:40 +00:00
|
|
|
import 'package:mockito/mockito.dart';
|
|
|
|
import 'package:process/process.dart';
|
2018-04-18 10:37:59 +00:00
|
|
|
|
2018-08-15 03:33:58 +00:00
|
|
|
import 'src/common.dart';
|
2018-04-18 10:37:59 +00:00
|
|
|
import 'src/context.dart';
|
2018-06-28 07:07:40 +00:00
|
|
|
import 'src/mocks.dart';
|
2018-04-18 10:37:59 +00:00
|
|
|
|
|
|
|
void main() {
|
2018-06-28 07:07:40 +00:00
|
|
|
MockProcessManager mockProcessManager;
|
|
|
|
MockConfig mockConfig;
|
|
|
|
MockAndroidSdk mockSdk;
|
2018-07-19 09:32:44 +00:00
|
|
|
MockXcode mockXcode;
|
2018-06-28 07:07:40 +00:00
|
|
|
|
|
|
|
setUp(() {
|
2018-09-12 06:29:29 +00:00
|
|
|
mockProcessManager = MockProcessManager();
|
|
|
|
mockConfig = MockConfig();
|
|
|
|
mockSdk = MockAndroidSdk();
|
|
|
|
mockXcode = MockXcode();
|
2018-06-28 07:07:40 +00:00
|
|
|
|
|
|
|
when(mockSdk.avdManagerPath).thenReturn('avdmanager');
|
|
|
|
when(mockSdk.emulatorPath).thenReturn('emulator');
|
|
|
|
});
|
|
|
|
|
2018-04-18 10:37:59 +00:00
|
|
|
group('EmulatorManager', () {
|
|
|
|
testUsingContext('getEmulators', () async {
|
|
|
|
// Test that EmulatorManager.getEmulators() doesn't throw.
|
2018-06-28 07:07:40 +00:00
|
|
|
final List<Emulator> emulators =
|
|
|
|
await emulatorManager.getAllAvailableEmulators();
|
2018-04-18 10:37:59 +00:00
|
|
|
expect(emulators, isList);
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('getEmulatorsById', () async {
|
2018-06-28 07:07:40 +00:00
|
|
|
final _MockEmulator emulator1 =
|
2018-09-12 06:29:29 +00:00
|
|
|
_MockEmulator('Nexus_5', 'Nexus 5', 'Google', '');
|
2018-06-28 07:07:40 +00:00
|
|
|
final _MockEmulator emulator2 =
|
2018-09-12 06:29:29 +00:00
|
|
|
_MockEmulator('Nexus_5X_API_27_x86', 'Nexus 5X', 'Google', '');
|
2018-06-28 07:07:40 +00:00
|
|
|
final _MockEmulator emulator3 =
|
2018-09-12 06:29:29 +00:00
|
|
|
_MockEmulator('iOS Simulator', 'iOS Simulator', 'Apple', '');
|
2018-06-28 07:07:40 +00:00
|
|
|
final List<Emulator> emulators = <Emulator>[
|
|
|
|
emulator1,
|
|
|
|
emulator2,
|
|
|
|
emulator3
|
|
|
|
];
|
|
|
|
final TestEmulatorManager testEmulatorManager =
|
2018-09-12 06:29:29 +00:00
|
|
|
TestEmulatorManager(emulators);
|
2018-04-18 10:37:59 +00:00
|
|
|
|
2018-10-05 05:54:56 +00:00
|
|
|
Future<void> expectEmulator(String id, List<Emulator> expected) async {
|
2018-06-28 07:07:40 +00:00
|
|
|
expect(await testEmulatorManager.getEmulatorsMatching(id), expected);
|
2018-04-18 10:37:59 +00:00
|
|
|
}
|
2018-06-28 07:07:40 +00:00
|
|
|
|
2018-08-31 03:57:44 +00:00
|
|
|
await expectEmulator('Nexus_5', <Emulator>[emulator1]);
|
|
|
|
await expectEmulator('Nexus_5X', <Emulator>[emulator2]);
|
|
|
|
await expectEmulator('Nexus_5X_API_27_x86', <Emulator>[emulator2]);
|
|
|
|
await expectEmulator('Nexus', <Emulator>[emulator1, emulator2]);
|
|
|
|
await expectEmulator('iOS Simulator', <Emulator>[emulator3]);
|
|
|
|
await expectEmulator('ios', <Emulator>[emulator3]);
|
2018-04-18 10:37:59 +00:00
|
|
|
});
|
2018-06-28 07:07:40 +00:00
|
|
|
|
|
|
|
testUsingContext('create emulator with an empty name does not fail',
|
|
|
|
() async {
|
|
|
|
final CreateEmulatorResult res = await emulatorManager.createEmulator();
|
|
|
|
expect(res.success, equals(true));
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
|
|
|
Config: () => mockConfig,
|
|
|
|
AndroidSdk: () => mockSdk,
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('create emulator with a unique name does not throw',
|
|
|
|
() async {
|
|
|
|
final CreateEmulatorResult res =
|
|
|
|
await emulatorManager.createEmulator(name: 'test');
|
|
|
|
expect(res.success, equals(true));
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
|
|
|
Config: () => mockConfig,
|
|
|
|
AndroidSdk: () => mockSdk,
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext('create emulator with an existing name errors', () async {
|
|
|
|
final CreateEmulatorResult res =
|
|
|
|
await emulatorManager.createEmulator(name: 'existing-avd-1');
|
|
|
|
expect(res.success, equals(false));
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
|
|
|
Config: () => mockConfig,
|
|
|
|
AndroidSdk: () => mockSdk,
|
|
|
|
});
|
|
|
|
|
|
|
|
testUsingContext(
|
|
|
|
'create emulator without a name but when default exists adds a suffix',
|
|
|
|
() async {
|
|
|
|
// First will get default name.
|
|
|
|
CreateEmulatorResult res = await emulatorManager.createEmulator();
|
|
|
|
expect(res.success, equals(true));
|
|
|
|
|
|
|
|
final String defaultName = res.emulatorName;
|
|
|
|
|
|
|
|
// Second...
|
|
|
|
res = await emulatorManager.createEmulator();
|
|
|
|
expect(res.success, equals(true));
|
|
|
|
expect(res.emulatorName, equals('${defaultName}_2'));
|
|
|
|
|
|
|
|
// Third...
|
|
|
|
res = await emulatorManager.createEmulator();
|
|
|
|
expect(res.success, equals(true));
|
|
|
|
expect(res.emulatorName, equals('${defaultName}_3'));
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
|
|
|
Config: () => mockConfig,
|
|
|
|
AndroidSdk: () => mockSdk,
|
|
|
|
});
|
2018-04-18 10:37:59 +00:00
|
|
|
});
|
2018-07-19 09:32:44 +00:00
|
|
|
|
|
|
|
group('ios_emulators', () {
|
|
|
|
bool didAttemptToRunSimulator = false;
|
|
|
|
setUp(() {
|
|
|
|
when(mockXcode.xcodeSelectPath).thenReturn('/fake/Xcode.app/Contents/Developer');
|
|
|
|
when(mockXcode.getSimulatorPath()).thenAnswer((_) => '/fake/simulator.app');
|
|
|
|
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
|
|
|
|
final List<String> args = invocation.positionalArguments[0];
|
|
|
|
if (args.length >= 3 && args[0] == 'open' && args[1] == '-a' && args[2] == '/fake/simulator.app') {
|
|
|
|
didAttemptToRunSimulator = true;
|
|
|
|
}
|
2018-09-12 06:29:29 +00:00
|
|
|
return ProcessResult(101, 0, '', '');
|
2018-07-19 09:32:44 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
testUsingContext('runs correct launch commands', () async {
|
2018-09-12 06:29:29 +00:00
|
|
|
final Emulator emulator = IOSEmulator('ios');
|
2018-07-19 09:32:44 +00:00
|
|
|
await emulator.launch();
|
|
|
|
expect(didAttemptToRunSimulator, equals(true));
|
|
|
|
}, overrides: <Type, Generator>{
|
|
|
|
ProcessManager: () => mockProcessManager,
|
|
|
|
Config: () => mockConfig,
|
|
|
|
Xcode: () => mockXcode,
|
|
|
|
});
|
|
|
|
});
|
2018-04-18 10:37:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
class TestEmulatorManager extends EmulatorManager {
|
|
|
|
TestEmulatorManager(this.allEmulators);
|
|
|
|
|
2018-10-04 05:28:07 +00:00
|
|
|
final List<Emulator> allEmulators;
|
|
|
|
|
2018-04-18 10:37:59 +00:00
|
|
|
@override
|
2018-05-03 16:12:31 +00:00
|
|
|
Future<List<Emulator>> getAllAvailableEmulators() {
|
2018-09-12 06:29:29 +00:00
|
|
|
return Future<List<Emulator>>.value(allEmulators);
|
2018-04-18 10:37:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
class _MockEmulator extends Emulator {
|
2018-06-28 07:07:40 +00:00
|
|
|
_MockEmulator(String id, this.name, this.manufacturer, this.label)
|
|
|
|
: super(id, true);
|
2018-04-18 10:37:59 +00:00
|
|
|
|
|
|
|
@override
|
2018-04-18 12:43:22 +00:00
|
|
|
final String name;
|
2018-06-28 07:07:40 +00:00
|
|
|
|
2018-04-18 12:43:22 +00:00
|
|
|
@override
|
|
|
|
final String manufacturer;
|
2018-06-28 07:07:40 +00:00
|
|
|
|
2018-04-18 12:43:22 +00:00
|
|
|
@override
|
|
|
|
final String label;
|
|
|
|
|
2018-04-18 13:50:16 +00:00
|
|
|
@override
|
2018-06-14 06:59:50 +00:00
|
|
|
Future<void> launch() {
|
2018-09-12 06:29:29 +00:00
|
|
|
throw UnimplementedError('Not implemented in Mock');
|
2018-04-18 13:50:16 +00:00
|
|
|
}
|
2018-04-18 10:37:59 +00:00
|
|
|
}
|
2018-06-28 07:07:40 +00:00
|
|
|
|
|
|
|
class MockConfig extends Mock implements Config {}
|
|
|
|
|
|
|
|
class MockProcessManager extends Mock implements ProcessManager {
|
|
|
|
/// We have to send a command that fails in order to get the list of valid
|
|
|
|
/// system images paths. This is an example of the output to use in the mock.
|
|
|
|
static const String mockCreateFailureOutput =
|
|
|
|
'Error: Package path (-k) not specified. Valid system image paths are:\n'
|
|
|
|
'system-images;android-27;google_apis;x86\n'
|
|
|
|
'system-images;android-P;google_apis;x86\n'
|
|
|
|
'system-images;android-27;google_apis_playstore;x86\n'
|
|
|
|
'null\n'; // Yep, these really end with null (on dantup's machine at least)
|
|
|
|
|
2018-08-02 10:02:32 +00:00
|
|
|
static const ListEquality<String> _equality = ListEquality<String>();
|
2018-06-28 07:07:40 +00:00
|
|
|
final List<String> _existingAvds = <String>['existing-avd-1'];
|
|
|
|
|
|
|
|
@override
|
|
|
|
ProcessResult runSync(
|
|
|
|
List<dynamic> command, {
|
|
|
|
String workingDirectory,
|
|
|
|
Map<String, String> environment,
|
|
|
|
bool includeParentEnvironment = true,
|
|
|
|
bool runInShell = false,
|
|
|
|
Encoding stdoutEncoding,
|
|
|
|
Encoding stderrEncoding
|
|
|
|
}) {
|
|
|
|
final String program = command[0];
|
|
|
|
final List<String> args = command.sublist(1);
|
|
|
|
switch (command[0]) {
|
|
|
|
case '/usr/bin/xcode-select':
|
2018-09-12 06:29:29 +00:00
|
|
|
throw ProcessException(program, args);
|
2018-06-28 07:07:40 +00:00
|
|
|
break;
|
|
|
|
case 'emulator':
|
|
|
|
return _handleEmulator(args);
|
|
|
|
case 'avdmanager':
|
|
|
|
return _handleAvdManager(args);
|
|
|
|
}
|
2018-09-12 06:29:29 +00:00
|
|
|
throw StateError('Unexpected process call: $command');
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ProcessResult _handleEmulator(List<String> args) {
|
|
|
|
if (_equality.equals(args, <String>['-list-avds'])) {
|
2018-09-12 06:29:29 +00:00
|
|
|
return ProcessResult(101, 0, '${_existingAvds.join('\n')}\n', '');
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
2018-09-12 06:29:29 +00:00
|
|
|
throw ProcessException('emulator', args);
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ProcessResult _handleAvdManager(List<String> args) {
|
|
|
|
if (_equality.equals(args, <String>['list', 'device', '-c'])) {
|
2018-09-12 06:29:29 +00:00
|
|
|
return ProcessResult(101, 0, 'test\ntest2\npixel\npixel-xl\n', '');
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
|
|
|
if (_equality.equals(args, <String>['create', 'avd', '-n', 'temp'])) {
|
2018-09-12 06:29:29 +00:00
|
|
|
return ProcessResult(101, 1, '', mockCreateFailureOutput);
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
|
|
|
if (args.length == 8 &&
|
|
|
|
_equality.equals(args,
|
|
|
|
<String>['create', 'avd', '-n', args[3], '-k', args[5], '-d', args[7]])) {
|
|
|
|
// In order to support testing auto generation of names we need to support
|
|
|
|
// tracking any created emulators and reject when they already exist so this
|
|
|
|
// mock will compare the name of the AVD being created with the fake existing
|
|
|
|
// list and either reject if it exists, or add it to the list and return success.
|
|
|
|
final String name = args[3];
|
|
|
|
// Error if this AVD already existed
|
|
|
|
if (_existingAvds.contains(name)) {
|
2018-09-12 06:29:29 +00:00
|
|
|
return ProcessResult(
|
2018-06-28 07:07:40 +00:00
|
|
|
101,
|
|
|
|
1,
|
|
|
|
'',
|
|
|
|
"Error: Android Virtual Device '$name' already exists.\n"
|
|
|
|
'Use --force if you want to replace it.');
|
|
|
|
} else {
|
|
|
|
_existingAvds.add(name);
|
2018-09-12 06:29:29 +00:00
|
|
|
return ProcessResult(101, 0, '', '');
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
|
|
|
}
|
2018-09-12 06:29:29 +00:00
|
|
|
throw ProcessException('emulator', args);
|
2018-06-28 07:07:40 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-19 09:32:44 +00:00
|
|
|
|
|
|
|
class MockXcode extends Mock implements Xcode {}
|