| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * A library for writing dart unit tests. | 6 * A library for writing dart unit tests. |
| 7 * | 7 * |
| 8 * To import this library, use the pub package manager. | 8 * To import this library, use the pub package manager. |
| 9 * Create a pubspec.yaml file in your project and add | 9 * Create a pubspec.yaml file in your project and add |
| 10 * a dependency on unittest with the following lines: | 10 * a dependency on unittest with the following lines: |
| 11 * dependencies: | 11 * dependencies: |
| (...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 251 */ | 251 */ |
| 252 Map testState = {}; | 252 Map testState = {}; |
| 253 | 253 |
| 254 /** | 254 /** |
| 255 * Creates a new test case with the given description and body. The | 255 * Creates a new test case with the given description and body. The |
| 256 * description will include the descriptions of any surrounding group() | 256 * description will include the descriptions of any surrounding group() |
| 257 * calls. | 257 * calls. |
| 258 */ | 258 */ |
| 259 void test(String spec, TestFunction body) { | 259 void test(String spec, TestFunction body) { |
| 260 ensureInitialized(); | 260 ensureInitialized(); |
| 261 _tests.add(new TestCase(_tests.length + 1, _fullSpec(spec), body, 0)); | 261 _tests.add(new TestCase._internal(_tests.length + 1, _fullSpec(spec), body)); |
| 262 } | 262 } |
| 263 | 263 |
| 264 /** | 264 /** |
| 265 * Creates a new test case with the given description and body. The | 265 * Creates a new test case with the given description and body. The |
| 266 * description will include the descriptions of any surrounding group() | 266 * description will include the descriptions of any surrounding group() |
| 267 * calls. | 267 * calls. |
| 268 * | 268 * |
| 269 * "solo_" means that this will be the only test that is run. All other tests | 269 * "solo_" means that this will be the only test that is run. All other tests |
| 270 * will be skipped. This is a convenience function to let you quickly isolate | 270 * will be skipped. This is a convenience function to let you quickly isolate |
| 271 * a single test by adding "solo_" before it to temporarily disable all other | 271 * a single test by adding "solo_" before it to temporarily disable all other |
| 272 * tests. | 272 * tests. |
| 273 */ | 273 */ |
| 274 void solo_test(String spec, TestFunction body) { | 274 void solo_test(String spec, TestFunction body) { |
| 275 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, | 275 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, |
| 276 // all of the solo-ed tests and none of the non-solo-ed ones should run. | 276 // all of the solo-ed tests and none of the non-solo-ed ones should run. |
| 277 if (_soloTest != null) { | 277 if (_soloTest != null) { |
| 278 throw new Exception('Only one test can be soloed right now.'); | 278 throw new Exception('Only one test can be soloed right now.'); |
| 279 } | 279 } |
| 280 | 280 |
| 281 ensureInitialized(); | 281 ensureInitialized(); |
| 282 | 282 |
| 283 _soloTest = new TestCase(_tests.length + 1, _fullSpec(spec), body, 0); | 283 _soloTest = new TestCase._internal(_tests.length + 1, _fullSpec(spec), body); |
| 284 _tests.add(_soloTest); | 284 _tests.add(_soloTest); |
| 285 } | 285 } |
| 286 | 286 |
| 287 /** Sentinel value for [_SpreadArgsHelper]. */ | 287 /** Sentinel value for [_SpreadArgsHelper]. */ |
| 288 class _Sentinel { | 288 class _Sentinel { |
| 289 const _Sentinel(); | 289 const _Sentinel(); |
| 290 } | 290 } |
| 291 | 291 |
| 292 /** Simulates spread arguments using named arguments. */ | 292 /** Simulates spread arguments using named arguments. */ |
| 293 // TODO(sigmund): remove this class and simply use a closure with named | 293 // TODO(sigmund): remove this class and simply use a closure with named |
| (...skipping 25 matching lines...) Expand all Loading... |
| 319 _currentTest < _tests.length && | 319 _currentTest < _tests.length && |
| 320 _tests[_currentTest] != null)) { | 320 _tests[_currentTest] != null)) { |
| 321 print("No valid test, did you forget to run your test inside a call " | 321 print("No valid test, did you forget to run your test inside a call " |
| 322 "to test()?"); | 322 "to test()?"); |
| 323 } | 323 } |
| 324 assert(_currentTest >= 0 && | 324 assert(_currentTest >= 0 && |
| 325 _currentTest < _tests.length && | 325 _currentTest < _tests.length && |
| 326 _tests[_currentTest] != null); | 326 _tests[_currentTest] != null); |
| 327 testCase = _tests[_currentTest]; | 327 testCase = _tests[_currentTest]; |
| 328 if (isDone != null || minExpected > 0) { | 328 if (isDone != null || minExpected > 0) { |
| 329 testCase.callbackFunctionsOutstanding++; | 329 testCase._callbackFunctionsOutstanding++; |
| 330 complete = false; | 330 complete = false; |
| 331 } else { | 331 } else { |
| 332 complete = true; | 332 complete = true; |
| 333 } | 333 } |
| 334 } | 334 } |
| 335 | 335 |
| 336 static _makeCallbackId(String id, Function callback) { | 336 static _makeCallbackId(String id, Function callback) { |
| 337 // Try to create a reasonable id. | 337 // Try to create a reasonable id. |
| 338 if (id != null) { | 338 if (id != null) { |
| 339 return "$id "; | 339 return "$id "; |
| (...skipping 14 matching lines...) Expand all Loading... |
| 354 return ''; | 354 return ''; |
| 355 } | 355 } |
| 356 | 356 |
| 357 shouldCallBack() { | 357 shouldCallBack() { |
| 358 ++actualCalls; | 358 ++actualCalls; |
| 359 if (testCase.isComplete) { | 359 if (testCase.isComplete) { |
| 360 // Don't run if the test is done. We don't throw here as this is not | 360 // Don't run if the test is done. We don't throw here as this is not |
| 361 // the current test, but we do mark the old test as having an error | 361 // the current test, but we do mark the old test as having an error |
| 362 // if it previously passed. | 362 // if it previously passed. |
| 363 if (testCase.result == PASS) { | 363 if (testCase.result == PASS) { |
| 364 testCase.error( | 364 testCase._error( |
| 365 'Callback ${id}called ($actualCalls) after test case ' | 365 'Callback ${id}called ($actualCalls) after test case ' |
| 366 '${testCase.description} has already been marked as ' | 366 '${testCase.description} has already been marked as ' |
| 367 '${testCase.result}.', ''); | 367 '${testCase.result}.', ''); |
| 368 } | 368 } |
| 369 return false; | 369 return false; |
| 370 } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) { | 370 } else if (maxExpectedCalls >= 0 && actualCalls > maxExpectedCalls) { |
| 371 throw new TestFailure('Callback ${id}called more times than expected ' | 371 throw new TestFailure('Callback ${id}called more times than expected ' |
| 372 '($maxExpectedCalls).'); | 372 '($maxExpectedCalls).'); |
| 373 } | 373 } |
| 374 return true; | 374 return true; |
| 375 } | 375 } |
| 376 | 376 |
| 377 after() { | 377 after() { |
| 378 if (!complete) { | 378 if (!complete) { |
| 379 if (minExpectedCalls > 0 && actualCalls < minExpectedCalls) return; | 379 if (minExpectedCalls > 0 && actualCalls < minExpectedCalls) return; |
| 380 if (isDone != null && !isDone()) return; | 380 if (isDone != null && !isDone()) return; |
| 381 | 381 |
| 382 // Mark this callback as complete and remove it from the testcase | 382 // Mark this callback as complete and remove it from the testcase |
| 383 // oustanding callback count; if that hits zero the testcase is done. | 383 // oustanding callback count; if that hits zero the testcase is done. |
| 384 complete = true; | 384 complete = true; |
| 385 testCase.markCallbackComplete(); | 385 testCase._markCallbackComplete(); |
| 386 } | 386 } |
| 387 } | 387 } |
| 388 | 388 |
| 389 invoke([arg0 = sentinel, arg1 = sentinel, arg2 = sentinel, | 389 invoke([arg0 = sentinel, arg1 = sentinel, arg2 = sentinel, |
| 390 arg3 = sentinel, arg4 = sentinel]) { | 390 arg3 = sentinel, arg4 = sentinel]) { |
| 391 return _guardAsync(() { | 391 return _guardAsync(() { |
| 392 if (!shouldCallBack()) { | 392 if (!shouldCallBack()) { |
| 393 return; | 393 return; |
| 394 } else if (arg0 == sentinel) { | 394 } else if (arg0 == sentinel) { |
| 395 return callback(); | 395 return callback(); |
| 396 } else if (arg1 == sentinel) { | 396 } else if (arg1 == sentinel) { |
| 397 return callback(arg0); | 397 return callback(arg0); |
| 398 } else if (arg2 == sentinel) { | 398 } else if (arg2 == sentinel) { |
| 399 return callback(arg0, arg1); | 399 return callback(arg0, arg1); |
| 400 } else if (arg3 == sentinel) { | 400 } else if (arg3 == sentinel) { |
| 401 return callback(arg0, arg1, arg2); | 401 return callback(arg0, arg1, arg2); |
| 402 } else if (arg4 == sentinel) { | 402 } else if (arg4 == sentinel) { |
| 403 return callback(arg0, arg1, arg2, arg3); | 403 return callback(arg0, arg1, arg2, arg3); |
| 404 } else { | 404 } else { |
| 405 testCase.error( | 405 testCase._error( |
| 406 'unittest lib does not support callbacks with more than' | 406 'unittest lib does not support callbacks with more than' |
| 407 ' 4 arguments.', | 407 ' 4 arguments.', |
| 408 ''); | 408 ''); |
| 409 } | 409 } |
| 410 }, | 410 }, |
| 411 after, testNum); | 411 after, testNum); |
| 412 } | 412 } |
| 413 | 413 |
| 414 invoke0() { | 414 invoke0() { |
| 415 return _guardAsync( | 415 return _guardAsync( |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 648 }); | 648 }); |
| 649 } | 649 } |
| 650 | 650 |
| 651 /** | 651 /** |
| 652 * Utility function that can be used to notify the test framework that an | 652 * Utility function that can be used to notify the test framework that an |
| 653 * error was caught outside of this library. | 653 * error was caught outside of this library. |
| 654 */ | 654 */ |
| 655 void _reportTestError(String msg, String trace) { | 655 void _reportTestError(String msg, String trace) { |
| 656 if (_currentTest < _tests.length) { | 656 if (_currentTest < _tests.length) { |
| 657 final testCase = _tests[_currentTest]; | 657 final testCase = _tests[_currentTest]; |
| 658 testCase.error(msg, trace); | 658 testCase._error(msg, trace); |
| 659 } else { | 659 } else { |
| 660 _uncaughtErrorMessage = "$msg: $trace"; | 660 _uncaughtErrorMessage = "$msg: $trace"; |
| 661 } | 661 } |
| 662 } | 662 } |
| 663 | 663 |
| 664 /** | 664 /** |
| 665 * Runs [callback] at the end of the event loop. Note that we don't wrap | 665 * Runs [callback] at the end of the event loop. Note that we don't wrap |
| 666 * the callback in guardAsync; this is for test framework functions which | 666 * the callback in guardAsync; this is for test framework functions which |
| 667 * should not be throwing unexpected exceptions that end up failing test | 667 * should not be throwing unexpected exceptions that end up failing test |
| 668 * cases! Furthermore, we need the final exception to be thrown but not | 668 * cases! Furthermore, we need the final exception to be thrown but not |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 743 _registerException(_currentTest, e, trace); | 743 _registerException(_currentTest, e, trace); |
| 744 } | 744 } |
| 745 | 745 |
| 746 /** | 746 /** |
| 747 * Registers that an exception was caught for the current test. | 747 * Registers that an exception was caught for the current test. |
| 748 */ | 748 */ |
| 749 _registerException(testNum, e, [trace]) { | 749 _registerException(testNum, e, [trace]) { |
| 750 trace = trace == null ? '' : trace.toString(); | 750 trace = trace == null ? '' : trace.toString(); |
| 751 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 751 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 752 if (_tests[testNum].result == null) { | 752 if (_tests[testNum].result == null) { |
| 753 _tests[testNum].fail(message, trace); | 753 _tests[testNum]._fail(message, trace); |
| 754 } else { | 754 } else { |
| 755 _tests[testNum].error(message, trace); | 755 _tests[testNum]._error(message, trace); |
| 756 } | 756 } |
| 757 } | 757 } |
| 758 | 758 |
| 759 /** | 759 /** |
| 760 * Runs a batch of tests, yielding whenever an asynchronous test starts | 760 * Runs a batch of tests, yielding whenever an asynchronous test starts |
| 761 * running. Tests will resume executing when such asynchronous test calls | 761 * running. Tests will resume executing when such asynchronous test calls |
| 762 * [done] or if it fails with an exception. | 762 * [done] or if it fails with an exception. |
| 763 */ | 763 */ |
| 764 _nextBatch() { | 764 _nextBatch() { |
| 765 while (true) { | 765 while (true) { |
| 766 if (_currentTest >= _tests.length) { | 766 if (_currentTest >= _tests.length) { |
| 767 _completeTests(); | 767 _completeTests(); |
| 768 break; | 768 break; |
| 769 } | 769 } |
| 770 final testCase = _tests[_currentTest]; | 770 final testCase = _tests[_currentTest]; |
| 771 var f = _guardAsync(testCase.run, null, _currentTest); | 771 var f = _guardAsync(testCase._run, null, _currentTest); |
| 772 if (f != null) { | 772 if (f != null) { |
| 773 f.whenComplete(() { | 773 f.whenComplete(() { |
| 774 _nextTestCase(); // Schedule the next test. | 774 _nextTestCase(); // Schedule the next test. |
| 775 }); | 775 }); |
| 776 break; | 776 break; |
| 777 } | 777 } |
| 778 _currentTest++; | 778 _currentTest++; |
| 779 } | 779 } |
| 780 } | 780 } |
| 781 | 781 |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 838 _soloTest = _tests[i]; | 838 _soloTest = _tests[i]; |
| 839 break; | 839 break; |
| 840 } | 840 } |
| 841 } | 841 } |
| 842 } | 842 } |
| 843 | 843 |
| 844 /** Enable/disable a test by ID. */ | 844 /** Enable/disable a test by ID. */ |
| 845 void _setTestEnabledState(int testId, bool state) { | 845 void _setTestEnabledState(int testId, bool state) { |
| 846 // Try fast path first. | 846 // Try fast path first. |
| 847 if (_tests.length > testId && _tests[testId].id == testId) { | 847 if (_tests.length > testId && _tests[testId].id == testId) { |
| 848 _tests[testId].enabled = state; | 848 _tests[testId]._enabled = state; |
| 849 } else { | 849 } else { |
| 850 for (var i = 0; i < _tests.length; i++) { | 850 for (var i = 0; i < _tests.length; i++) { |
| 851 if (_tests[i].id == testId) { | 851 if (_tests[i].id == testId) { |
| 852 _tests[i].enabled = state; | 852 _tests[i]._enabled = state; |
| 853 break; | 853 break; |
| 854 } | 854 } |
| 855 } | 855 } |
| 856 } | 856 } |
| 857 } | 857 } |
| 858 | 858 |
| 859 /** Enable a test by ID. */ | 859 /** Enable a test by ID. */ |
| 860 void enableTest(int testId) => _setTestEnabledState(testId, true); | 860 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 861 | 861 |
| 862 /** Disable a test by ID. */ | 862 /** Disable a test by ID. */ |
| 863 void disableTest(int testId) => _setTestEnabledState(testId, false); | 863 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 864 | 864 |
| 865 /** Signature for a test function. */ | 865 /** Signature for a test function. */ |
| 866 typedef dynamic TestFunction(); | 866 typedef dynamic TestFunction(); |
| OLD | NEW |