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

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
« no previous file with comments | « pkg/unittest/test_case.dart ('k') | pkg/unittest/vm_config.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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(_testNum);
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(testNum) {
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 != testNum) {
658 if (_tests[testNum].result == PASS) {
659 _tests[testNum].error("Unexpected extra callbacks");
660 }
661 return; // Extraneous callback.
662 }
663 if (_currentTest < _tests.length) {
664 final testCase = _tests[_currentTest];
665 --testCase.callbackFunctionsOutstanding;
666 if (testCase.callbackFunctionsOutstanding < 0) {
667 // TODO(gram): Check: Can this even happen?
668 testCase.error(
669 'More calls to _handleCallbackFunctionComplete() than expected.',
670 '');
671 } else if (testCase.callbackFunctionsOutstanding == 0) {
672 if (!testCase.isComplete) {
673 testCase.pass();
674 }
675 _nextTestCase();
676 }
677 }
678 });
679 }
680
681 /** Advance to the next test case. */
682 void _nextTestCase() {
683 _currentTest++;
684 _testRunner();
685 }
686
687 /**
688 * Temporary hack: expose old API.
689 * TODO(gram) remove this when WebKit tests are working with new framework
690 */
691 void callbackDone() {
692 _handleCallbackFunctionComplete(_currentTest);
693 }
694
695 /**
696 * Utility function that can be used to notify the test framework that an
697 * error was caught outside of this library.
698 */
699 void _reportTestError(String msg, String trace) {
700 if (_currentTest < _tests.length) {
701 final testCase = _tests[_currentTest];
702 testCase.error(msg, trace);
703 if (testCase.callbackFunctionsOutstanding > 0) {
704 _nextTestCase();
705 }
706 } else {
707 _uncaughtErrorMessage = "$msg: $trace";
708 }
709 }
710
711 /** Runs [callback] at the end of the event loop. */
712 _defer(void callback()) {
713 // Exploit isolate ports as a platform-independent mechanism to queue a
714 // message at the end of the event loop.
715 // TODO(sigmund): expose this functionality somewhere in our libraries.
716 final port = new ReceivePort();
717 port.receive((msg, reply) {
718 callback();
719 port.close();
720 });
721 port.toSendPort().send(null, null);
722 }
723
724 rerunTests() {
725 _uncaughtErrorMessage = null;
726 _initialized = true; // We don't want to reset the test array.
727 runTests();
728 }
729
730 /**
731 * Filter the tests. [testFilter] can be a [RegExp], a [String] or a
732 * predicate function. This is different to enabling/disabling tests
733 * in that it removes the tests completely.
734 */
735 void filterTests(testFilter) {
736 var filterFunction;
737 if (testFilter is String) {
738 RegExp re = new RegExp(testFilter);
739 filterFunction = (t) => re.hasMatch(t.description);
740 } else if (testFilter is RegExp) {
741 filterFunction = (t) => testFilter.hasMatch(t.description);
742 } else if (testFilter is Function) {
743 filterFunction = testFilter;
744 }
745 _tests = _tests.filter(filterFunction);
746 }
747
748 /** Runs all queued tests, one at a time. */
749 runTests() {
750 _currentTest = 0;
751 _currentGroup = '';
752
753 // If we are soloing a test, remove all the others.
754 if (_soloTest != null) {
755 filterTests((t) => t == _soloTest);
756 }
757
758 _config.onStart();
759
760 _defer(() {
761 _testRunner();
762 });
763 }
764
765 /**
766 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, update
767 * the [_currentTest] status accordingly.
768 */
769 guardAsync(tryBody, [finallyBody, testNum = -1]) {
770 if (testNum < 0) testNum = _currentTest;
771 try {
772 return tryBody();
773 } catch (e, trace) {
774 _registerException(testNum, e, trace);
775 } finally {
776 if (finallyBody != null) finallyBody();
777 }
778 }
779
780 /**
781 * Registers that an exception was caught for the current test.
782 */
783 registerException(e, [trace]) {
784 _registerException(_currentTest, e, trace);
785 }
786
787 /**
788 * Registers that an exception was caught for the current test.
789 */
790 _registerException(testNum, e, [trace]) {
791 trace = trace == null ? '' : trace.toString();
792 if (_tests[testNum].result == null) {
793 String message = (e is ExpectException) ? e.message : 'Caught $e';
794 _tests[testNum].fail(message, trace);
795 } else {
796 _tests[testNum].error('Caught $e', trace);
797 }
798 if (testNum == _currentTest &&
799 _tests[testNum].callbackFunctionsOutstanding > 0) {
800 _nextTestCase();
801 }
802 }
803
804 /**
805 * Runs a batch of tests, yielding whenever an asynchronous test starts
806 * running. Tests will resume executing when such asynchronous test calls
807 * [done] or if it fails with an exception.
808 */
809 _nextBatch() {
810 while (_currentTest < _tests.length) {
811 final testCase = _tests[_currentTest];
812 guardAsync(() {
813 testCase.run();
814 if (!testCase.isComplete && testCase.callbackFunctionsOutstanding == 0) {
815 testCase.pass();
816 }
817 }, null, _currentTest);
818
819 if (!testCase.isComplete &&
820 testCase.callbackFunctionsOutstanding > 0) return;
821 _currentTest++;
822 }
823
824 _completeTests();
825 }
826
827 /** Publish results on the page and notify controller. */
828 _completeTests() {
829 if (!_initialized) return;
830 int testsPassed_ = 0;
831 int testsFailed_ = 0;
832 int testsErrors_ = 0;
833
834 for (TestCase t in _tests) {
835 switch (t.result) {
836 case PASS: testsPassed_++; break;
837 case FAIL: testsFailed_++; break;
838 case ERROR: testsErrors_++; break;
839 }
840 }
841 _config.onDone(testsPassed_, testsFailed_, testsErrors_, _tests,
842 _uncaughtErrorMessage);
843 _initialized = false;
844 }
845
846 String _fullSpec(String spec) {
847 if (spec === null) return '$_currentGroup';
848 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec;
849 }
850
851 void fail(String message) {
852 throw new ExpectException(message);
853 }
854
855 /**
856 * Lazily initializes the test library if not already initialized.
857 */
858 ensureInitialized() {
859 if (_initialized) {
860 return;
861 }
862 _initialized = true;
863
864 _tests = <TestCase>[];
865 _testRunner = _nextBatch;
866 _uncaughtErrorMessage = null;
867
868 if (_config == null) {
869 _config = new Configuration();
870 }
871 _config.onInit();
872
873 if (_config.autoStart) {
874 // Immediately queue the suite up. It will run after a timeout (i.e. after
875 // main() has returned).
876 _defer(runTests);
877 }
878 }
879
880 /** Select a solo test by ID. */
881 void setSoloTest(int id) {
882 for (var i = 0; i < _tests.length; i++) {
883 if (_tests[i].id == id) {
884 _soloTest = _tests[i];
885 break;
886 }
887 }
888 }
889
890 /** Enable/disable a test by ID. */
891 void _setTestEnabledState(int testId, bool state) {
892 // Try fast path first.
893 if (_tests.length > testId && _tests[testId].id == testId) {
894 _tests[testId].enabled = state;
895 } else {
896 for (var i = 0; i < _tests.length; i++) {
897 if (_tests[i].id == testId) {
898 _tests[i].enabled = state;
899 break;
900 }
901 }
902 }
903 }
904
905 /** Enable a test by ID. */
906 void enableTest(int testId) => _setTestEnabledState(testId, true);
907
908 /** Disable a test by ID. */
909 void disableTest(int testId) => _setTestEnabledState(testId, false);
910
911 /** Signature for a test function. */
912 typedef void TestFunction();
OLDNEW
« no previous file with comments | « pkg/unittest/test_case.dart ('k') | pkg/unittest/vm_config.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698