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 |