| 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 |