Allow some async work in main with global declarer (#1314)
When a test script is run directly without the test runner creating a
wrapping script there is a global `Declarer` which gets instantiated
during the first interaction with the test framework, such as a call to
`group` or `test`. This would wait a microtask to start running the
declared test cases, but would not work with a `main` that did other
async work before declaring more tests. We can't support arbitrary async
waits because we don't have a consistent signal that the `main` work is
done, so use `pumpEventQueue` as a best effort.
diff --git a/pkgs/test/test/runner/runner_test.dart b/pkgs/test/test/runner/runner_test.dart
index 224fdc2..697917e 100644
--- a/pkgs/test/test/runner/runner_test.dart
+++ b/pkgs/test/test/runner/runner_test.dart
@@ -362,6 +362,34 @@
});
});
+ group('runs successful tests with async setup', () {
+ setUp(() async {
+ await d.file('test.dart', '''
+ import 'package:test/test.dart';
+
+ void main() async {
+ test("success 1", () {});
+
+ await () async {};
+
+ test("success 2", () {});
+ }
+ ''').create();
+ });
+
+ test('defined in a single file', () async {
+ var test = await runTest(['test.dart']);
+ expect(test.stdout, emitsThrough(contains('+2: All tests passed!')));
+ await test.shouldExit(0);
+ });
+
+ test('directly', () async {
+ var test = await runDart(['test.dart']);
+ expect(test.stdout, emitsThrough(contains('All tests passed!')));
+ await test.shouldExit(0);
+ });
+ });
+
group('runs failing tests', () {
test('defaults to chaining stack traces', () async {
await d.file('test.dart', _asyncFailure).create();
diff --git a/pkgs/test_core/lib/test_core.dart b/pkgs/test_core/lib/test_core.dart
index 8a31a76..af2357e 100644
--- a/pkgs/test_core/lib/test_core.dart
+++ b/pkgs/test_core/lib/test_core.dart
@@ -14,6 +14,7 @@
import 'package:test_api/src/backend/declarer.dart'; // ignore: implementation_imports
import 'package:test_api/src/backend/invoker.dart'; // ignore: implementation_imports
import 'package:test_api/src/frontend/timeout.dart'; // ignore: implementation_imports
+import 'package:test_api/src/frontend/utils.dart'; // ignore: implementation_imports
import 'package:test_api/src/utils.dart'; // ignore: implementation_imports
import 'src/runner/engine.dart';
@@ -48,10 +49,13 @@
// Since there's no Zone-scoped declarer, the test file is being run directly.
// In order to run the tests, we set up our own Declarer via
- // [_globalDeclarer], and schedule a microtask to run the tests once they're
- // finished being defined.
+ // [_globalDeclarer], and pump the event queue as a best effort to wait for
+ // all tests to be defined before starting them.
_globalDeclarer = Declarer();
- scheduleMicrotask(() async {
+
+ () async {
+ await pumpEventQueue();
+
var suite = RunnerSuite(const PluginEnvironment(), SuiteConfiguration.empty,
_globalDeclarer!.build(), SuitePlatform(Runtime.vm, os: currentOSGuess),
path: p.prettyUri(Uri.base));
@@ -67,7 +71,8 @@
if (success == true) return null;
print('');
unawaited(Future.error('Dummy exception to set exit code.'));
- });
+ }();
+
return _globalDeclarer!;
}