Support asynchronous main methods.
This also releases 0.12.0-rc.1.
Closes #92
R=kevmoo@google.com
Review URL: https://codereview.chromium.org//1107743002
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ae970ae..14c532b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,9 @@
* Remove `handleExternalError`. This was never used in practice and its function
was unclear.
+* If a test suite's `main()` method returns a `Future`, tests may be declared
+ until that `Future` returns.
+
### 0.12.0-rc.0
* Tests, groups, and suites can now be configured on a platform-by-platform
diff --git a/lib/src/runner/browser/iframe_listener.dart b/lib/src/runner/browser/iframe_listener.dart
index b345f37..7680451 100644
--- a/lib/src/runner/browser/iframe_listener.dart
+++ b/lib/src/runner/browser/iframe_listener.dart
@@ -56,24 +56,23 @@
}
var declarer = new Declarer();
- try {
- runZoned(main, zoneValues: {#test.declarer: declarer});
- } catch (error, stackTrace) {
+ runZoned(() => new Future.sync(main), zoneValues: {
+ #test.declarer: declarer
+ }).then((_) {
+ var url = Uri.parse(window.location.href);
+ var message = JSON.decode(Uri.decodeFull(url.fragment));
+ var metadata = new Metadata.deserialize(message['metadata']);
+ var browser = TestPlatform.find(message['browser']);
+
+ var suite = new Suite(declarer.tests, metadata: metadata)
+ .forPlatform(browser);
+ new IframeListener._(suite)._listen(channel);
+ }, onError: (error, stackTrace) {
channel.sink.add({
"type": "error",
"error": RemoteException.serialize(error, stackTrace)
});
- return;
- }
-
- var url = Uri.parse(window.location.href);
- var message = JSON.decode(Uri.decodeFull(url.fragment));
- var metadata = new Metadata.deserialize(message['metadata']);
- var browser = TestPlatform.find(message['browser']);
-
- var suite = new Suite(declarer.tests, metadata: metadata)
- .forPlatform(browser);
- new IframeListener._(suite)._listen(channel);
+ });
}
/// Constructs a [MultiChannel] wrapping the `postMessage` communication with
diff --git a/lib/src/runner/vm/isolate_listener.dart b/lib/src/runner/vm/isolate_listener.dart
index ed23a1d..a25c9d6 100644
--- a/lib/src/runner/vm/isolate_listener.dart
+++ b/lib/src/runner/vm/isolate_listener.dart
@@ -51,19 +51,18 @@
}
var declarer = new Declarer();
- try {
- runZoned(main, zoneValues: {#test.declarer: declarer});
- } catch (error, stackTrace) {
+ runZoned(() => new Future.sync(main), zoneValues: {
+ #test.declarer: declarer
+ }).then((_) {
+ var suite = new Suite(declarer.tests, metadata: metadata)
+ .forPlatform(TestPlatform.vm, os: currentOS);
+ new IsolateListener._(suite)._listen(sendPort);
+ }, onError: (error, stackTrace) {
sendPort.send({
"type": "error",
"error": RemoteException.serialize(error, stackTrace)
});
- return;
- }
-
- var suite = new Suite(declarer.tests, metadata: metadata)
- .forPlatform(TestPlatform.vm, os: currentOS);
- new IsolateListener._(suite)._listen(sendPort);
+ });
}
/// Sends a message over [sendPort] indicating that the tests failed to load.
diff --git a/pubspec.yaml b/pubspec.yaml
index 6c96002..3e7ab79 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: test
-version: 0.12.0-dev
+version: 0.12.0-rc.1
author: Dart Team <misc@dartlang.org>
description: A library for writing dart unit tests.
homepage: https://github.com/dart-lang/test
diff --git a/test/runner/browser/loader_test.dart b/test/runner/browser/loader_test.dart
index fbf549c..9dd4f62 100644
--- a/test/runner/browser/loader_test.dart
+++ b/test/runner/browser/loader_test.dart
@@ -88,6 +88,39 @@
});
});
+
+ test("loads tests that are defined asynchronously", () {
+ new File(p.join(_sandbox, 'a_test.dart')).writeAsStringSync("""
+import 'dart:async';
+
+import 'package:test/test.dart';
+
+Future main() {
+ return new Future(() {
+ test("success", () {});
+
+ return new Future(() {
+ test("failure", () => throw new TestFailure('oh no'));
+
+ return new Future(() {
+ test("error", () => throw 'oh no');
+ });
+ });
+ });
+}
+""");
+
+ return _loader.loadFile(p.join(_sandbox, 'a_test.dart')).toList()
+ .then((suites) {
+ expect(suites, hasLength(1));
+ var suite = suites.first;
+ expect(suite.tests, hasLength(3));
+ expect(suite.tests[0].name, equals("success"));
+ expect(suite.tests[1].name, equals("failure"));
+ expect(suite.tests[2].name, equals("error"));
+ });
+ });
+
test("loads a suite both in the browser and the VM", () {
var loader = new Loader([TestPlatform.vm, TestPlatform.chrome],
root: _sandbox,
diff --git a/test/runner/isolate_listener_test.dart b/test/runner/isolate_listener_test.dart
index 0960c5a..2952832 100644
--- a/test/runner/isolate_listener_test.dart
+++ b/test/runner/isolate_listener_test.dart
@@ -56,6 +56,21 @@
});
});
+ test("waits for a returned future sending a response", () {
+ return _spawnIsolate(_asyncTests).then((receivePort) {
+ return receivePort.first;
+ }).then((response) {
+ expect(response, containsPair("type", "success"));
+ expect(response, contains("tests"));
+
+ var tests = response["tests"];
+ expect(tests, hasLength(3));
+ expect(tests[0], containsPair("name", "successful 1"));
+ expect(tests[1], containsPair("name", "successful 2"));
+ expect(tests[2], containsPair("name", "successful 3"));
+ });
+ });
+
test("sends an error response if loading fails", () {
return _spawnIsolate(_loadError).then((receivePort) {
return receivePort.first;
@@ -330,6 +345,23 @@
});
}
+/// An isolate entrypoint that defines three tests asynchronously.
+void _asyncTests(SendPort sendPort) {
+ IsolateListener.start(sendPort, new Metadata(), () => () {
+ return new Future(() {
+ test("successful 1", () {});
+
+ return new Future(() {
+ test("successful 2", () {});
+
+ return new Future(() {
+ test("successful 3", () {});
+ });
+ });
+ });
+ });
+}
+
/// An isolate entrypoint that defines a test that fails.
void _failingTest(SendPort sendPort) {
IsolateListener.start(sendPort, new Metadata(), () => () {