| 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/backend/invoker.dart'; | |
| 9 import 'package:unittest/src/backend/live_test.dart'; | |
| 10 import 'package:unittest/src/backend/state.dart'; | |
| 11 import 'package:unittest/src/backend/suite.dart'; | |
| 12 import 'package:unittest/src/runner/isolate_test.dart'; | |
| 13 import 'package:unittest/src/runner/vm_listener.dart'; | |
| 14 import 'package:unittest/src/util/io.dart'; | |
| 15 import 'package:unittest/src/util/remote_exception.dart'; | |
| 16 import 'package:unittest/unittest.dart'; | |
| 17 | |
| 18 import 'utils.dart'; | |
| 19 | |
| 20 /// An isolate that's been spun up for the current test. | |
| 21 /// | |
| 22 /// This is tracked so that it can be killed once the test is done. | |
| 23 Isolate _isolate; | |
| 24 | |
| 25 /// A live test that's running for the current test. | |
| 26 /// | |
| 27 /// This is tracked so that it can be closed once the test is done. | |
| 28 LiveTest _liveTest; | |
| 29 | |
| 30 void main() { | |
| 31 tearDown(() { | |
| 32 if (_isolate != null && supportsIsolateKill) { | |
| 33 _isolate.kill(Isolate.IMMEDIATE); | |
| 34 } | |
| 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((response) { | |
| 45 expect(response, containsPair("type", "success")); | |
| 46 expect(response, contains("tests")); | |
| 47 | |
| 48 var tests = response["tests"]; | |
| 49 expect(tests, hasLength(3)); | |
| 50 expect(tests[0], containsPair("name", "successful 1")); | |
| 51 expect(tests[1], containsPair("name", "successful 2")); | |
| 52 expect(tests[2], containsPair("name", "successful 3")); | |
| 53 }); | |
| 54 }); | |
| 55 | |
| 56 test("sends an error response if loading fails", () { | |
| 57 return _spawnIsolate(_loadError).then((receivePort) { | |
| 58 return receivePort.first; | |
| 59 }).then((response) { | |
| 60 expect(response, containsPair("type", "error")); | |
| 61 expect(response, contains("error")); | |
| 62 | |
| 63 var error = RemoteException.deserialize(response["error"]).error; | |
| 64 expect(error.message, equals("oh no")); | |
| 65 expect(error.type, equals("String")); | |
| 66 }); | |
| 67 }); | |
| 68 | |
| 69 test("sends an error response on a NoSuchMethodError", () { | |
| 70 return _spawnIsolate(_noSuchMethodError).then((receivePort) { | |
| 71 return receivePort.first; | |
| 72 }).then((response) { | |
| 73 expect(response, containsPair("type", "loadException")); | |
| 74 expect(response, | |
| 75 containsPair("message", "No top-level main() function defined.")); | |
| 76 }); | |
| 77 }); | |
| 78 | |
| 79 test("sends an error response on non-function main", () { | |
| 80 return _spawnIsolate(_nonFunction).then((receivePort) { | |
| 81 return receivePort.first; | |
| 82 }).then((response) { | |
| 83 expect(response, containsPair("type", "loadException")); | |
| 84 expect(response, | |
| 85 containsPair("message", "Top-level main getter is not a function.")); | |
| 86 }); | |
| 87 }); | |
| 88 | |
| 89 test("sends an error response on wrong-arity main", () { | |
| 90 return _spawnIsolate(_wrongArity).then((receivePort) { | |
| 91 return receivePort.first; | |
| 92 }).then((response) { | |
| 93 expect(response, containsPair("type", "loadException")); | |
| 94 expect( | |
| 95 response, | |
| 96 containsPair( | |
| 97 "message", | |
| 98 "Top-level main() function takes arguments.")); | |
| 99 }); | |
| 100 }); | |
| 101 | |
| 102 group("in a successful test", () { | |
| 103 test("the state changes from pending to running to complete", () { | |
| 104 return _isolateTest(_successfulTests).then((liveTest) { | |
| 105 liveTest.onError.listen(expectAsync((_) {}, count: 0)); | |
| 106 | |
| 107 expect(liveTest.state, | |
| 108 equals(const State(Status.pending, Result.success))); | |
| 109 | |
| 110 var future = liveTest.run(); | |
| 111 | |
| 112 expect(liveTest.state, | |
| 113 equals(const State(Status.running, Result.success))); | |
| 114 | |
| 115 return future.then((_) { | |
| 116 expect(liveTest.state, | |
| 117 equals(const State(Status.complete, Result.success))); | |
| 118 }); | |
| 119 }); | |
| 120 }); | |
| 121 | |
| 122 test("onStateChange fires for each state change", () { | |
| 123 return _isolateTest(_successfulTests).then((liveTest) { | |
| 124 liveTest.onError.listen(expectAsync((_) {}, count: 0)); | |
| 125 | |
| 126 var first = true; | |
| 127 liveTest.onStateChange.listen(expectAsync((state) { | |
| 128 if (first) { | |
| 129 expect(state.status, equals(Status.running)); | |
| 130 first = false; | |
| 131 } else { | |
| 132 expect(state.status, equals(Status.complete)); | |
| 133 } | |
| 134 expect(state.result, equals(Result.success)); | |
| 135 }, count: 2, max: 2)); | |
| 136 | |
| 137 return liveTest.run(); | |
| 138 }); | |
| 139 }); | |
| 140 }); | |
| 141 | |
| 142 group("in a test with failures", () { | |
| 143 test("a failure reported causes the test to fail", () { | |
| 144 return _isolateTest(_failingTest).then((liveTest) { | |
| 145 expectSingleFailure(liveTest); | |
| 146 return liveTest.run(); | |
| 147 }); | |
| 148 }); | |
| 149 | |
| 150 test("a failure reported asynchronously after the test causes it to error", | |
| 151 () { | |
| 152 return _isolateTest(_failAfterSucceedTest).then((liveTest) { | |
| 153 expectStates(liveTest, [ | |
| 154 const State(Status.running, Result.success), | |
| 155 const State(Status.complete, Result.success), | |
| 156 const State(Status.complete, Result.failure), | |
| 157 const State(Status.complete, Result.error) | |
| 158 ]); | |
| 159 | |
| 160 expectErrors(liveTest, [(error) { | |
| 161 expect(lastState, | |
| 162 equals(const State(Status.complete, Result.failure))); | |
| 163 expect(error, isTestFailure("oh no")); | |
| 164 }, (error) { | |
| 165 expect(lastState, equals(const State(Status.complete, Result.error))); | |
| 166 expect(error, isRemoteException( | |
| 167 "This test failed after it had already completed. Make sure to " | |
| 168 "use [expectAsync]\n" | |
| 169 "or the [completes] matcher when testing async code.")); | |
| 170 }]); | |
| 171 | |
| 172 return liveTest.run(); | |
| 173 }); | |
| 174 }); | |
| 175 | |
| 176 test("multiple asynchronous failures are reported", () { | |
| 177 return _isolateTest(_multiFailTest).then((liveTest) { | |
| 178 expectStates(liveTest, [ | |
| 179 const State(Status.running, Result.success), | |
| 180 const State(Status.complete, Result.failure) | |
| 181 ]); | |
| 182 | |
| 183 expectErrors(liveTest, [(error) { | |
| 184 expect(lastState.status, equals(Status.complete)); | |
| 185 expect(error, isTestFailure("one")); | |
| 186 }, (error) { | |
| 187 expect(error, isTestFailure("two")); | |
| 188 }, (error) { | |
| 189 expect(error, isTestFailure("three")); | |
| 190 }, (error) { | |
| 191 expect(error, isTestFailure("four")); | |
| 192 }]); | |
| 193 | |
| 194 return liveTest.run(); | |
| 195 }); | |
| 196 }); | |
| 197 }); | |
| 198 | |
| 199 group("in a test with errors", () { | |
| 200 test("an error reported causes the test to error", () { | |
| 201 return _isolateTest(_errorTest).then((liveTest) { | |
| 202 expectStates(liveTest, [ | |
| 203 const State(Status.running, Result.success), | |
| 204 const State(Status.complete, Result.error) | |
| 205 ]); | |
| 206 | |
| 207 expectErrors(liveTest, [(error) { | |
| 208 expect(lastState.status, equals(Status.complete)); | |
| 209 expect(error, isRemoteException("oh no")); | |
| 210 }]); | |
| 211 | |
| 212 return liveTest.run(); | |
| 213 }); | |
| 214 }); | |
| 215 | |
| 216 test("an error reported asynchronously after the test causes it to error", | |
| 217 () { | |
| 218 return _isolateTest(_errorAfterSucceedTest).then((liveTest) { | |
| 219 expectStates(liveTest, [ | |
| 220 const State(Status.running, Result.success), | |
| 221 const State(Status.complete, Result.success), | |
| 222 const State(Status.complete, Result.error) | |
| 223 ]); | |
| 224 | |
| 225 expectErrors(liveTest, [(error) { | |
| 226 expect(lastState, | |
| 227 equals(const State(Status.complete, Result.error))); | |
| 228 expect(error, isRemoteException("oh no")); | |
| 229 }, (error) { | |
| 230 expect(error, isRemoteException( | |
| 231 "This test failed after it had already completed. Make sure to " | |
| 232 "use [expectAsync]\n" | |
| 233 "or the [completes] matcher when testing async code.")); | |
| 234 }]); | |
| 235 | |
| 236 return liveTest.run(); | |
| 237 }); | |
| 238 }); | |
| 239 | |
| 240 test("multiple asynchronous errors are reported", () { | |
| 241 return _isolateTest(_multiErrorTest).then((liveTest) { | |
| 242 expectStates(liveTest, [ | |
| 243 const State(Status.running, Result.success), | |
| 244 const State(Status.complete, Result.error) | |
| 245 ]); | |
| 246 | |
| 247 expectErrors(liveTest, [(error) { | |
| 248 expect(lastState.status, equals(Status.complete)); | |
| 249 expect(error, isRemoteException("one")); | |
| 250 }, (error) { | |
| 251 expect(error, isRemoteException("two")); | |
| 252 }, (error) { | |
| 253 expect(error, isRemoteException("three")); | |
| 254 }, (error) { | |
| 255 expect(error, isRemoteException("four")); | |
| 256 }]); | |
| 257 | |
| 258 return liveTest.run(); | |
| 259 }); | |
| 260 }); | |
| 261 }); | |
| 262 } | |
| 263 | |
| 264 /// Loads the first test defined in [entryPoint] in another isolate. | |
| 265 /// | |
| 266 /// This test will be automatically closed when the test is finished. | |
| 267 Future<LiveTest> _isolateTest(void entryPoint(SendPort sendPort)) { | |
| 268 return _spawnIsolate(entryPoint).then((receivePort) { | |
| 269 return receivePort.first; | |
| 270 }).then((response) { | |
| 271 expect(response, containsPair("type", "success")); | |
| 272 | |
| 273 var testMap = response["tests"].first; | |
| 274 var test = new IsolateTest(testMap["name"], testMap["sendPort"]); | |
| 275 var suite = new Suite("suite", [test]); | |
| 276 _liveTest = test.load(suite); | |
| 277 return _liveTest; | |
| 278 }); | |
| 279 } | |
| 280 | |
| 281 /// Spawns an isolate from [entryPoint], sends it a new [SendPort], and returns | |
| 282 /// the corresponding [ReceivePort]. | |
| 283 /// | |
| 284 /// This isolate will be automatically killed when the test is finished. | |
| 285 Future<ReceivePort> _spawnIsolate(void entryPoint(SendPort sendPort)) { | |
| 286 var receivePort = new ReceivePort(); | |
| 287 return Isolate.spawn(entryPoint, receivePort.sendPort).then((isolate) { | |
| 288 _isolate = isolate; | |
| 289 return receivePort; | |
| 290 }); | |
| 291 } | |
| 292 | |
| 293 /// An isolate entrypoint that throws immediately. | |
| 294 void _loadError(SendPort sendPort) => | |
| 295 VmListener.start(sendPort, () => () => throw 'oh no'); | |
| 296 | |
| 297 /// An isolate entrypoint that throws a NoSuchMethodError. | |
| 298 void _noSuchMethodError(SendPort sendPort) { | |
| 299 return VmListener.start(sendPort, () => | |
| 300 throw new NoSuchMethodError(null, #main, [], {})); | |
| 301 } | |
| 302 | |
| 303 /// An isolate entrypoint that returns a non-function. | |
| 304 void _nonFunction(SendPort sendPort) => | |
| 305 VmListener.start(sendPort, () => null); | |
| 306 | |
| 307 /// An isolate entrypoint that returns a function with the wrong arity. | |
| 308 void _wrongArity(SendPort sendPort) => | |
| 309 VmListener.start(sendPort, () => (_) {}); | |
| 310 | |
| 311 /// An isolate entrypoint that defines three tests that succeed. | |
| 312 void _successfulTests(SendPort sendPort) { | |
| 313 VmListener.start(sendPort, () => () { | |
| 314 test("successful 1", () {}); | |
| 315 test("successful 2", () {}); | |
| 316 test("successful 3", () {}); | |
| 317 }); | |
| 318 } | |
| 319 | |
| 320 /// An isolate entrypoint that defines a test that fails. | |
| 321 void _failingTest(SendPort sendPort) { | |
| 322 VmListener.start(sendPort, () => () { | |
| 323 test("failure", () => throw new TestFailure('oh no')); | |
| 324 }); | |
| 325 } | |
| 326 | |
| 327 /// An isolate entrypoint that defines a test that fails after succeeding. | |
| 328 void _failAfterSucceedTest(SendPort sendPort) { | |
| 329 VmListener.start(sendPort, () => () { | |
| 330 test("fail after succeed", () { | |
| 331 pumpEventQueue().then((_) { | |
| 332 throw new TestFailure('oh no'); | |
| 333 }); | |
| 334 }); | |
| 335 }); | |
| 336 } | |
| 337 | |
| 338 /// An isolate entrypoint that defines a test that fails multiple times. | |
| 339 void _multiFailTest(SendPort sendPort) { | |
| 340 VmListener.start(sendPort, () => () { | |
| 341 test("multiple failures", () { | |
| 342 Invoker.current.addOutstandingCallback(); | |
| 343 new Future(() => throw new TestFailure("one")); | |
| 344 new Future(() => throw new TestFailure("two")); | |
| 345 new Future(() => throw new TestFailure("three")); | |
| 346 new Future(() => throw new TestFailure("four")); | |
| 347 }); | |
| 348 }); | |
| 349 } | |
| 350 | |
| 351 /// An isolate entrypoint that defines a test that errors. | |
| 352 void _errorTest(SendPort sendPort) { | |
| 353 VmListener.start(sendPort, () => () { | |
| 354 test("error", () => throw 'oh no'); | |
| 355 }); | |
| 356 } | |
| 357 | |
| 358 /// An isolate entrypoint that defines a test that errors after succeeding. | |
| 359 void _errorAfterSucceedTest(SendPort sendPort) { | |
| 360 VmListener.start(sendPort, () => () { | |
| 361 test("error after succeed", () { | |
| 362 pumpEventQueue().then((_) => throw 'oh no'); | |
| 363 }); | |
| 364 }); | |
| 365 } | |
| 366 | |
| 367 /// An isolate entrypoint that defines a test that errors multiple times. | |
| 368 void _multiErrorTest(SendPort sendPort) { | |
| 369 VmListener.start(sendPort, () => () { | |
| 370 test("multiple errors", () { | |
| 371 Invoker.current.addOutstandingCallback(); | |
| 372 new Future(() => throw "one"); | |
| 373 new Future(() => throw "two"); | |
| 374 new Future(() => throw "three"); | |
| 375 new Future(() => throw "four"); | |
| 376 }); | |
| 377 }); | |
| 378 } | |
| OLD | NEW |