Index: test/vm_listener_test.dart |
diff --git a/test/vm_listener_test.dart b/test/vm_listener_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b626ad151a72a86cf4cb05c007053674145c1d22 |
--- /dev/null |
+++ b/test/vm_listener_test.dart |
@@ -0,0 +1,308 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. 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 'dart:isolate'; |
+ |
+import 'package:unittest/src/declarer.dart'; |
+import 'package:unittest/src/invoker.dart'; |
+import 'package:unittest/src/isolate_test.dart'; |
+import 'package:unittest/src/live_test.dart'; |
+import 'package:unittest/src/state.dart'; |
+import 'package:unittest/src/suite.dart'; |
+import 'package:unittest/src/vm_listener.dart'; |
+import 'package:unittest/unittest.dart'; |
+ |
+import 'utils.dart'; |
+ |
+/// The current declarer. |
+Declarer get _declarer => Zone.current[#unittest.declarer]; |
+ |
+/// An isolate that's been spun up for the current test. |
+/// |
+/// This is tracked so that it can be killed once the test is done. |
+Isolate _isolate; |
+ |
+/// A live test that's running for the current test. |
+/// |
+/// This is tracked so that it can be closed once the test is done. |
+LiveTest _liveTest; |
+ |
+void main() { |
+ tearDown(() { |
+ if (_isolate != null) _isolate.kill(Isolate.IMMEDIATE); |
+ _isolate = null; |
+ |
+ if (_liveTest != null) _liveTest.close(); |
+ _liveTest = null; |
+ }); |
+ |
+ test("sends a list of available tests on startup", () { |
+ return _spawnIsolate(_successfulTests).then((receivePort) { |
+ return receivePort.first; |
+ }).then((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")); |
+ }); |
+ }); |
+ |
+ group("in a successful test", () { |
+ test("the state changes from pending to running to complete", () { |
+ return _isolateTest(_successfulTests).then((liveTest) { |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ expect(liveTest.state, |
+ equals(const State(Status.pending, Result.success))); |
+ |
+ var future = liveTest.run(); |
+ |
+ expect(liveTest.state, |
+ equals(const State(Status.running, Result.success))); |
+ |
+ return future.then((_) { |
+ expect(liveTest.state, |
+ equals(const State(Status.complete, Result.success))); |
+ }); |
+ }); |
+ }); |
+ |
+ test("onStateChange fires for each state change", () { |
+ return _isolateTest(_successfulTests).then((liveTest) { |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ var first = true; |
+ liveTest.onStateChange.listen(expectAsync((state) { |
+ if (first) { |
+ expect(state.status, equals(Status.running)); |
+ first = false; |
+ } else { |
+ expect(state.status, equals(Status.complete)); |
+ } |
+ expect(state.result, equals(Result.success)); |
+ }, count: 2, max: 2)); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ }); |
+ |
+ group("in a test with failures", () { |
+ test("a failure reported causes the test to fail", () { |
+ return _isolateTest(_failingTest).then((liveTest) { |
+ expectSingleFailure(liveTest); |
+ return liveTest.run(); |
+ }); |
+ }); |
+ |
+ test("a failure reported asynchronously after the test causes it to error", |
+ () { |
+ return _isolateTest(_failAfterSucceedTest).then((liveTest) { |
+ expectStates(liveTest, [ |
+ const State(Status.running, Result.success), |
+ const State(Status.complete, Result.success), |
+ const State(Status.complete, Result.failure), |
+ const State(Status.complete, Result.error) |
+ ]); |
+ |
+ expectErrors(liveTest, [(error) { |
+ expect(lastState, |
+ equals(const State(Status.complete, Result.failure))); |
+ expect(error, isTestFailure("oh no")); |
+ }, (error) { |
+ expect(lastState, equals(const State(Status.complete, Result.error))); |
+ expect(error, isRemoteException( |
+ "This test failed after it had already completed. Make sure to " |
+ "use [expectAsync]\n" |
+ "or the [completes] matcher when testing async code.")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ |
+ test("multiple asynchronous failures are reported", () { |
+ return _isolateTest(_multiFailTest).then((liveTest) { |
+ expectStates(liveTest, [ |
+ const State(Status.running, Result.success), |
+ const State(Status.complete, Result.failure) |
+ ]); |
+ |
+ expectErrors(liveTest, [(error) { |
+ expect(lastState.status, equals(Status.complete)); |
+ expect(error, isTestFailure("one")); |
+ }, (error) { |
+ expect(error, isTestFailure("two")); |
+ }, (error) { |
+ expect(error, isTestFailure("three")); |
+ }, (error) { |
+ expect(error, isTestFailure("four")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ }); |
+ |
+ group("in a test with errors", () { |
+ test("an error reported causes the test to error", () { |
+ return _isolateTest(_errorTest).then((liveTest) { |
+ expectStates(liveTest, [ |
+ const State(Status.running, Result.success), |
+ const State(Status.complete, Result.error) |
+ ]); |
+ |
+ expectErrors(liveTest, [(error) { |
+ expect(lastState.status, equals(Status.complete)); |
+ expect(error, isRemoteException("oh no")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ |
+ test("an error reported asynchronously after the test causes it to error", |
+ () { |
+ return _isolateTest(_errorAfterSucceedTest).then((liveTest) { |
+ expectStates(liveTest, [ |
+ const State(Status.running, Result.success), |
+ const State(Status.complete, Result.success), |
+ const State(Status.complete, Result.error) |
+ ]); |
+ |
+ expectErrors(liveTest, [(error) { |
+ expect(lastState, |
+ equals(const State(Status.complete, Result.error))); |
+ expect(error, isRemoteException("oh no")); |
+ }, (error) { |
+ expect(error, isRemoteException( |
+ "This test failed after it had already completed. Make sure to " |
+ "use [expectAsync]\n" |
+ "or the [completes] matcher when testing async code.")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ |
+ test("multiple asynchronous errors are reported", () { |
+ return _isolateTest(_multiErrorTest).then((liveTest) { |
+ expectStates(liveTest, [ |
+ const State(Status.running, Result.success), |
+ const State(Status.complete, Result.error) |
+ ]); |
+ |
+ expectErrors(liveTest, [(error) { |
+ expect(lastState.status, equals(Status.complete)); |
+ expect(error, isRemoteException("one")); |
+ }, (error) { |
+ expect(error, isRemoteException("two")); |
+ }, (error) { |
+ expect(error, isRemoteException("three")); |
+ }, (error) { |
+ expect(error, isRemoteException("four")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ }); |
+} |
+ |
+/// Loads the first test defined in [entryPoint] in another isolate. |
+/// |
+/// This test will be automatically closed when the test is finished. |
+Future<LiveTest> _isolateTest(void entryPoint(SendPort sendPort)) { |
+ return _spawnIsolate(entryPoint).then((receivePort) { |
+ return receivePort.first; |
+ }).then((response) { |
+ var testMap = response.first; |
+ var test = new IsolateTest(testMap["name"], testMap["sendPort"]); |
+ var suite = new Suite("suite", [test]); |
+ _liveTest = test.load(suite); |
+ return _liveTest; |
+ }); |
+} |
+ |
+/// Spawns an isolate from [entryPoint], sends it a new [SendPort], and returns |
+/// the corresponding [ReceivePort]. |
+/// |
+/// This isolate will be automatically killed when the test is finished. |
+Future<ReceivePort> _spawnIsolate(void entryPoint(SendPort sendPort)) { |
+ var receivePort = new ReceivePort(); |
+ return Isolate.spawn(entryPoint, receivePort.sendPort).then((isolate) { |
+ _isolate = isolate; |
+ return receivePort; |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines three tests that succeed. |
+void _successfulTests(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("successful 1", () {}); |
+ _declarer.test("successful 2", () {}); |
+ _declarer.test("successful 3", () {}); |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines a test that fails. |
+void _failingTest(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("failure", () => throw new TestFailure('oh no')); |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines a test that fails after succeeding. |
+void _failAfterSucceedTest(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("fail after succeed", () { |
+ pumpEventQueue().then((_) { |
+ throw new TestFailure('oh no'); |
+ }); |
+ }); |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines a test that fails multiple times. |
+void _multiFailTest(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("multiple failures", () { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw new TestFailure("one")); |
+ new Future(() => throw new TestFailure("two")); |
+ new Future(() => throw new TestFailure("three")); |
+ new Future(() => throw new TestFailure("four")); |
+ }); |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines a test that errors. |
+void _errorTest(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("error", () => throw 'oh no'); |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines a test that errors after succeeding. |
+void _errorAfterSucceedTest(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("error after succeed", () { |
+ pumpEventQueue().then((_) => throw 'oh no'); |
+ }); |
+ }); |
+} |
+ |
+/// An isolate entrypoint that defines a test that errors multiple times. |
+void _multiErrorTest(SendPort sendPort) { |
+ VmListener.start(sendPort, () { |
+ _declarer.test("multiple errors", () { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw "one"); |
+ new Future(() => throw "two"); |
+ new Future(() => throw "three"); |
+ new Future(() => throw "four"); |
+ }); |
+ }); |
+} |