Commit 96ccad53 authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Update flutter_tools test utils to prepare for record/replay tests (#8591)

1. Add matchers for the `ProcessExit` exception class
2. Add ability to control the setup of the `AppContext` we use in
   `testUsingContext()`
3. Clean up the code that figures out the location of `Cache.flutterRoot`
   such that it works with `pub run test`. It previously only worked
   when the tests were invoked with standalone `dart`

`#3` above will also help unblock #7941
parent 3cf1ea6d
......@@ -7,6 +7,7 @@ import 'package:test/test.dart';
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
......@@ -25,7 +26,7 @@ void updateFileModificationTime(String path,
fs.file(path).setLastModifiedSync(modificationTime);
}
/// Matcher for functions that throw ToolExit.
/// Matcher for functions that throw [ToolExit].
Matcher throwsToolExit([int exitCode]) {
return exitCode == null
? throwsA(isToolExit)
......@@ -34,3 +35,13 @@ Matcher throwsToolExit([int exitCode]) {
/// Matcher for [ToolExit]s.
const Matcher isToolExit = const isInstanceOf<ToolExit>();
/// Matcher for functions that throw [ProcessExit].
Matcher throwsProcessExit([dynamic exitCode]) {
return exitCode == null
? throwsA(isProcessExit)
: throwsA(allOf(isProcessExit, (ProcessExit e) => e.exitCode == exitCode));
}
/// Matcher for [ProcessExit]s.
const Matcher isProcessExit = const isInstanceOf<ProcessExit>();
......@@ -32,42 +32,47 @@ MockDoctor get testDoctor => context[Doctor];
typedef dynamic Generator();
typedef void ContextInitializer(AppContext testContext);
void _defaultInitializeContext(AppContext testContext) {
testContext.putIfAbsent(DeviceManager, () => new MockDeviceManager());
testContext.putIfAbsent(DevFSConfig, () => new DevFSConfig());
testContext.putIfAbsent(Doctor, () => new MockDoctor());
testContext.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
testContext.putIfAbsent(Cache, () => new Cache());
testContext.putIfAbsent(Artifacts, () => new CachedArtifacts());
testContext.putIfAbsent(OperatingSystemUtils, () => new MockOperatingSystemUtils());
testContext.putIfAbsent(Xcode, () => new Xcode());
testContext.putIfAbsent(IOSSimulatorUtils, () {
final MockIOSSimulatorUtils mock = new MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenReturn(<IOSSimulator>[]);
return mock;
});
testContext.putIfAbsent(SimControl, () => new MockSimControl());
testContext.putIfAbsent(Usage, () => new MockUsage());
}
void testUsingContext(String description, dynamic testMethod(), {
Timeout timeout,
Map<Type, Generator> overrides: const <Type, Generator>{},
ContextInitializer initializeContext: _defaultInitializeContext,
bool skip, // should default to `false`, but https://github.com/dart-lang/test/issues/545 doesn't allow this
}) {
test(description, () async {
final AppContext testContext = new AppContext();
// Initialize the test context with some default mocks.
// Seed these context entries first since others depend on them
// The context always starts with these value since others depend on them.
testContext.putIfAbsent(Platform, () => const LocalPlatform());
testContext.putIfAbsent(FileSystem, () => const LocalFileSystem());
testContext.putIfAbsent(ProcessManager, () => const LocalProcessManager());
testContext.putIfAbsent(Logger, () => new BufferLogger());
testContext.putIfAbsent(Config, () => new Config());
// Order-independent context entries
testContext.putIfAbsent(DeviceManager, () => new MockDeviceManager());
testContext.putIfAbsent(DevFSConfig, () => new DevFSConfig());
testContext.putIfAbsent(Doctor, () => new MockDoctor());
testContext.putIfAbsent(HotRunnerConfig, () => new HotRunnerConfig());
testContext.putIfAbsent(Cache, () => new Cache());
testContext.putIfAbsent(Artifacts, () => new CachedArtifacts());
testContext.putIfAbsent(OperatingSystemUtils, () => new MockOperatingSystemUtils());
testContext.putIfAbsent(Xcode, () => new Xcode());
testContext.putIfAbsent(IOSSimulatorUtils, () {
final MockIOSSimulatorUtils mock = new MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenReturn(<IOSSimulator>[]);
return mock;
});
testContext.putIfAbsent(SimControl, () => new MockSimControl());
testContext.putIfAbsent(Usage, () => new MockUsage());
final String basePath = fs.path.dirname(fs.path.fromUri(platform.script));
final String flutterRoot =
fs.path.normalize(fs.path.join(basePath, '..', '..', '..'));
// Apply the initializer after seeding the base value above.
initializeContext(testContext);
final String flutterRoot = getFlutterRoot();
try {
return await testContext.runInZone(() {
// Apply the overrides to the test context in the zone since their
......@@ -95,6 +100,32 @@ void testUsingContext(String description, dynamic testMethod(), {
}, timeout: timeout, skip: skip);
}
String getFlutterRoot() {
Error invalidScript() => new StateError('Invalid script: ${platform.script}');
String toolsPath;
switch (platform.script.scheme) {
case 'file':
final List<String> parts = fs.path.split(fs.path.fromUri(platform.script));
final int toolsIndex = parts.indexOf('flutter_tools');
if (toolsIndex == -1)
throw invalidScript();
toolsPath = fs.path.joinAll(parts.sublist(0, toolsIndex + 1));
break;
case 'data':
final RegExp flutterTools = new RegExp(r'(file://[^%]*[/\\]flutter_tools)');
final Match match = flutterTools.firstMatch(platform.script.path);
if (match == null)
throw invalidScript();
toolsPath = Uri.parse(match.group(1)).path;
break;
default:
throw invalidScript();
}
return fs.path.normalize(fs.path.join(toolsPath, '..', '..'));
}
class MockDeviceManager implements DeviceManager {
List<Device> devices = <Device>[];
......
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