OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:async'; |
| 6 import 'dart:isolate'; |
| 7 |
| 8 import 'package:unittest/src/declarer.dart'; |
| 9 import 'package:unittest/src/invoker.dart'; |
| 10 import 'package:unittest/src/isolate_test.dart'; |
| 11 import 'package:unittest/src/live_test.dart'; |
| 12 import 'package:unittest/src/state.dart'; |
| 13 import 'package:unittest/src/suite.dart'; |
| 14 import 'package:unittest/src/vm_listener.dart'; |
| 15 import 'package:unittest/unittest.dart'; |
| 16 |
| 17 import 'utils.dart'; |
| 18 |
| 19 /// The current declarer. |
| 20 Declarer get _declarer => Zone.current[#unittest.declarer]; |
| 21 |
| 22 /// An isolate that's been spun up for the current test. |
| 23 /// |
| 24 /// This is tracked so that it can be killed once the test is done. |
| 25 Isolate _isolate; |
| 26 |
| 27 /// A live test that's running for the current test. |
| 28 /// |
| 29 /// This is tracked so that it can be closed once the test is done. |
| 30 LiveTest _liveTest; |
| 31 |
| 32 void main() { |
| 33 tearDown(() { |
| 34 if (_isolate != null) _isolate.kill(Isolate.IMMEDIATE); |
| 35 _isolate = null; |
| 36 |
| 37 if (_liveTest != null) _liveTest.close(); |
| 38 _liveTest = null; |
| 39 }); |
| 40 |
| 41 test("sends a list of available tests on startup", () { |
| 42 return _spawnIsolate(_successfulTests).then((receivePort) { |
| 43 return receivePort.first; |
| 44 }).then((tests) { |
| 45 expect(tests, hasLength(3)); |
| 46 expect(tests[0], containsPair("name", "successful 1")); |
| 47 expect(tests[1], containsPair("name", "successful 2")); |
| 48 expect(tests[2], containsPair("name", "successful 3")); |
| 49 }); |
| 50 }); |
| 51 |
| 52 group("in a successful test", () { |
| 53 test("the state changes from pending to running to complete", () { |
| 54 return _isolateTest(_successfulTests).then((liveTest) { |
| 55 liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
| 56 |
| 57 expect(liveTest.state, |
| 58 equals(const State(Status.pending, Result.success))); |
| 59 |
| 60 var future = liveTest.run(); |
| 61 |
| 62 expect(liveTest.state, |
| 63 equals(const State(Status.running, Result.success))); |
| 64 |
| 65 return future.then((_) { |
| 66 expect(liveTest.state, |
| 67 equals(const State(Status.complete, Result.success))); |
| 68 }); |
| 69 }); |
| 70 }); |
| 71 |
| 72 test("onStateChange fires for each state change", () { |
| 73 return _isolateTest(_successfulTests).then((liveTest) { |
| 74 liveTest.onError.listen(expectAsync((_) {}, count: 0)); |
| 75 |
| 76 var first = true; |
| 77 liveTest.onStateChange.listen(expectAsync((state) { |
| 78 if (first) { |
| 79 expect(state.status, equals(Status.running)); |
| 80 first = false; |
| 81 } else { |
| 82 expect(state.status, equals(Status.complete)); |
| 83 } |
| 84 expect(state.result, equals(Result.success)); |
| 85 }, count: 2, max: 2)); |
| 86 |
| 87 return liveTest.run(); |
| 88 }); |
| 89 }); |
| 90 }); |
| 91 |
| 92 group("in a test with failures", () { |
| 93 test("a failure reported causes the test to fail", () { |
| 94 return _isolateTest(_failingTest).then((liveTest) { |
| 95 expectSingleFailure(liveTest); |
| 96 return liveTest.run(); |
| 97 }); |
| 98 }); |
| 99 |
| 100 test("a failure reported asynchronously after the test causes it to error", |
| 101 () { |
| 102 return _isolateTest(_failAfterSucceedTest).then((liveTest) { |
| 103 expectStates(liveTest, [ |
| 104 const State(Status.running, Result.success), |
| 105 const State(Status.complete, Result.success), |
| 106 const State(Status.complete, Result.failure), |
| 107 const State(Status.complete, Result.error) |
| 108 ]); |
| 109 |
| 110 expectErrors(liveTest, [(error) { |
| 111 expect(lastState, |
| 112 equals(const State(Status.complete, Result.failure))); |
| 113 expect(error, isTestFailure("oh no")); |
| 114 }, (error) { |
| 115 expect(lastState, equals(const State(Status.complete, Result.error))); |
| 116 expect(error, isRemoteException( |
| 117 "This test failed after it had already completed. Make sure to " |
| 118 "use [expectAsync]\n" |
| 119 "or the [completes] matcher when testing async code.")); |
| 120 }]); |
| 121 |
| 122 return liveTest.run(); |
| 123 }); |
| 124 }); |
| 125 |
| 126 test("multiple asynchronous failures are reported", () { |
| 127 return _isolateTest(_multiFailTest).then((liveTest) { |
| 128 expectStates(liveTest, [ |
| 129 const State(Status.running, Result.success), |
| 130 const State(Status.complete, Result.failure) |
| 131 ]); |
| 132 |
| 133 expectErrors(liveTest, [(error) { |
| 134 expect(lastState.status, equals(Status.complete)); |
| 135 expect(error, isTestFailure("one")); |
| 136 }, (error) { |
| 137 expect(error, isTestFailure("two")); |
| 138 }, (error) { |
| 139 expect(error, isTestFailure("three")); |
| 140 }, (error) { |
| 141 expect(error, isTestFailure("four")); |
| 142 }]); |
| 143 |
| 144 return liveTest.run(); |
| 145 }); |
| 146 }); |
| 147 }); |
| 148 |
| 149 group("in a test with errors", () { |
| 150 test("an error reported causes the test to error", () { |
| 151 return _isolateTest(_errorTest).then((liveTest) { |
| 152 expectStates(liveTest, [ |
| 153 const State(Status.running, Result.success), |
| 154 const State(Status.complete, Result.error) |
| 155 ]); |
| 156 |
| 157 expectErrors(liveTest, [(error) { |
| 158 expect(lastState.status, equals(Status.complete)); |
| 159 expect(error, isRemoteException("oh no")); |
| 160 }]); |
| 161 |
| 162 return liveTest.run(); |
| 163 }); |
| 164 }); |
| 165 |
| 166 test("an error reported asynchronously after the test causes it to error", |
| 167 () { |
| 168 return _isolateTest(_errorAfterSucceedTest).then((liveTest) { |
| 169 expectStates(liveTest, [ |
| 170 const State(Status.running, Result.success), |
| 171 const State(Status.complete, Result.success), |
| 172 const State(Status.complete, Result.error) |
| 173 ]); |
| 174 |
| 175 expectErrors(liveTest, [(error) { |
| 176 expect(lastState, |
| 177 equals(const State(Status.complete, Result.error))); |
| 178 expect(error, isRemoteException("oh no")); |
| 179 }, (error) { |
| 180 expect(error, isRemoteException( |
| 181 "This test failed after it had already completed. Make sure to " |
| 182 "use [expectAsync]\n" |
| 183 "or the [completes] matcher when testing async code.")); |
| 184 }]); |
| 185 |
| 186 return liveTest.run(); |
| 187 }); |
| 188 }); |
| 189 |
| 190 test("multiple asynchronous errors are reported", () { |
| 191 return _isolateTest(_multiErrorTest).then((liveTest) { |
| 192 expectStates(liveTest, [ |
| 193 const State(Status.running, Result.success), |
| 194 const State(Status.complete, Result.error) |
| 195 ]); |
| 196 |
| 197 expectErrors(liveTest, [(error) { |
| 198 expect(lastState.status, equals(Status.complete)); |
| 199 expect(error, isRemoteException("one")); |
| 200 }, (error) { |
| 201 expect(error, isRemoteException("two")); |
| 202 }, (error) { |
| 203 expect(error, isRemoteException("three")); |
| 204 }, (error) { |
| 205 expect(error, isRemoteException("four")); |
| 206 }]); |
| 207 |
| 208 return liveTest.run(); |
| 209 }); |
| 210 }); |
| 211 }); |
| 212 } |
| 213 |
| 214 /// Loads the first test defined in [entryPoint] in another isolate. |
| 215 /// |
| 216 /// This test will be automatically closed when the test is finished. |
| 217 Future<LiveTest> _isolateTest(void entryPoint(SendPort sendPort)) { |
| 218 return _spawnIsolate(entryPoint).then((receivePort) { |
| 219 return receivePort.first; |
| 220 }).then((response) { |
| 221 var testMap = response.first; |
| 222 var test = new IsolateTest(testMap["name"], testMap["sendPort"]); |
| 223 var suite = new Suite("suite", [test]); |
| 224 _liveTest = test.load(suite); |
| 225 return _liveTest; |
| 226 }); |
| 227 } |
| 228 |
| 229 /// Spawns an isolate from [entryPoint], sends it a new [SendPort], and returns |
| 230 /// the corresponding [ReceivePort]. |
| 231 /// |
| 232 /// This isolate will be automatically killed when the test is finished. |
| 233 Future<ReceivePort> _spawnIsolate(void entryPoint(SendPort sendPort)) { |
| 234 var receivePort = new ReceivePort(); |
| 235 return Isolate.spawn(entryPoint, receivePort.sendPort).then((isolate) { |
| 236 _isolate = isolate; |
| 237 return receivePort; |
| 238 }); |
| 239 } |
| 240 |
| 241 /// An isolate entrypoint that defines three tests that succeed. |
| 242 void _successfulTests(SendPort sendPort) { |
| 243 VmListener.start(sendPort, () { |
| 244 _declarer.test("successful 1", () {}); |
| 245 _declarer.test("successful 2", () {}); |
| 246 _declarer.test("successful 3", () {}); |
| 247 }); |
| 248 } |
| 249 |
| 250 /// An isolate entrypoint that defines a test that fails. |
| 251 void _failingTest(SendPort sendPort) { |
| 252 VmListener.start(sendPort, () { |
| 253 _declarer.test("failure", () => throw new TestFailure('oh no')); |
| 254 }); |
| 255 } |
| 256 |
| 257 /// An isolate entrypoint that defines a test that fails after succeeding. |
| 258 void _failAfterSucceedTest(SendPort sendPort) { |
| 259 VmListener.start(sendPort, () { |
| 260 _declarer.test("fail after succeed", () { |
| 261 pumpEventQueue().then((_) { |
| 262 throw new TestFailure('oh no'); |
| 263 }); |
| 264 }); |
| 265 }); |
| 266 } |
| 267 |
| 268 /// An isolate entrypoint that defines a test that fails multiple times. |
| 269 void _multiFailTest(SendPort sendPort) { |
| 270 VmListener.start(sendPort, () { |
| 271 _declarer.test("multiple failures", () { |
| 272 Invoker.current.addOutstandingCallback(); |
| 273 new Future(() => throw new TestFailure("one")); |
| 274 new Future(() => throw new TestFailure("two")); |
| 275 new Future(() => throw new TestFailure("three")); |
| 276 new Future(() => throw new TestFailure("four")); |
| 277 }); |
| 278 }); |
| 279 } |
| 280 |
| 281 /// An isolate entrypoint that defines a test that errors. |
| 282 void _errorTest(SendPort sendPort) { |
| 283 VmListener.start(sendPort, () { |
| 284 _declarer.test("error", () => throw 'oh no'); |
| 285 }); |
| 286 } |
| 287 |
| 288 /// An isolate entrypoint that defines a test that errors after succeeding. |
| 289 void _errorAfterSucceedTest(SendPort sendPort) { |
| 290 VmListener.start(sendPort, () { |
| 291 _declarer.test("error after succeed", () { |
| 292 pumpEventQueue().then((_) => throw 'oh no'); |
| 293 }); |
| 294 }); |
| 295 } |
| 296 |
| 297 /// An isolate entrypoint that defines a test that errors multiple times. |
| 298 void _multiErrorTest(SendPort sendPort) { |
| 299 VmListener.start(sendPort, () { |
| 300 _declarer.test("multiple errors", () { |
| 301 Invoker.current.addOutstandingCallback(); |
| 302 new Future(() => throw "one"); |
| 303 new Future(() => throw "two"); |
| 304 new Future(() => throw "three"); |
| 305 new Future(() => throw "four"); |
| 306 }); |
| 307 }); |
| 308 } |
OLD | NEW |