Initial sketch of tools testbed (#31765)
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index 70049bc..8c318bc 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -4,7 +4,7 @@
import 'dart:async';
import 'dart:convert';
-import 'dart:io' as io show IOSink;
+import 'dart:io' as io show IOSink, ProcessSignal;
import 'package:flutter_tools/src/android/android_device.dart';
import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk;
@@ -196,6 +196,38 @@
final Stream<List<int>> stderr;
}
+/// A fake process implemenation which can be provided all necessary values.
+class FakeProcess implements Process {
+ FakeProcess({
+ this.pid = 1,
+ Future<int> exitCode,
+ Stream<List<int>> stdin,
+ this.stdout = const Stream<List<int>>.empty(),
+ this.stderr = const Stream<List<int>>.empty(),
+ }) : exitCode = exitCode ?? Future<int>.value(0),
+ stdin = stdin ?? MemoryIOSink();
+
+ @override
+ final int pid;
+
+ @override
+ final Future<int> exitCode;
+
+ @override
+ final io.IOSink stdin;
+
+ @override
+ final Stream<List<int>> stdout;
+
+ @override
+ final Stream<List<int>> stderr;
+
+ @override
+ bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) {
+ return true;
+ }
+}
+
/// A process that prompts the user to proceed, then asynchronously writes
/// some lines to stdout before it exits.
class PromptingProcess implements Process {
diff --git a/packages/flutter_tools/test/src/testbed.dart b/packages/flutter_tools/test/src/testbed.dart
new file mode 100644
index 0000000..9582152
--- /dev/null
+++ b/packages/flutter_tools/test/src/testbed.dart
@@ -0,0 +1,86 @@
+// Copyright 2019 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/context.dart';
+import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/cache.dart';
+import 'package:flutter_tools/src/context_runner.dart';
+
+export 'package:flutter_tools/src/base/context.dart' show Generator;
+
+final Map<Type, Generator> _testbedDefaults = <Type, Generator>{
+ FileSystem: () => MemoryFileSystem(),
+ Logger: () => BufferLogger(),
+};
+
+/// Manages interaction with the tool injection and runner system.
+///
+/// The Testbed automatically injects reasonable defaults through the context
+/// DI system such as a [BufferLogger] and a [MemoryFileSytem].
+///
+/// Example:
+///
+/// Testing that a filesystem operation works as expected
+///
+/// void main() {
+/// group('Example', () {
+/// Testbed testbed;
+///
+/// setUp(() {
+/// testbed = Testbed(setUp: () {
+/// fs.file('foo').createSync()
+/// });
+/// })
+///
+/// test('Can delete a file', () => testBed.run(() {
+/// expect(fs.file('foo').existsSync(), true);
+/// fs.file('foo').deleteSync();
+/// expect(fs.file('foo').existsSync(), false);
+/// }));
+/// });
+/// }
+///
+/// For a more detailed example, see the code in test_compiler_test.dart.
+class Testbed {
+ /// Creates a new [TestBed]
+ ///
+ /// `overrides` provides more overrides in addition to the test defaults.
+ /// `setup` may be provided to apply mocks within the tool managed zone,
+ /// including any specified overrides.
+ Testbed({Future<void> Function() setup, Map<Type, Generator> overrides})
+ : _setup = setup,
+ _overrides = overrides;
+
+
+ final Future<void> Function() _setup;
+ final Map<Type, Generator> _overrides;
+
+ /// Runs `test` within a tool zone.
+ FutureOr<T> run<T>(FutureOr<T> Function() test) {
+ final Map<Type, Generator> testOverrides = Map<Type, Generator>.from(_testbedDefaults);
+ if (_overrides != null) {
+ testOverrides.addAll(_overrides);
+ }
+ // Cache the original flutter root to restore after the test case.
+ final String originalFlutterRoot = Cache.flutterRoot;
+ return runInContext<T>(() {
+ return context.run<T>(
+ name: 'testbed',
+ overrides: testOverrides,
+ body: () async {
+ Cache.flutterRoot = '';
+ if (_setup != null) {
+ await _setup();
+ }
+ await test();
+ Cache.flutterRoot = originalFlutterRoot;
+ }
+ );
+ });
+ }
+}
\ No newline at end of file
diff --git a/packages/flutter_tools/test/test_compiler_test.dart b/packages/flutter_tools/test/test_compiler_test.dart
index 3a3550d..f2fb4b1 100644
--- a/packages/flutter_tools/test/test_compiler_test.dart
+++ b/packages/flutter_tools/test/test_compiler_test.dart
@@ -2,7 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/compile.dart';
import 'package:flutter_tools/src/project.dart';
@@ -10,20 +9,31 @@
import 'package:mockito/mockito.dart';
import 'src/common.dart';
-import 'src/context.dart';
+import 'src/testbed.dart';
void main() {
- group('TestCompiler', () {
- testUsingContext('compiles test file with no errors', () async {
- fs.file('pubspec.yaml').createSync();
- fs.file('.packages').createSync();
- fs.file('test/foo.dart').createSync(recursive: true);
- final MockResidentCompiler residentCompiler = MockResidentCompiler();
- final TestCompiler testCompiler = FakeTestCompiler(
- false,
- FlutterProject.current(),
- residentCompiler,
+ group(TestCompiler, () {
+ Testbed testbed;
+ FakeTestCompiler testCompiler;
+ MockResidentCompiler residentCompiler;
+
+ setUp(() {
+ testbed = Testbed(
+ setup: () {
+ fs.file('pubspec.yaml').createSync();
+ fs.file('.packages').createSync();
+ fs.file('test/foo.dart').createSync(recursive: true);
+ residentCompiler = MockResidentCompiler();
+ testCompiler = FakeTestCompiler(
+ false,
+ FlutterProject.current(),
+ residentCompiler,
+ );
+ },
);
+ });
+
+ test('Reports a dill file when compile is successful', () => testbed.run(() async {
when(residentCompiler.recompile(
'test/foo.dart',
<Uri>[Uri.parse('test/foo.dart')],
@@ -35,20 +45,9 @@
expect(await testCompiler.compile('test/foo.dart'), 'test/foo.dart.dill');
expect(fs.file('test/foo.dart.dill').existsSync(), true);
- }, overrides: <Type, Generator>{
- FileSystem: () => MemoryFileSystem(),
- });
+ }));
- testUsingContext('does not compile test file with errors', () async {
- fs.file('pubspec.yaml').createSync();
- fs.file('.packages').createSync();
- fs.file('test/foo.dart').createSync(recursive: true);
- final MockResidentCompiler residentCompiler = MockResidentCompiler();
- final TestCompiler testCompiler = FakeTestCompiler(
- false,
- FlutterProject.current(),
- residentCompiler,
- );
+ test('Reports null when a compile fails', () => testbed.run(() async {
when(residentCompiler.recompile(
'test/foo.dart',
<Uri>[Uri.parse('test/foo.dart')],
@@ -60,9 +59,7 @@
expect(await testCompiler.compile('test/foo.dart'), null);
expect(fs.file('test/foo.dart.dill').existsSync(), false);
- }, overrides: <Type, Generator>{
- FileSystem: () => MemoryFileSystem(),
- });
+ }));
});
}
diff --git a/packages/flutter_tools/test/web/compile_test.dart b/packages/flutter_tools/test/web/compile_test.dart
index b7a33c5..905dd83 100644
--- a/packages/flutter_tools/test/web/compile_test.dart
+++ b/packages/flutter_tools/test/web/compile_test.dart
@@ -3,51 +3,47 @@
// found in the LICENSE file.
import 'package:flutter_tools/src/artifacts.dart';
-import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/io.dart';
-import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/globals.dart';
import 'package:flutter_tools/src/web/compile.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
-import '../src/context.dart';
+import '../src/common.dart';
+import '../src/mocks.dart';
+import '../src/testbed.dart';
void main() {
- final MockProcessManager mockProcessManager = MockProcessManager();
- final MockProcess mockProcess = MockProcess();
- final BufferLogger mockLogger = BufferLogger();
+ group(WebCompiler, () {
+ MockProcessManager mockProcessManager;
+ Testbed testBed;
- testUsingContext('invokes dart2js with correct arguments', () async {
- const WebCompiler webCompiler = WebCompiler();
- final String engineDartPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
- final String dart2jsPath = artifacts.getArtifactPath(Artifact.dart2jsSnapshot);
- final String flutterWebSdkPath = artifacts.getArtifactPath(Artifact.flutterWebSdk);
- final String librariesPath = fs.path.join(flutterWebSdkPath, 'libraries.json');
+ setUp(() {
+ mockProcessManager = MockProcessManager();
+ testBed = Testbed(setup: () async {
+ final String engineDartPath = artifacts.getArtifactPath(Artifact.engineDartBinary);
+ when(mockProcessManager.start(any)).thenAnswer((Invocation invocation) async => FakeProcess());
+ when(mockProcessManager.canRun(engineDartPath)).thenReturn(true);
- when(mockProcess.stdout).thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
- when(mockProcess.stderr).thenAnswer((Invocation invocation) => const Stream<List<int>>.empty());
- when(mockProcess.exitCode).thenAnswer((Invocation invocation) async => 0);
- when(mockProcessManager.start(any)).thenAnswer((Invocation invocation) async => mockProcess);
- when(mockProcessManager.canRun(engineDartPath)).thenReturn(true);
+ }, overrides: <Type, Generator>{
+ ProcessManager: () => mockProcessManager,
+ });
+ });
- await webCompiler.compile(target: 'lib/main.dart');
+ test('invokes dart2js with correct arguments', () => testBed.run(() async {
+ await webCompiler.compile(target: 'lib/main.dart');
- final String outputPath = fs.path.join('build', 'web', 'main.dart.js');
- verify(mockProcessManager.start(<String>[
- engineDartPath,
- dart2jsPath,
- 'lib/main.dart',
- '-o',
- outputPath,
- '--libraries-spec=$librariesPath',
- '-m',
- ])).called(1);
- }, overrides: <Type, Generator>{
- ProcessManager: () => mockProcessManager,
- Logger: () => mockLogger,
+ verify(mockProcessManager.start(<String>[
+ 'bin/cache/dart-sdk/bin/dart',
+ 'bin/cache/dart-sdk/bin/snapshots/dart2js.dart.snapshot',
+ 'lib/main.dart',
+ '-o',
+ 'build/web/main.dart.js',
+ '--libraries-spec=bin/cache/flutter_web_sdk/libraries.json',
+ '-m',
+ ])).called(1);
+
+ }));
});
}
class MockProcessManager extends Mock implements ProcessManager {}
-class MockProcess extends Mock implements Process {}