mirror of
https://github.com/flutter/flutter
synced 2024-10-13 03:32:55 +00:00
Initial sketch of tools testbed (#31765)
This commit is contained in:
parent
48936d9a95
commit
5e77d6508e
|
@ -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 @@ class MockProcess extends Mock implements Process {
|
|||
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 {
|
||||
|
|
86
packages/flutter_tools/test/src/testbed.dart
Normal file
86
packages/flutter_tools/test/src/testbed.dart
Normal file
|
@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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:flutter_tools/src/test/test_compiler.dart';
|
|||
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 @@ void main() {
|
|||
|
||||
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 @@ void main() {
|
|||
|
||||
expect(await testCompiler.compile('test/foo.dart'), null);
|
||||
expect(fs.file('test/foo.dart.dill').existsSync(), false);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem(),
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {}
|
||||
|
|
Loading…
Reference in a new issue