OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 /** | |
6 * A library for writing dart unit tests. | |
7 * | |
8 * To import this library, use the pub package manager. | |
9 * Create a pubspec.yaml file in your project and add | |
10 * a dependency on unittest with the following lines: | |
11 * dependencies: | |
12 * unittest: | |
13 * sdk: unittest | |
14 * | |
15 * Then run 'pub install' from your project directory or using | |
16 * the DartEditor. | |
17 * | |
18 * Please see [Pub Getting Started](http://pub.dartlang.org/doc) | |
19 * for more details about the pub package manager. | |
20 * | |
21 * ##Concepts## | |
22 * | |
23 * * Tests: Tests are specified via the top-level function [test], they can be | |
24 * organized together using [group]. | |
25 * * Checks: Test expectations can be specified via [expect] | |
26 * * Matchers: [expect] assertions are written declaratively using [Matcher]s | |
27 * * Configuration: The framework can be adapted by calling [configure] with a | |
28 * [Configuration]. Common configurations can be found in this package | |
29 * under: 'dom\_config.dart' (deprecated), 'html\_config.dart' (for running | |
30 * tests compiled to Javascript in a browser), and 'vm\_config.dart' (for | |
31 * running native Dart tests on the VM). | |
32 * | |
33 * ##Examples## | |
34 * | |
35 * A trivial test: | |
36 * | |
37 * import 'package:unittest/unittest.dart'; | |
38 * main() { | |
39 * test('this is a test', () { | |
40 * int x = 2 + 3; | |
41 * expect(x, equals(5)); | |
42 * }); | |
43 * } | |
44 * | |
45 * Multiple tests: | |
46 * | |
47 * import 'package:unittest/unittest.dart'; | |
48 * main() { | |
49 * test('this is a test', () { | |
50 * int x = 2 + 3; | |
51 * expect(x, equals(5)); | |
52 * }); | |
53 * test('this is another test', () { | |
54 * int x = 2 + 3; | |
55 * expect(x, equals(5)); | |
56 * }); | |
57 * } | |
58 * | |
59 * Multiple tests, grouped by category: | |
60 * | |
61 * import 'package:unittest/unittest.dart'; | |
62 * main() { | |
63 * group('group A', () { | |
64 * test('test A.1', () { | |
65 * int x = 2 + 3; | |
66 * expect(x, equals(5)); | |
67 * }); | |
68 * test('test A.2', () { | |
69 * int x = 2 + 3; | |
70 * expect(x, equals(5)); | |
71 * }); | |
72 * }); | |
73 * group('group B', () { | |
74 * test('this B.1', () { | |
75 * int x = 2 + 3; | |
76 * expect(x, equals(5)); | |
77 * }); | |
78 * }); | |
79 * } | |
80 * | |
81 * Asynchronous tests: if callbacks expect between 0 and 2 positional arguments, | |
82 * depending on the suffix of expectAsyncX(). expectAsyncX() will wrap a | |
83 * function into a new callback and will not consider the test complete until | |
84 * that callback is run. A count argument can be provided to specify the number | |
85 * of times the callback should be called (the default is 1). | |
86 * | |
87 * import 'package:unittest/unittest.dart'; | |
88 * import 'dart:isolate'; | |
89 * main() { | |
90 * test('callback is executed once', () { | |
91 * // wrap the callback of an asynchronous call with [expectAsync0] if | |
92 * // the callback takes 0 arguments... | |
93 * var timer = new Timer(0, (_) => expectAsync0(() { | |
94 * int x = 2 + 3; | |
95 * expect(x, equals(5)); | |
96 * })); | |
97 * }); | |
98 * | |
99 * test('callback is executed twice', () { | |
100 * var callback = (_) => expectAsync0(() { | |
101 * int x = 2 + 3; | |
102 * expect(x, equals(5)); | |
103 * }, count: 2); // <-- we can indicate multiplicity to [expectAsync0] | |
104 * new Timer(0, callback); | |
105 * new Timer(0, callback); | |
106 * }); | |
107 * } | |
108 * | |
109 * expectAsyncX() will wrap the callback code in a try/catch handler to handle | |
110 * exceptions (treated as test failures). There may be times when the number of | |
111 * times a callback should be called is non-deterministic. In this case a dummy | |
112 * callback can be created with expectAsync0((){}) and this can be called from | |
113 * the real callback when it is finally complete. In this case the body of the | |
114 * callback should be protected within a call to guardAsync(); this will ensure | |
115 * that exceptions are properly handled. | |
116 * | |
117 * Note: due to some language limitations we have to use different functions | |
118 * depending on the number of positional arguments of the callback. In the | |
119 * future, we plan to expose a single `expectAsync` function that can be used | |
120 * regardless of the number of positional arguments. This requires new langauge | |
121 * features or fixes to the current spec (e.g. see | |
122 * [Issue 2706](http://dartbug.com/2706)). | |
123 * | |
124 * Meanwhile, we plan to add this alternative API for callbacks of more than 2 | |
125 * arguments or that take named parameters. (this is not implemented yet, | |
126 * but will be coming here soon). | |
127 * | |
128 * import 'package:unittest/unittest.dart'; | |
129 * import 'dart:isolate'; | |
130 * main() { | |
131 * test('callback is executed', () { | |
132 * // indicate ahead of time that an async callback is expected. | |
133 * var async = startAsync(); | |
134 * new Timer(0, (_) { | |
135 * // Guard the body of the callback, so errors are propagated | |
136 * // correctly. | |
137 * guardAsync(() { | |
138 * int x = 2 + 3; | |
139 * expect(x, equals(5)); | |
140 * }); | |
141 * // indicate that the asynchronous callback was invoked. | |
142 * async.complete(); | |
143 * }); | |
144 * }); | |
145 * } | |
146 * | |
147 */ | |
148 library unittest; | |
149 | |
150 import 'dart:isolate'; | |
151 | |
152 part 'collection_matchers.dart'; | |
153 part 'config.dart'; | |
154 part 'core_matchers.dart'; | |
155 part 'description.dart'; | |
156 part 'expect.dart'; | |
157 part 'future_matchers.dart'; | |
158 part 'interfaces.dart'; | |
159 part 'map_matchers.dart'; | |
160 part 'matcher.dart'; | |
161 part 'mock.dart'; | |
162 part 'numeric_matchers.dart'; | |
163 part 'operator_matchers.dart'; | |
164 part 'string_matchers.dart'; | |
165 part 'test_case.dart'; | |
166 | |
167 /** [Configuration] used by the unittest library. */ | |
168 Configuration _config = null; | |
169 | |
170 Configuration get config => _config; | |
171 | |
172 /** | |
173 * Set the [Configuration] used by the unittest library. Returns any | |
174 * previous configuration. | |
175 * TODO: consider deprecating in favor of a setter now we have a getter. | |
176 */ | |
177 Configuration configure(Configuration config) { | |
178 Configuration _oldConfig = _config; | |
179 _config = config; | |
180 return _oldConfig; | |
181 } | |
182 | |
183 void logMessage(String message) => _config.logMessage(message); | |
184 | |
185 /** | |
186 * Description text of the current test group. If multiple groups are nested, | |
187 * this will contain all of their text concatenated. | |
188 */ | |
189 String _currentGroup = ''; | |
190 | |
191 /** Separator used between group names and test names. */ | |
192 String groupSep = ' '; | |
193 | |
194 /** Tests executed in this suite. */ | |
195 List<TestCase> _tests; | |
196 | |
197 /** Get the list of tests. */ | |
198 get testCases => _tests; | |
199 | |
200 /** | |
201 * Callback used to run tests. Entrypoints can replace this with their own | |
202 * if they want. | |
203 */ | |
204 Function _testRunner; | |
205 | |
206 /** Setup function called before each test in a group */ | |
207 Function _testSetup; | |
208 | |
209 /** Teardown function called after each test in a group */ | |
210 Function _testTeardown; | |
211 | |
212 /** Current test being executed. */ | |
213 int _currentTest = 0; | |
214 | |
215 /** Whether the framework is in an initialized state. */ | |
216 bool _initialized = false; | |
217 | |
218 String _uncaughtErrorMessage = null; | |
219 | |
220 /** Test case result strings. */ | |
221 // TODO(gram) we should change these constants to use a different string | |
222 // (so that writing 'FAIL' in the middle of a test doesn't | |
223 // imply that the test fails). We can't do it without also changing | |
224 // the testrunner and test.dart though. | |
225 const PASS = 'pass'; | |
226 const FAIL = 'fail'; | |
227 const ERROR = 'error'; | |
228 | |
229 /** If set, then all other test cases will be ignored. */ | |
230 TestCase _soloTest; | |
231 | |
232 /** | |
233 * A map that can be used to communicate state between a test driver | |
234 * or main() function and the tests, particularly when these two | |
235 * are otherwise independent. For example, a test driver that starts | |
236 * an HTTP server and then runs tests that access that server could use | |
237 * this as a way of communicating the server port to the tests. | |
238 */ | |
239 Map testState = {}; | |
240 | |
241 /** | |
242 * (Deprecated) Evaluates the [function] and validates that it throws an | |
243 * exception. If [callback] is provided, then it will be invoked with the | |
244 * thrown exception. The callback may do any validation it wants. In addition, | |
245 * if it returns `false`, that also indicates an expectation failure. | |
246 */ | |
247 void expectThrow(function, [bool callback(exception)]) { | |
248 bool threw = false; | |
249 try { | |
250 function(); | |
251 } catch (e) { | |
252 threw = true; | |
253 | |
254 // Also let the callback look at it. | |
255 if (callback != null) { | |
256 var result = callback(e); | |
257 | |
258 // If the callback explicitly returned false, treat that like an | |
259 // expectation too. (If it returns null, though, don't.) | |
260 if (result == false) { | |
261 fail('Exception:\n$e\ndid not match expectation.'); | |
262 } | |
263 } | |
264 } | |
265 | |
266 if (threw != true) fail('An expected exception was not thrown.'); | |
267 } | |
268 | |
269 /** | |
270 * Creates a new test case with the given description and body. The | |
271 * description will include the descriptions of any surrounding group() | |
272 * calls. | |
273 */ | |
274 void test(String spec, TestFunction body) { | |
275 ensureInitialized(); | |
276 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); | |
277 } | |
278 | |
279 /** | |
280 * (Deprecated) Creates a new async test case with the given description | |
281 * and body. The description will include the descriptions of any surrounding | |
282 * group() calls. | |
283 */ | |
284 // TODO(sigmund): deprecate this API | |
285 void asyncTest(String spec, int callbacks, TestFunction body) { | |
286 ensureInitialized(); | |
287 | |
288 final testCase = new TestCase( | |
289 _tests.length + 1, _fullSpec(spec), body, callbacks); | |
290 _tests.add(testCase); | |
291 | |
292 if (callbacks < 1) { | |
293 testCase.error( | |
294 'Async tests must wait for at least one callback ', ''); | |
295 } | |
296 } | |
297 | |
298 /** | |
299 * Creates a new test case with the given description and body. The | |
300 * description will include the descriptions of any surrounding group() | |
301 * calls. | |
302 * | |
303 * "solo_" means that this will be the only test that is run. All other tests | |
304 * will be skipped. This is a convenience function to let you quickly isolate | |
305 * a single test by adding "solo_" before it to temporarily disable all other | |
306 * tests. | |
307 */ | |
308 void solo_test(String spec, TestFunction body) { | |
309 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, | |
310 // all of the solo-ed tests and none of the non-solo-ed ones should run. | |
311 if (_soloTest != null) { | |
312 throw new Exception('Only one test can be soloed right now.'); | |
313 } | |
314 | |
315 ensureInitialized(); | |
316 | |
317 _soloTest = new TestCase(_tests.length + 1, _fullSpec(spec), body, 0); | |
318 _tests.add(_soloTest); | |
319 } | |
320 | |
321 /** Sentinel value for [_SpreadArgsHelper]. */ | |
322 class _Sentinel { | |
323 const _Sentinel(); | |
324 } | |
325 | |
326 /** Simulates spread arguments using named arguments. */ | |
327 // TODO(sigmund): remove this class and simply use a closure with named | |
328 // arguments (if still applicable). | |
329 class _SpreadArgsHelper { | |
330 Function _callback; | |
331 int _expectedCalls; | |
332 int _actualCalls = 0; | |
333 int _testNum; | |
334 TestCase _testCase; | |
335 Function _shouldCallBack; | |
336 Function _isDone; | |
337 static const _sentinel = const _Sentinel(); | |
338 | |
339 _init(Function callback, Function shouldCallBack, Function isDone, | |
340 [expectedCalls = 0]) { | |
341 ensureInitialized(); | |
342 if (!(_currentTest >= 0 && | |
343 _currentTest < _tests.length && | |
344 _tests[_currentTest] != null)) { | |
345 print("No valid test, did you forget to run your test inside a call " | |
346 "to test()?"); | |
347 } | |
348 assert(_currentTest >= 0 && | |
349 _currentTest < _tests.length && | |
350 _tests[_currentTest] != null); | |
351 _callback = callback; | |
352 _shouldCallBack = shouldCallBack; | |
353 _isDone = isDone; | |
354 _expectedCalls = expectedCalls; | |
355 _testNum = _currentTest; | |
356 _testCase = _tests[_currentTest]; | |
357 if (expectedCalls > 0) { | |
358 _testCase.callbackFunctionsOutstanding++; | |
359 } | |
360 } | |
361 | |
362 _SpreadArgsHelper(callback, shouldCallBack, isDone) { | |
363 _init(callback, shouldCallBack, isDone); | |
364 } | |
365 | |
366 _SpreadArgsHelper.fixedCallCount(callback, expectedCalls) { | |
367 _init(callback, _checkCallCount, _allCallsDone, expectedCalls); | |
368 } | |
369 | |
370 _SpreadArgsHelper.variableCallCount(callback, isDone) { | |
371 _init(callback, _always, isDone, 1); | |
372 } | |
373 | |
374 _SpreadArgsHelper.optionalCalls(callback) { | |
375 _init(callback, _always, () => false, 0); | |
376 } | |
377 | |
378 _after() { | |
379 if (_isDone()) { | |
380 _handleCallbackFunctionComplete(); | |
381 } | |
382 } | |
383 | |
384 _allCallsDone() => _actualCalls == _expectedCalls; | |
385 | |
386 _always() { | |
387 // Always run except if the test is done. | |
388 if (_testCase.isComplete) { | |
389 _testCase.error( | |
390 'Callback called after already being marked as done ($_actualCalls).', | |
391 ''); | |
392 return false; | |
393 } else { | |
394 return true; | |
395 } | |
396 } | |
397 | |
398 invoke([arg0 = _sentinel, arg1 = _sentinel, arg2 = _sentinel, | |
399 arg3 = _sentinel, arg4 = _sentinel]) { | |
400 return guardAsync(() { | |
401 ++_actualCalls; | |
402 if (!_shouldCallBack()) { | |
403 return; | |
404 } else if (arg0 == _sentinel) { | |
405 return _callback(); | |
406 } else if (arg1 == _sentinel) { | |
407 return _callback(arg0); | |
408 } else if (arg2 == _sentinel) { | |
409 return _callback(arg0, arg1); | |
410 } else if (arg3 == _sentinel) { | |
411 return _callback(arg0, arg1, arg2); | |
412 } else if (arg4 == _sentinel) { | |
413 return _callback(arg0, arg1, arg2, arg3); | |
414 } else { | |
415 _testCase.error( | |
416 'unittest lib does not support callbacks with more than' | |
417 ' 4 arguments.', | |
418 ''); | |
419 } | |
420 }, | |
421 _after, _testNum); | |
422 } | |
423 | |
424 invoke0() { | |
425 return guardAsync( | |
426 () { | |
427 ++_actualCalls; | |
428 if (_shouldCallBack()) { | |
429 return _callback(); | |
430 } | |
431 }, | |
432 _after, _testNum); | |
433 } | |
434 | |
435 invoke1(arg1) { | |
436 return guardAsync( | |
437 () { | |
438 ++_actualCalls; | |
439 if (_shouldCallBack()) { | |
440 return _callback(arg1); | |
441 } | |
442 }, | |
443 _after, _testNum); | |
444 } | |
445 | |
446 invoke2(arg1, arg2) { | |
447 return guardAsync( | |
448 () { | |
449 ++_actualCalls; | |
450 if (_shouldCallBack()) { | |
451 return _callback(arg1, arg2); | |
452 } | |
453 }, | |
454 _after, _testNum); | |
455 } | |
456 | |
457 /** Returns false if we exceded the number of expected calls. */ | |
458 bool _checkCallCount() { | |
459 if (_actualCalls > _expectedCalls) { | |
460 _testCase.error('Callback called more times than expected ' | |
461 '($_actualCalls > $_expectedCalls).', ''); | |
462 return false; | |
463 } | |
464 return true; | |
465 } | |
466 } | |
467 | |
468 /** | |
469 * Indicate that [callback] is expected to be called a [count] number of times | |
470 * (by default 1). The unittest framework will wait for the callback to run the | |
471 * specified [count] times before it continues with the following test. Using | |
472 * [_expectAsync] will also ensure that errors that occur within [callback] are | |
473 * tracked and reported. [callback] should take between 0 and 4 positional | |
474 * arguments (named arguments are not supported here). | |
475 */ | |
476 Function _expectAsync(Function callback, [int count = 1]) { | |
477 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke; | |
478 } | |
479 | |
480 /** | |
481 * Indicate that [callback] is expected to be called a [count] number of times | |
482 * (by default 1). The unittest framework will wait for the callback to run the | |
483 * specified [count] times before it continues with the following test. Using | |
484 * [expectAsync0] will also ensure that errors that occur within [callback] are | |
485 * tracked and reported. [callback] should take 0 positional arguments (named | |
486 * arguments are not supported). | |
487 */ | |
488 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
489 Function expectAsync0(Function callback, [int count = 1]) { | |
490 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke0; | |
491 } | |
492 | |
493 /** Like [expectAsync0] but [callback] should take 1 positional argument. */ | |
494 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
495 Function expectAsync1(Function callback, {int count: 1}) { | |
496 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke1; | |
497 } | |
498 | |
499 /** Like [expectAsync0] but [callback] should take 2 positional arguments. */ | |
500 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
501 Function expectAsync2(Function callback, [int count = 1]) { | |
502 return new _SpreadArgsHelper.fixedCallCount(callback, count).invoke2; | |
503 } | |
504 | |
505 /** | |
506 * Indicate that [callback] is expected to be called until [isDone] returns | |
507 * true. The unittest framework checks [isDone] after each callback and only | |
508 * when it returns true will it continue with the following test. Using | |
509 * [expectAsyncUntil] will also ensure that errors that occur within | |
510 * [callback] are tracked and reported. [callback] should take between 0 and | |
511 * 4 positional arguments (named arguments are not supported). | |
512 */ | |
513 Function _expectAsyncUntil(Function callback, Function isDone) { | |
514 return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke; | |
515 } | |
516 | |
517 /** | |
518 * Indicate that [callback] is expected to be called until [isDone] returns | |
519 * true. The unittest framework check [isDone] after each callback and only | |
520 * when it returns true will it continue with the following test. Using | |
521 * [expectAsyncUntil0] will also ensure that errors that occur within | |
522 * [callback] are tracked and reported. [callback] should take 0 positional | |
523 * arguments (named arguments are not supported). | |
524 */ | |
525 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
526 Function expectAsyncUntil0(Function callback, Function isDone) { | |
527 return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke0; | |
528 } | |
529 | |
530 /** | |
531 * Like [expectAsyncUntil0] but [callback] should take 1 positional argument. | |
532 */ | |
533 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
534 Function expectAsyncUntil1(Function callback, Function isDone) { | |
535 return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke1; | |
536 } | |
537 | |
538 /** | |
539 * Like [expectAsyncUntil0] but [callback] should take 2 positional arguments. | |
540 */ | |
541 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
542 Function expectAsyncUntil2(Function callback, Function isDone) { | |
543 return new _SpreadArgsHelper.variableCallCount(callback, isDone).invoke2; | |
544 } | |
545 | |
546 /** | |
547 * Wraps the [callback] in a new function and returns that function. The new | |
548 * function will be able to handle exceptions by directing them to the correct | |
549 * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that | |
550 * might optionally be called but may never be called during the test. | |
551 * [callback] should take between 0 and 4 positional arguments (named arguments | |
552 * are not supported). | |
553 */ | |
554 Function _protectAsync(Function callback) { | |
555 return new _SpreadArgsHelper.optionalCalls(callback).invoke; | |
556 } | |
557 | |
558 /** | |
559 * Wraps the [callback] in a new function and returns that function. The new | |
560 * function will be able to handle exceptions by directing them to the correct | |
561 * test. This is thus similar to expectAsync0. Use it to wrap any callbacks that | |
562 * might optionally be called but may never be called during the test. | |
563 * [callback] should take 0 positional arguments (named arguments are not | |
564 * supported). | |
565 */ | |
566 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
567 Function protectAsync0(Function callback) { | |
568 return new _SpreadArgsHelper.optionalCalls(callback).invoke0; | |
569 } | |
570 | |
571 /** | |
572 * Like [protectAsync0] but [callback] should take 1 positional argument. | |
573 */ | |
574 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
575 Function protectAsync1(Function callback) { | |
576 return new _SpreadArgsHelper.optionalCalls(callback).invoke1; | |
577 } | |
578 | |
579 /** | |
580 * Like [protectAsync0] but [callback] should take 2 positional arguments. | |
581 */ | |
582 // TODO(sigmund): deprecate this API when issue 2706 is fixed. | |
583 Function protectAsync2(Function callback) { | |
584 return new _SpreadArgsHelper.optionalCalls(callback).invoke2; | |
585 } | |
586 | |
587 /** | |
588 * Creates a new named group of tests. Calls to group() or test() within the | |
589 * body of the function passed to this will inherit this group's description. | |
590 */ | |
591 void group(String description, void body()) { | |
592 ensureInitialized(); | |
593 // Concatenate the new group. | |
594 final parentGroup = _currentGroup; | |
595 if (_currentGroup != '') { | |
596 // Add a space. | |
597 _currentGroup = '$_currentGroup$groupSep$description'; | |
598 } else { | |
599 // The first group. | |
600 _currentGroup = description; | |
601 } | |
602 | |
603 // Groups can be nested, so we need to preserve the current | |
604 // settings for test setup/teardown. | |
605 Function parentSetup = _testSetup; | |
606 Function parentTeardown = _testTeardown; | |
607 | |
608 try { | |
609 _testSetup = null; | |
610 _testTeardown = null; | |
611 body(); | |
612 } catch (e, trace) { | |
613 var stack = (trace == null) ? '' : ': ${trace.toString()}'; | |
614 _uncaughtErrorMessage = "${e.toString()}$stack"; | |
615 } finally { | |
616 // Now that the group is over, restore the previous one. | |
617 _currentGroup = parentGroup; | |
618 _testSetup = parentSetup; | |
619 _testTeardown = parentTeardown; | |
620 } | |
621 } | |
622 | |
623 /** | |
624 * Register a [setUp] function for a test [group]. This function will | |
625 * be called before each test in the group is run. Note that if groups | |
626 * are nested only the most locally scoped [setUp] function will be run. | |
627 * [setUp] and [tearDown] should be called within the [group] before any | |
628 * calls to [test]. | |
629 */ | |
630 void setUp(Function setupTest) { | |
631 _testSetup = setupTest; | |
632 } | |
633 | |
634 /** | |
635 * Register a [tearDown] function for a test [group]. This function will | |
636 * be called after each test in the group is run. Note that if groups | |
637 * are nested only the most locally scoped [tearDown] function will be run. | |
638 * [setUp] and [tearDown] should be called within the [group] before any | |
639 * calls to [test]. | |
640 */ | |
641 void tearDown(Function teardownTest) { | |
642 _testTeardown = teardownTest; | |
643 } | |
644 | |
645 /** | |
646 * Called when one of the callback functions is done with all expected | |
647 * calls. | |
648 */ | |
649 void _handleCallbackFunctionComplete() { | |
650 // TODO (gram): we defer this to give the nextBatch recursive | |
651 // stack a chance to unwind. This is a temporary hack but | |
652 // really a bunch of code here needs to be fixed. We have a | |
653 // single array that is being iterated through by nextBatch(), | |
654 // which is recursively invoked in the case of async tests that | |
655 // run synchronously. Bad things can then happen. | |
656 _defer(() { | |
657 if (_currentTest < _tests.length) { | |
658 final testCase = _tests[_currentTest]; | |
659 --testCase.callbackFunctionsOutstanding; | |
660 if (testCase.callbackFunctionsOutstanding < 0) { | |
661 // TODO(gram): Check: Can this even happen? | |
662 testCase.error( | |
663 'More calls to _handleCallbackFunctionComplete() than expected.', | |
664 ''); | |
665 } else if (testCase.callbackFunctionsOutstanding == 0) { | |
666 if (!testCase.isComplete) { | |
667 testCase.pass(); | |
668 } | |
669 _nextTestCase(); | |
670 } | |
671 } | |
672 }); | |
673 } | |
674 | |
675 /** Advance to the next test case. */ | |
676 void _nextTestCase() { | |
677 _currentTest++; | |
678 _testRunner(); | |
679 } | |
680 | |
681 /** | |
682 * Temporary hack: expose old API. | |
683 * TODO(gram) remove this when WebKit tests are working with new framework | |
684 */ | |
685 void callbackDone() { | |
686 _handleCallbackFunctionComplete(); | |
687 } | |
688 | |
689 /** | |
690 * Utility function that can be used to notify the test framework that an | |
691 * error was caught outside of this library. | |
692 */ | |
693 void _reportTestError(String msg, String trace) { | |
694 if (_currentTest < _tests.length) { | |
695 final testCase = _tests[_currentTest]; | |
696 testCase.error(msg, trace); | |
697 if (testCase.callbackFunctionsOutstanding > 0) { | |
698 _nextTestCase(); | |
699 } | |
700 } else { | |
701 _uncaughtErrorMessage = "$msg: $trace"; | |
702 } | |
703 } | |
704 | |
705 /** Runs [callback] at the end of the event loop. */ | |
706 _defer(void callback()) { | |
707 // Exploit isolate ports as a platform-independent mechanism to queue a | |
708 // message at the end of the event loop. | |
709 // TODO(sigmund): expose this functionality somewhere in our libraries. | |
710 final port = new ReceivePort(); | |
711 port.receive((msg, reply) { | |
712 callback(); | |
713 port.close(); | |
714 }); | |
715 port.toSendPort().send(null, null); | |
716 } | |
717 | |
718 rerunTests() { | |
719 _uncaughtErrorMessage = null; | |
720 _initialized = true; // We don't want to reset the test array. | |
721 runTests(); | |
722 } | |
723 | |
724 /** | |
725 * Filter the tests. [testFilter] can be a [RegExp], a [String] or a | |
726 * predicate function. This is different to enabling/disabling tests | |
727 * in that it removes the tests completely. | |
728 */ | |
729 void filterTests(testFilter) { | |
730 var filterFunction; | |
731 if (testFilter is String) { | |
732 RegExp re = new RegExp(testFilter); | |
733 filterFunction = (t) => re.hasMatch(t.description); | |
734 } else if (testFilter is RegExp) { | |
735 filterFunction = (t) => testFilter.hasMatch(t.description); | |
736 } else if (testFilter is Function) { | |
737 filterFunction = testFilter; | |
738 } | |
739 _tests = _tests.filter(filterFunction); | |
740 } | |
741 | |
742 /** Runs all queued tests, one at a time. */ | |
743 runTests() { | |
744 _currentTest = 0; | |
745 _currentGroup = ''; | |
746 | |
747 // If we are soloing a test, remove all the others. | |
748 if (_soloTest != null) { | |
749 filterTests((t) => t == _soloTest); | |
750 } | |
751 | |
752 _config.onStart(); | |
753 | |
754 _defer(() { | |
755 _testRunner(); | |
756 }); | |
757 } | |
758 | |
759 /** | |
760 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, update | |
761 * the [_currentTest] status accordingly. | |
762 */ | |
763 guardAsync(tryBody, [finallyBody, testNum = -1]) { | |
764 if (testNum < 0) testNum = _currentTest; | |
765 try { | |
766 return tryBody(); | |
767 } catch (e, trace) { | |
768 _registerException(testNum, e, trace); | |
769 } finally { | |
770 if (finallyBody != null) finallyBody(); | |
771 } | |
772 } | |
773 | |
774 /** | |
775 * Registers that an exception was caught for the current test. | |
776 */ | |
777 registerException(e, [trace]) { | |
778 _registerException(_currentTest, e, trace); | |
779 } | |
780 | |
781 /** | |
782 * Registers that an exception was caught for the current test. | |
783 */ | |
784 _registerException(testNum, e, [trace]) { | |
785 trace = trace == null ? '' : trace.toString(); | |
786 if (_tests[testNum].result == null) { | |
787 String message = (e is ExpectException) ? e.message : 'Caught $e'; | |
788 _tests[testNum].fail(message, trace); | |
789 } else { | |
790 _tests[testNum].error('Caught $e', trace); | |
791 } | |
792 if (testNum == _currentTest && | |
793 _tests[testNum].callbackFunctionsOutstanding > 0) { | |
794 _nextTestCase(); | |
795 } | |
796 } | |
797 | |
798 /** | |
799 * Runs a batch of tests, yielding whenever an asynchronous test starts | |
800 * running. Tests will resume executing when such asynchronous test calls | |
801 * [done] or if it fails with an exception. | |
802 */ | |
803 _nextBatch() { | |
804 while (_currentTest < _tests.length) { | |
805 final testCase = _tests[_currentTest]; | |
806 guardAsync(() { | |
807 testCase.run(); | |
808 if (!testCase.isComplete && testCase.callbackFunctionsOutstanding == 0) { | |
809 testCase.pass(); | |
810 } | |
811 }, null, _currentTest); | |
812 | |
813 if (!testCase.isComplete && | |
814 testCase.callbackFunctionsOutstanding > 0) return; | |
815 _currentTest++; | |
816 } | |
817 | |
818 _completeTests(); | |
819 } | |
820 | |
821 /** Publish results on the page and notify controller. */ | |
822 _completeTests() { | |
823 if (!_initialized) return; | |
824 int testsPassed_ = 0; | |
825 int testsFailed_ = 0; | |
826 int testsErrors_ = 0; | |
827 | |
828 for (TestCase t in _tests) { | |
829 switch (t.result) { | |
830 case PASS: testsPassed_++; break; | |
831 case FAIL: testsFailed_++; break; | |
832 case ERROR: testsErrors_++; break; | |
833 } | |
834 } | |
835 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests, | |
836 _uncaughtErrorMessage); | |
837 _initialized = false; | |
838 } | |
839 | |
840 String _fullSpec(String spec) { | |
841 if (spec === null) return '$_currentGroup'; | |
842 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; | |
843 } | |
844 | |
845 void fail(String message) { | |
846 throw new ExpectException(message); | |
847 } | |
848 | |
849 /** | |
850 * Lazily initializes the test library if not already initialized. | |
851 */ | |
852 ensureInitialized() { | |
853 if (_initialized) { | |
854 return; | |
855 } | |
856 _initialized = true; | |
857 | |
858 _tests = <TestCase>[]; | |
859 _testRunner = _nextBatch; | |
860 _uncaughtErrorMessage = null; | |
861 | |
862 if (_config == null) { | |
863 _config = new Configuration(); | |
864 } | |
865 _config.onInit(); | |
866 | |
867 if (_config.autoStart) { | |
868 // Immediately queue the suite up. It will run after a timeout (i.e. after | |
869 // main() has returned). | |
870 _defer(runTests); | |
871 } | |
872 } | |
873 | |
874 /** Select a solo test by ID. */ | |
875 void setSoloTest(int id) { | |
876 for (var i = 0; i < _tests.length; i++) { | |
877 if (_tests[i].id == id) { | |
878 _soloTest = _tests[i]; | |
879 break; | |
880 } | |
881 } | |
882 } | |
883 | |
884 /** Enable/disable a test by ID. */ | |
885 void _setTestEnabledState(int testId, bool state) { | |
886 // Try fast path first. | |
887 if (_tests.length > testId && _tests[testId].id == testId) { | |
888 _tests[testId].enabled = state; | |
889 } else { | |
890 for (var i = 0; i < _tests.length; i++) { | |
891 if (_tests[i].id == testId) { | |
892 _tests[i].enabled = state; | |
893 break; | |
894 } | |
895 } | |
896 } | |
897 } | |
898 | |
899 /** Enable a test by ID. */ | |
900 void enableTest(int testId) => _setTestEnabledState(testId, true); | |
901 | |
902 /** Disable a test by ID. */ | |
903 void disableTest(int testId) => _setTestEnabledState(testId, false); | |
904 | |
905 /** Signature for a test function. */ | |
906 typedef void TestFunction(); | |
OLD | NEW |