speed up tests by using a precompiled test runner (#1605)
Adds a new `precompileTestExecutable` method which you must call before calling `runTest`. It should be invoked in a top level `setUpAll` method, and will clean up its output in `tearDownAll`.
This should significantly speed up tests using `runTest` more than once.
diff --git a/pkgs/test/test/io.dart b/pkgs/test/test/io.dart
index 1820980..af9bfd4 100644
--- a/pkgs/test/test/io.dart
+++ b/pkgs/test/test/io.dart
@@ -67,7 +67,39 @@
StreamMatcher containsInOrder(Iterable<String> strings) =>
emitsInOrder(strings.map((string) => emitsThrough(contains(string))));
+/// Lazily compile the test package to kernel and re-use that, initialized with
+/// [precompileTestExecutable].
+String? _testExecutablePath;
+
+/// Must be invoked before any call to [runTests], should be invoked in a top
+/// level `setUpAll` for best caching results.
+Future<void> precompileTestExecutable() async {
+ if (_testExecutablePath != null) {
+ throw StateError('Test executable already precompiled');
+ }
+ var tmpDirectory = await Directory.systemTemp.createTemp('test');
+ var precompiledPath = p.join(tmpDirectory.path, 'test_runner.dill');
+ var result = await Process.run(Platform.executable, [
+ 'compile',
+ 'kernel',
+ p.url.join(await packageDir, 'bin', 'test.dart'),
+ '-o',
+ precompiledPath,
+ ]);
+ if (result.exitCode != 0) {
+ throw StateError(
+ 'Failed to compile test runner:\n${result.stdout}\n${result.stderr}');
+ }
+
+ addTearDown(() async {
+ await tmpDirectory.delete(recursive: true);
+ });
+ _testExecutablePath = precompiledPath;
+}
+
/// Runs the test executable with the package root set properly.
+///
+/// You must invoke [precompileTestExecutable] before invoking this function.
Future<TestProcess> runTest(Iterable<String> args,
{String? reporter,
String? fileReporter,
@@ -77,10 +109,15 @@
String? packageConfig,
Iterable<String>? vmArgs}) async {
concurrency ??= 1;
+ var testExecutablePath = _testExecutablePath;
+ if (testExecutablePath == null) {
+ throw StateError(
+ 'You must call `precompileTestExecutable` before calling `runTest`');
+ }
var allArgs = [
...?vmArgs,
- Uri.file(p.url.join(await packageDir, 'bin', 'test.dart')).toString(),
+ testExecutablePath,
'--concurrency=$concurrency',
if (reporter != null) '--reporter=$reporter',
if (fileReporter != null) '--file-reporter=$fileReporter',
diff --git a/pkgs/test/test/runner/browser/chrome_test.dart b/pkgs/test/test/runner/browser/chrome_test.dart
index b5ac51e..fb556e8 100644
--- a/pkgs/test/test/runner/browser/chrome_test.dart
+++ b/pkgs/test/test/runner/browser/chrome_test.dart
@@ -15,6 +15,8 @@
import 'code_server.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('starts Chrome with the given URL', () async {
var server = await CodeServer.start();
diff --git a/pkgs/test/test/runner/browser/compact_reporter_test.dart b/pkgs/test/test/runner/browser/compact_reporter_test.dart
index 4a65f92..c8a14fc 100644
--- a/pkgs/test/test/runner/browser/compact_reporter_test.dart
+++ b/pkgs/test/test/runner/browser/compact_reporter_test.dart
@@ -10,6 +10,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('prints the platform name when running on multiple platforms', () async {
await d.file('test.dart', '''
import 'dart:async';
diff --git a/pkgs/test/test/runner/browser/expanded_reporter_test.dart b/pkgs/test/test/runner/browser/expanded_reporter_test.dart
index c334843..21b1bc4 100644
--- a/pkgs/test/test/runner/browser/expanded_reporter_test.dart
+++ b/pkgs/test/test/runner/browser/expanded_reporter_test.dart
@@ -11,6 +11,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('prints the platform name when running on multiple platforms', () async {
await d.file('test.dart', '''
import 'dart:async';
diff --git a/pkgs/test/test/runner/browser/firefox_test.dart b/pkgs/test/test/runner/browser/firefox_test.dart
index 748eafe..d1ac035 100644
--- a/pkgs/test/test/runner/browser/firefox_test.dart
+++ b/pkgs/test/test/runner/browser/firefox_test.dart
@@ -16,6 +16,8 @@
import 'code_server.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('starts Firefox with the given URL', () async {
var server = await CodeServer.start();
diff --git a/pkgs/test/test/runner/browser/internet_explorer_test.dart b/pkgs/test/test/runner/browser/internet_explorer_test.dart
index e240755..a0fadbf 100644
--- a/pkgs/test/test/runner/browser/internet_explorer_test.dart
+++ b/pkgs/test/test/runner/browser/internet_explorer_test.dart
@@ -16,6 +16,8 @@
import 'code_server.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('starts IE with the given URL', () async {
var server = await CodeServer.start();
diff --git a/pkgs/test/test/runner/browser/runner_test.dart b/pkgs/test/test/runner/browser/runner_test.dart
index ccd12ce..c9e2a44 100644
--- a/pkgs/test/test/runner/browser/runner_test.dart
+++ b/pkgs/test/test/runner/browser/runner_test.dart
@@ -28,6 +28,8 @@
''';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('fails gracefully if', () {
test('a test file fails to compile', () async {
await d.file('test.dart', 'invalid Dart file').create();
diff --git a/pkgs/test/test/runner/browser/safari_test.dart b/pkgs/test/test/runner/browser/safari_test.dart
index e1f5a1a..f45f5e6 100644
--- a/pkgs/test/test/runner/browser/safari_test.dart
+++ b/pkgs/test/test/runner/browser/safari_test.dart
@@ -16,6 +16,8 @@
import 'code_server.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('starts Safari with the given URL', () async {
var server = await CodeServer.start();
diff --git a/pkgs/test/test/runner/compact_reporter_test.dart b/pkgs/test/test/runner/compact_reporter_test.dart
index 4e21916..92b5c67 100644
--- a/pkgs/test/test/runner/compact_reporter_test.dart
+++ b/pkgs/test/test/runner/compact_reporter_test.dart
@@ -13,6 +13,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('reports when no tests are run', () async {
await d.file('test.dart', 'void main() {}').create();
diff --git a/pkgs/test/test/runner/configuration/custom_platform_test.dart b/pkgs/test/test/runner/configuration/custom_platform_test.dart
index 050a32c..39a6bbd 100644
--- a/pkgs/test/test/runner/configuration/custom_platform_test.dart
+++ b/pkgs/test/test/runner/configuration/custom_platform_test.dart
@@ -17,6 +17,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
setUp(() async {
await d.file('test.dart', '''
import 'package:test/test.dart';
diff --git a/pkgs/test/test/runner/configuration/duplicate_names_test.dart b/pkgs/test/test/runner/configuration/duplicate_names_test.dart
index 8e5fe9e..a6f6cc4 100644
--- a/pkgs/test/test/runner/configuration/duplicate_names_test.dart
+++ b/pkgs/test/test/runner/configuration/duplicate_names_test.dart
@@ -14,6 +14,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('duplicate names', () {
group('can be disabled for', () {
for (var function in ['group', 'test']) {
diff --git a/pkgs/test/test/runner/configuration/global_test.dart b/pkgs/test/test/runner/configuration/global_test.dart
index 1ebd0cf..dce108b 100644
--- a/pkgs/test/test/runner/configuration/global_test.dart
+++ b/pkgs/test/test/runner/configuration/global_test.dart
@@ -13,6 +13,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('ignores an empty file', () async {
await d.file('global_test.yaml', '').create();
diff --git a/pkgs/test/test/runner/configuration/platform_test.dart b/pkgs/test/test/runner/configuration/platform_test.dart
index d9bcede..a111801 100644
--- a/pkgs/test/test/runner/configuration/platform_test.dart
+++ b/pkgs/test/test/runner/configuration/platform_test.dart
@@ -14,6 +14,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('on_platform', () {
test('applies platform-specific configuration to matching tests', () async {
await d
diff --git a/pkgs/test/test/runner/configuration/presets_test.dart b/pkgs/test/test/runner/configuration/presets_test.dart
index e802a87..1d3b34a 100644
--- a/pkgs/test/test/runner/configuration/presets_test.dart
+++ b/pkgs/test/test/runner/configuration/presets_test.dart
@@ -14,6 +14,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('presets', () {
test("don't do anything by default", () async {
await d
diff --git a/pkgs/test/test/runner/configuration/randomize_order_test.dart b/pkgs/test/test/runner/configuration/randomize_order_test.dart
index adb853e..7d39aa3 100644
--- a/pkgs/test/test/runner/configuration/randomize_order_test.dart
+++ b/pkgs/test/test/runner/configuration/randomize_order_test.dart
@@ -12,6 +12,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('shuffles test order when passed a seed', () async {
await d.file('test.dart', '''
import 'package:test/test.dart';
@@ -176,7 +178,7 @@
test("test 2.2", () {});
test("test 2.3", () {});
test("test 2.4", () {});
- });
+ });
}
''').create();
diff --git a/pkgs/test/test/runner/configuration/tags_test.dart b/pkgs/test/test/runner/configuration/tags_test.dart
index c5fc8b6..208f119 100644
--- a/pkgs/test/test/runner/configuration/tags_test.dart
+++ b/pkgs/test/test/runner/configuration/tags_test.dart
@@ -14,6 +14,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('adds the specified tags', () async {
await d
.file(
diff --git a/pkgs/test/test/runner/configuration/top_level_error_test.dart b/pkgs/test/test/runner/configuration/top_level_error_test.dart
index eb08350..e4fd130 100644
--- a/pkgs/test/test/runner/configuration/top_level_error_test.dart
+++ b/pkgs/test/test/runner/configuration/top_level_error_test.dart
@@ -13,6 +13,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('rejects an invalid fold_stack_frames', () async {
await d
.file('dart_test.yaml', jsonEncode({'fold_stack_frames': 'flup'}))
diff --git a/pkgs/test/test/runner/configuration/top_level_test.dart b/pkgs/test/test/runner/configuration/top_level_test.dart
index d1031b5..2670c7a 100644
--- a/pkgs/test/test/runner/configuration/top_level_test.dart
+++ b/pkgs/test/test/runner/configuration/top_level_test.dart
@@ -15,6 +15,8 @@
import '../../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('ignores an empty file', () async {
await d.file('dart_test.yaml', '').create();
diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart
index 9298be3..a9005db 100644
--- a/pkgs/test/test/runner/coverage_test.dart
+++ b/pkgs/test/test/runner/coverage_test.dart
@@ -16,6 +16,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('with the --coverage flag,', () {
late Directory coverageDirectory;
diff --git a/pkgs/test/test/runner/data_isolate_strategy_test.dart b/pkgs/test/test/runner/data_isolate_strategy_test.dart
index 7063d50..5bae348 100644
--- a/pkgs/test/test/runner/data_isolate_strategy_test.dart
+++ b/pkgs/test/test/runner/data_isolate_strategy_test.dart
@@ -18,6 +18,7 @@
late PackageConfig currentPackageConfig;
setUpAll(() async {
+ await precompileTestExecutable();
currentPackageConfig =
await loadPackageConfigUri((await Isolate.packageConfig)!);
});
diff --git a/pkgs/test/test/runner/expanded_reporter_test.dart b/pkgs/test/test/runner/expanded_reporter_test.dart
index 54c0549..b14b676 100644
--- a/pkgs/test/test/runner/expanded_reporter_test.dart
+++ b/pkgs/test/test/runner/expanded_reporter_test.dart
@@ -13,6 +13,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('reports when no tests are run', () async {
await d.file('test.dart', 'void main() {}').create();
diff --git a/pkgs/test/test/runner/hybrid_test.dart b/pkgs/test/test/runner/hybrid_test.dart
index 4e66ce6..ccceb54 100644
--- a/pkgs/test/test/runner/hybrid_test.dart
+++ b/pkgs/test/test/runner/hybrid_test.dart
@@ -17,6 +17,7 @@
void main() {
late String packageRoot;
setUpAll(() async {
+ await precompileTestExecutable();
packageRoot = p.absolute(p.dirname(p
.fromUri(await Isolate.resolvePackageUri(Uri.parse('package:test/')))));
});
diff --git a/pkgs/test/test/runner/json_file_reporter_test.dart b/pkgs/test/test/runner/json_file_reporter_test.dart
index 59b5694..5cc3684 100644
--- a/pkgs/test/test/runner/json_file_reporter_test.dart
+++ b/pkgs/test/test/runner/json_file_reporter_test.dart
@@ -18,6 +18,8 @@
import 'json_reporter_utils.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('runs successful tests with a stdout reporter and file reporter', () {
return _expectReports('''
test('success 1', () {});
diff --git a/pkgs/test/test/runner/json_reporter_test.dart b/pkgs/test/test/runner/json_reporter_test.dart
index e0bd3a9..ede34de 100644
--- a/pkgs/test/test/runner/json_reporter_test.dart
+++ b/pkgs/test/test/runner/json_reporter_test.dart
@@ -15,6 +15,8 @@
import 'json_reporter_utils.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('runs several successful tests and reports when each completes', () {
return _expectReport('''
test('success 1', () {});
diff --git a/pkgs/test/test/runner/name_test.dart b/pkgs/test/test/runner/name_test.dart
index 447ecfa..301b21e 100644
--- a/pkgs/test/test/runner/name_test.dart
+++ b/pkgs/test/test/runner/name_test.dart
@@ -12,6 +12,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('with test.dart?name="name" query', () {
test('selects tests with matching names', () async {
await d.file('test.dart', '''
diff --git a/pkgs/test/test/runner/node/runner_test.dart b/pkgs/test/test/runner/node/runner_test.dart
index 149f951..b5d02c1 100644
--- a/pkgs/test/test/runner/node/runner_test.dart
+++ b/pkgs/test/test/runner/node/runner_test.dart
@@ -29,6 +29,8 @@
''';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('fails gracefully if', () {
test('a test file fails to compile', () async {
await d.file('test.dart', 'invalid Dart file').create();
diff --git a/pkgs/test/test/runner/pause_after_load_test.dart b/pkgs/test/test/runner/pause_after_load_test.dart
index 2dd4704..88576aa 100644
--- a/pkgs/test/test/runner/pause_after_load_test.dart
+++ b/pkgs/test/test/runner/pause_after_load_test.dart
@@ -13,6 +13,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('pauses the test runner for each file until the user presses enter',
() async {
await d.file('test1.dart', '''
diff --git a/pkgs/test/test/runner/precompiled_test.dart b/pkgs/test/test/runner/precompiled_test.dart
index 72022fa..1c0ec2b 100644
--- a/pkgs/test/test/runner/precompiled_test.dart
+++ b/pkgs/test/test/runner/precompiled_test.dart
@@ -20,6 +20,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('browser tests', () {
setUp(() async {
await d.file('to_precompile.dart', '''
diff --git a/pkgs/test/test/runner/pub_serve_test.dart b/pkgs/test/test/runner/pub_serve_test.dart
index 744385c..35d2578 100644
--- a/pkgs/test/test/runner/pub_serve_test.dart
+++ b/pkgs/test/test/runner/pub_serve_test.dart
@@ -19,6 +19,8 @@
String get _pubServeArg => '--pub-serve=$pubServePort';
void main() {
+ setUpAll(precompileTestExecutable);
+
setUp(() async {
await d.file('pubspec.yaml', '''
name: myapp
diff --git a/pkgs/test/test/runner/retry_test.dart b/pkgs/test/test/runner/retry_test.dart
index 8a3585f..519ce9a 100644
--- a/pkgs/test/test/runner/retry_test.dart
+++ b/pkgs/test/test/runner/retry_test.dart
@@ -10,6 +10,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('respects --no-retry flag with retry option', () async {
await d.file('test.dart', '''
import 'dart:async';
diff --git a/pkgs/test/test/runner/runner_test.dart b/pkgs/test/test/runner/runner_test.dart
index 75ca350..2d1eadf 100644
--- a/pkgs/test/test/runner/runner_test.dart
+++ b/pkgs/test/test/runner/runner_test.dart
@@ -123,6 +123,8 @@
', node]';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('prints help information', () async {
var test = await runTest(['--help']);
expectStdoutEquals(test, '''
diff --git a/pkgs/test/test/runner/set_up_all_test.dart b/pkgs/test/test/runner/set_up_all_test.dart
index e029019..1e280c6 100644
--- a/pkgs/test/test/runner/set_up_all_test.dart
+++ b/pkgs/test/test/runner/set_up_all_test.dart
@@ -11,6 +11,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('an error causes the run to fail', () async {
await d.file('test.dart', r'''
import 'package:test/test.dart';
diff --git a/pkgs/test/test/runner/shard_test.dart b/pkgs/test/test/runner/shard_test.dart
index 311b91e..5f1be9c 100644
--- a/pkgs/test/test/runner/shard_test.dart
+++ b/pkgs/test/test/runner/shard_test.dart
@@ -12,6 +12,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('divides all the tests among the available shards', () async {
await d.file('test.dart', '''
import 'package:test/test.dart';
diff --git a/pkgs/test/test/runner/signal_test.dart b/pkgs/test/test/runner/signal_test.dart
index d141329..f456494 100644
--- a/pkgs/test/test/runner/signal_test.dart
+++ b/pkgs/test/test/runner/signal_test.dart
@@ -23,6 +23,8 @@
// represent a best effort to kill the test runner at certain times during its
// execution.
void main() {
+ setUpAll(precompileTestExecutable);
+
setUp(() => d.dir('tmp').create());
group('during loading,', () {
diff --git a/pkgs/test/test/runner/skip_expect_test.dart b/pkgs/test/test/runner/skip_expect_test.dart
index e728827..6d51fac 100644
--- a/pkgs/test/test/runner/skip_expect_test.dart
+++ b/pkgs/test/test/runner/skip_expect_test.dart
@@ -11,6 +11,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
group('a skipped expect', () {
test('marks the test as skipped', () async {
await d.file('test.dart', '''
diff --git a/pkgs/test/test/runner/solo_test.dart b/pkgs/test/test/runner/solo_test.dart
index 4bdb199..508ac67 100644
--- a/pkgs/test/test/runner/solo_test.dart
+++ b/pkgs/test/test/runner/solo_test.dart
@@ -10,6 +10,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('only runs the tests marked as solo', () async {
await d.file('test.dart', '''
import 'dart:async';
diff --git a/pkgs/test/test/runner/tag_test.dart b/pkgs/test/test/runner/tag_test.dart
index d7a15c9..f2f6d19 100644
--- a/pkgs/test/test/runner/tag_test.dart
+++ b/pkgs/test/test/runner/tag_test.dart
@@ -11,6 +11,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
setUp(() async {
await d.file('test.dart', '''
import 'package:test/test.dart';
diff --git a/pkgs/test/test/runner/tear_down_all_test.dart b/pkgs/test/test/runner/tear_down_all_test.dart
index 67b4808..1304ccc 100644
--- a/pkgs/test/test/runner/tear_down_all_test.dart
+++ b/pkgs/test/test/runner/tear_down_all_test.dart
@@ -11,6 +11,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('an error causes the run to fail', () async {
await d.file('test.dart', r'''
import 'package:test/test.dart';
diff --git a/pkgs/test/test/runner/test_chain_test.dart b/pkgs/test/test/runner/test_chain_test.dart
index 5b83acb..e7c1da0 100644
--- a/pkgs/test/test/runner/test_chain_test.dart
+++ b/pkgs/test/test/runner/test_chain_test.dart
@@ -12,6 +12,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
setUp(() async {
await d.file('test.dart', '''
import 'dart:async';
diff --git a/pkgs/test/test/runner/test_on_test.dart b/pkgs/test/test/runner/test_on_test.dart
index 95a39bd..47c510f 100644
--- a/pkgs/test/test/runner/test_on_test.dart
+++ b/pkgs/test/test/runner/test_on_test.dart
@@ -20,6 +20,7 @@
late PackageConfig currentPackageConfig;
setUpAll(() async {
+ await precompileTestExecutable();
currentPackageConfig =
await loadPackageConfigUri((await Isolate.packageConfig)!);
});
diff --git a/pkgs/test/test/runner/timeout_test.dart b/pkgs/test/test/runner/timeout_test.dart
index a9756da..598b95d 100644
--- a/pkgs/test/test/runner/timeout_test.dart
+++ b/pkgs/test/test/runner/timeout_test.dart
@@ -11,6 +11,8 @@
import '../io.dart';
void main() {
+ setUpAll(precompileTestExecutable);
+
test('respects top-level @Timeout declarations', () async {
await d.file('test.dart', '''
@Timeout(const Duration(seconds: 0))