Index: mojo/public/dart/third_party/test/test/backend/invoker_test.dart |
diff --git a/mojo/public/dart/third_party/test/test/backend/invoker_test.dart b/mojo/public/dart/third_party/test/test/backend/invoker_test.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..1cc014db837dcbe6687644ad2be140d141539621 |
--- /dev/null |
+++ b/mojo/public/dart/third_party/test/test/backend/invoker_test.dart |
@@ -0,0 +1,700 @@ |
+// 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 'package:fake_async/fake_async.dart'; |
+import 'package:test/src/backend/invoker.dart'; |
+import 'package:test/src/backend/metadata.dart'; |
+import 'package:test/src/backend/state.dart'; |
+import 'package:test/src/backend/suite.dart'; |
+import 'package:test/test.dart'; |
+ |
+import '../utils.dart'; |
+ |
+void main() { |
+ var suite; |
+ setUp(() { |
+ lastState = null; |
+ suite = new Suite([]); |
+ }); |
+ |
+ group("Invoker.current", () { |
+ var invoker = Invoker.current; |
+ test("returns null outside of a test body", () { |
+ expect(invoker, isNull); |
+ }); |
+ |
+ test("returns the current invoker in a test body", () async { |
+ var invoker; |
+ var liveTest = _localTest(() { |
+ invoker = Invoker.current; |
+ }).load(suite); |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ await liveTest.run(); |
+ expect(invoker.liveTest, equals(liveTest)); |
+ }); |
+ |
+ test("returns the current invoker in a test body after the test completes", |
+ () async { |
+ var status; |
+ var completer = new Completer(); |
+ var liveTest = _localTest(() { |
+ // Use [new Future] in particular to wait longer than a microtask for |
+ // the test to complete. |
+ new Future(() { |
+ status = Invoker.current.liveTest.state.status; |
+ completer.complete(Invoker.current); |
+ }); |
+ }).load(suite); |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ expect(liveTest.run(), completes); |
+ var invoker = await completer.future; |
+ expect(invoker.liveTest, equals(liveTest)); |
+ expect(status, equals(Status.complete)); |
+ }); |
+ |
+ test("returns the current invoker in a tearDown body", () async { |
+ var invoker; |
+ var liveTest = _localTest(() {}, tearDown: () { |
+ invoker = Invoker.current; |
+ }).load(suite); |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ await liveTest.run(); |
+ expect(invoker.liveTest, equals(liveTest)); |
+ }); |
+ |
+ test("returns the current invoker in a tearDown body after the test " |
+ "completes", () async { |
+ var status; |
+ var completer = new Completer(); |
+ var liveTest = _localTest(() {}, tearDown: () { |
+ // Use [new Future] in particular to wait longer than a microtask for |
+ // the test to complete. |
+ new Future(() { |
+ status = Invoker.current.liveTest.state.status; |
+ completer.complete(Invoker.current); |
+ }); |
+ }).load(suite); |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ expect(liveTest.run(), completes); |
+ var invoker = await completer.future; |
+ expect(invoker.liveTest, equals(liveTest)); |
+ expect(status, equals(Status.complete)); |
+ }); |
+ }); |
+ |
+ group("in a successful test,", () { |
+ test("the state changes from pending to running to complete", () async { |
+ var stateInTest; |
+ var stateInTearDown; |
+ var liveTest; |
+ liveTest = _localTest(() { |
+ stateInTest = liveTest.state; |
+ }, tearDown: () { |
+ stateInTearDown = liveTest.state; |
+ }).load(suite); |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ expect(liveTest.state.status, equals(Status.pending)); |
+ expect(liveTest.state.result, equals(Result.success)); |
+ |
+ var future = liveTest.run(); |
+ |
+ expect(liveTest.state.status, equals(Status.running)); |
+ expect(liveTest.state.result, equals(Result.success)); |
+ |
+ await future; |
+ |
+ expect(stateInTest.status, equals(Status.running)); |
+ expect(stateInTest.result, equals(Result.success)); |
+ |
+ expect(stateInTearDown.status, equals(Status.running)); |
+ expect(stateInTearDown.result, equals(Result.success)); |
+ |
+ expect(liveTest.state.status, equals(Status.complete)); |
+ expect(liveTest.state.result, equals(Result.success)); |
+ }); |
+ |
+ test("onStateChange fires for each state change", () { |
+ var liveTest = _localTest(() {}).load(suite); |
+ 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(); |
+ }); |
+ |
+ test("onComplete completes once the test body and tearDown are done", () { |
+ var testRun = false; |
+ var tearDownRun = false; |
+ var liveTest = _localTest(() { |
+ testRun = true; |
+ }, tearDown: () { |
+ tearDownRun = true; |
+ }).load(suite); |
+ |
+ expect(liveTest.onComplete.then((_) { |
+ expect(testRun, isTrue); |
+ expect(tearDownRun, isTrue); |
+ }), completes); |
+ |
+ return liveTest.run(); |
+ }); |
+ }); |
+ |
+ group("in a test with failures,", () { |
+ test("a synchronous throw is reported and causes the test to fail", () { |
+ var liveTest = _localTest(() { |
+ throw new TestFailure('oh no'); |
+ }).load(suite); |
+ |
+ expectSingleFailure(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("a synchronous reported failure causes the test to fail", () { |
+ var liveTest = _localTest(() { |
+ registerException(new TestFailure("oh no")); |
+ }).load(suite); |
+ |
+ expectSingleFailure(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("a failure reported asynchronously during the test causes it to fail", |
+ () { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => registerException(new TestFailure("oh no"))); |
+ }).load(suite); |
+ |
+ expectSingleFailure(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("a failure thrown asynchronously during the test causes it to fail", |
+ () { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw new TestFailure("oh no")); |
+ }).load(suite); |
+ |
+ expectSingleFailure(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("a failure reported asynchronously after the test causes it to error", |
+ () { |
+ var liveTest = _localTest(() { |
+ new Future(() => registerException(new TestFailure("oh no"))); |
+ }).load(suite); |
+ |
+ 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, equals( |
+ "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", () { |
+ var liveTest = _localTest(() { |
+ 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")); |
+ }).load(suite); |
+ |
+ 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(); |
+ }); |
+ |
+ test("a failure after an error doesn't change the state of the test", () { |
+ var liveTest = _localTest(() { |
+ new Future(() => throw new TestFailure("fail")); |
+ throw "error"; |
+ }).load(suite); |
+ |
+ expectStates(liveTest, [ |
+ const State(Status.running, Result.success), |
+ const State(Status.complete, Result.error) |
+ ]); |
+ |
+ expectErrors(liveTest, [(error) { |
+ expect(lastState, equals(const State(Status.complete, Result.error))); |
+ expect(error, equals("error")); |
+ }, (error) { |
+ expect(error, isTestFailure("fail")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ |
+ test("tearDown is run after an asynchronous failure", () async { |
+ var stateDuringTearDown; |
+ var liveTest; |
+ liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw new TestFailure("oh no")); |
+ }, tearDown: () { |
+ stateDuringTearDown = liveTest.state; |
+ }).load(suite); |
+ |
+ expectSingleFailure(liveTest); |
+ await liveTest.run(); |
+ expect(stateDuringTearDown, |
+ equals(const State(Status.complete, Result.failure))); |
+ }); |
+ }); |
+ |
+ group("in a test with errors,", () { |
+ test("a synchronous throw is reported and causes the test to error", () { |
+ var liveTest = _localTest(() { |
+ throw 'oh no'; |
+ }).load(suite); |
+ |
+ expectSingleError(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("a synchronous reported error causes the test to error", () { |
+ var liveTest = _localTest(() { |
+ registerException("oh no"); |
+ }).load(suite); |
+ |
+ expectSingleError(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("an error reported asynchronously during the test causes it to error", |
+ () { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => registerException("oh no")); |
+ }).load(suite); |
+ |
+ expectSingleError(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("an error thrown asynchronously during the test causes it to error", |
+ () { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw "oh no"); |
+ }).load(suite); |
+ |
+ expectSingleError(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("an error reported asynchronously after the test causes it to error", |
+ () { |
+ var liveTest = _localTest(() { |
+ new Future(() => registerException("oh no")); |
+ }).load(suite); |
+ |
+ 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, equals("oh no")); |
+ }, (error) { |
+ expect(error, equals( |
+ "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", () { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw "one"); |
+ new Future(() => throw "two"); |
+ new Future(() => throw "three"); |
+ new Future(() => throw "four"); |
+ }).load(suite); |
+ |
+ 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, equals("one")); |
+ }, (error) { |
+ expect(error, equals("two")); |
+ }, (error) { |
+ expect(error, equals("three")); |
+ }, (error) { |
+ expect(error, equals("four")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ |
+ test("an error after a failure changes the state of the test", () { |
+ var liveTest = _localTest(() { |
+ new Future(() => throw "error"); |
+ throw new TestFailure("fail"); |
+ }).load(suite); |
+ |
+ expectStates(liveTest, [ |
+ const State(Status.running, 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("fail")); |
+ }, (error) { |
+ expect(lastState, equals(const State(Status.complete, Result.error))); |
+ expect(error, equals("error")); |
+ }]); |
+ |
+ return liveTest.run(); |
+ }); |
+ |
+ test("tearDown is run after an asynchronous error", () async { |
+ var stateDuringTearDown; |
+ var liveTest; |
+ liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw "oh no"); |
+ }, tearDown: () { |
+ stateDuringTearDown = liveTest.state; |
+ }).load(suite); |
+ |
+ expectSingleError(liveTest); |
+ await liveTest.run(); |
+ expect(stateDuringTearDown, |
+ equals(const State(Status.complete, Result.error))); |
+ }); |
+ |
+ test("an asynchronous error in tearDown causes the test to error", () { |
+ var liveTest = _localTest(() {}, tearDown: () { |
+ Invoker.current.addOutstandingCallback(); |
+ new Future(() => throw "oh no"); |
+ }).load(suite); |
+ |
+ expectSingleError(liveTest); |
+ return liveTest.run(); |
+ }); |
+ |
+ test("an error reported in the test body after tearDown begins running " |
+ "doesn't stop tearDown", () async { |
+ var tearDownComplete = false;; |
+ var completer = new Completer(); |
+ |
+ var liveTest; |
+ liveTest = _localTest(() { |
+ completer.future.then((_) => throw "not again"); |
+ throw "oh no"; |
+ }, tearDown: () { |
+ completer.complete(); |
+ |
+ // Pump the event queue so that we will run the following code after the |
+ // test body has thrown a second error. |
+ Invoker.current.addOutstandingCallback(); |
+ pumpEventQueue().then((_) { |
+ Invoker.current.removeOutstandingCallback(); |
+ tearDownComplete = true; |
+ }); |
+ }).load(suite); |
+ |
+ 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, equals("oh no")); |
+ }, |
+ (error) => expect(error, equals("not again")) |
+ ]); |
+ |
+ await liveTest.run(); |
+ expect(tearDownComplete, isTrue); |
+ }); |
+ }); |
+ |
+ test("a test doesn't complete until there are no outstanding callbacks", |
+ () async { |
+ var outstandingCallbackRemoved = false; |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ |
+ // Pump the event queue to make sure the test isn't coincidentally |
+ // completing after the outstanding callback is removed. |
+ pumpEventQueue().then((_) { |
+ outstandingCallbackRemoved = true; |
+ Invoker.current.removeOutstandingCallback(); |
+ }); |
+ }).load(suite); |
+ |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ await liveTest.run(); |
+ expect(outstandingCallbackRemoved, isTrue); |
+ }); |
+ |
+ test("a test's tearDown isn't run until there are no outstanding callbacks", |
+ () async { |
+ var outstandingCallbackRemoved = false; |
+ var outstandingCallbackRemovedBeforeTeardown = false; |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ pumpEventQueue().then((_) { |
+ outstandingCallbackRemoved = true; |
+ Invoker.current.removeOutstandingCallback(); |
+ }); |
+ }, tearDown: () { |
+ outstandingCallbackRemovedBeforeTeardown = outstandingCallbackRemoved; |
+ }).load(suite); |
+ |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ await liveTest.run(); |
+ expect(outstandingCallbackRemovedBeforeTeardown, isTrue); |
+ }); |
+ |
+ test("a test's tearDown doesn't complete until there are no outstanding " |
+ "callbacks", () async { |
+ var outstandingCallbackRemoved = false; |
+ var liveTest = _localTest(() {}, tearDown: () { |
+ Invoker.current.addOutstandingCallback(); |
+ |
+ // Pump the event queue to make sure the test isn't coincidentally |
+ // completing after the outstanding callback is removed. |
+ pumpEventQueue().then((_) { |
+ outstandingCallbackRemoved = true; |
+ Invoker.current.removeOutstandingCallback(); |
+ }); |
+ }).load(suite); |
+ |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ await liveTest.run(); |
+ expect(outstandingCallbackRemoved, isTrue); |
+ }); |
+ |
+ test("a test body's outstanding callbacks can't complete its tearDown", |
+ () async { |
+ var outstandingCallbackRemoved = false; |
+ var completer = new Completer(); |
+ var liveTest = _localTest(() { |
+ // Once the tearDown runs, remove an outstanding callback to see if it |
+ // causes the tearDown to complete. |
+ completer.future.then((_) { |
+ Invoker.current.removeOutstandingCallback(); |
+ }); |
+ }, tearDown: () { |
+ Invoker.current.addOutstandingCallback(); |
+ |
+ // This will cause the test BODY to remove an outstanding callback, which |
+ // shouldn't cause the test to complete. |
+ completer.complete(); |
+ |
+ pumpEventQueue().then((_) { |
+ outstandingCallbackRemoved = true; |
+ Invoker.current.removeOutstandingCallback(); |
+ }); |
+ }).load(suite); |
+ |
+ liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
+ |
+ await liveTest.run(); |
+ expect(outstandingCallbackRemoved, isTrue); |
+ }); |
+ |
+ test("a test's prints are captured and reported", () { |
+ expect(() { |
+ var liveTest = _localTest(() { |
+ print("Hello,"); |
+ return new Future(() => print("world!")); |
+ }).load(suite); |
+ |
+ expect(liveTest.onPrint.take(2).toList(), |
+ completion(equals(["Hello,", "world!"]))); |
+ |
+ return liveTest.run(); |
+ }, prints(isEmpty)); |
+ }); |
+ |
+ group("timeout:", () { |
+ test("a test times out after 30 seconds by default", () { |
+ new FakeAsync().run((async) { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ }).load(suite); |
+ |
+ 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, new isInstanceOf<TimeoutException>()); |
+ }]); |
+ |
+ liveTest.run(); |
+ async.elapse(new Duration(seconds: 30)); |
+ }); |
+ }); |
+ |
+ test("a test's custom timeout takes precedence", () { |
+ new FakeAsync().run((async) { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ }, metadata: new Metadata( |
+ timeout: new Timeout(new Duration(seconds: 15)))).load(suite); |
+ |
+ 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, new isInstanceOf<TimeoutException>()); |
+ }]); |
+ |
+ liveTest.run(); |
+ async.elapse(new Duration(seconds: 15)); |
+ }); |
+ }); |
+ |
+ test("a timeout factor is applied on top of the 30s default", () { |
+ new FakeAsync().run((async) { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ }, metadata: new Metadata(timeout: new Timeout.factor(0.5))) |
+ .load(suite); |
+ |
+ 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, new isInstanceOf<TimeoutException>()); |
+ }]); |
+ |
+ liveTest.run(); |
+ async.elapse(new Duration(seconds: 15)); |
+ }); |
+ }); |
+ |
+ test("a test with Timeout.none never times out", () { |
+ new FakeAsync().run((async) { |
+ var liveTest = _localTest(() { |
+ Invoker.current.addOutstandingCallback(); |
+ }, metadata: new Metadata(timeout: Timeout.none)).load(suite); |
+ |
+ expectStates(liveTest, [const State(Status.running, Result.success)]); |
+ |
+ liveTest.run(); |
+ async.elapse(new Duration(hours: 10)); |
+ expect(liveTest.state.status, equals(Status.running)); |
+ }); |
+ }); |
+ }); |
+ |
+ group("waitForOutstandingCallbacks:", () { |
+ test("waits for the wrapped function to complete", () async { |
+ var functionCompleted = false; |
+ await Invoker.current.waitForOutstandingCallbacks(() async { |
+ await pumpEventQueue(); |
+ functionCompleted = true; |
+ }); |
+ |
+ expect(functionCompleted, isTrue); |
+ }); |
+ |
+ test("waits for registered callbacks in the wrapped function to run", |
+ () async { |
+ var callbackRun = false; |
+ await Invoker.current.waitForOutstandingCallbacks(() { |
+ pumpEventQueue().then(expectAsync((_) { |
+ callbackRun = true; |
+ })); |
+ }); |
+ |
+ expect(callbackRun, isTrue); |
+ }); |
+ |
+ test("doesn't automatically block the enclosing context", () async { |
+ var innerFunctionCompleted = false; |
+ await Invoker.current.waitForOutstandingCallbacks(() { |
+ Invoker.current.waitForOutstandingCallbacks(() async { |
+ await pumpEventQueue(); |
+ innerFunctionCompleted = true; |
+ }); |
+ }); |
+ |
+ expect(innerFunctionCompleted, isFalse); |
+ }); |
+ }); |
+} |
+ |
+LocalTest _localTest(body(), {tearDown(), Metadata metadata}) { |
+ if (metadata == null) metadata = new Metadata(); |
+ return new LocalTest("test", metadata, body, tearDown: tearDown); |
+} |