Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Side by Side Diff: pkg/unittest/unittest.dart

Issue 11301046: Restructure pkg/unittest and pkg/webdriver to follow the pub conventions. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698