| OLD | NEW |
| 1 // Copyright (c) 2013, 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, install the | 8 * To import this library, install the |
| 9 * [unittest package](http://pub.dartlang.org/packages/unittest) via the pub | 9 * [unittest package](http://pub.dartlang.org/packages/unittest) via the pub |
| 10 * package manager. See the [Getting Started](http://pub.dartlang.org/doc) | 10 * package manager. See the [Getting Started](http://pub.dartlang.org/doc) |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 148 * async.complete(); | 148 * async.complete(); |
| 149 * }); | 149 * }); |
| 150 * }); | 150 * }); |
| 151 * } | 151 * } |
| 152 * | 152 * |
| 153 */ | 153 */ |
| 154 library unittest; | 154 library unittest; |
| 155 | 155 |
| 156 import 'dart:async'; | 156 import 'dart:async'; |
| 157 import 'dart:isolate'; | 157 import 'dart:isolate'; |
| 158 import 'dart:collection'; |
| 158 import 'matcher.dart'; | 159 import 'matcher.dart'; |
| 159 export 'matcher.dart'; | 160 export 'matcher.dart'; |
| 160 | 161 |
| 161 // TODO(amouravski): We should not need to import mock here, but it's necessary | 162 // TODO(amouravski): We should not need to import mock here, but it's necessary |
| 162 // to enable dartdoc on the mock library, as it's not picked up normally. | 163 // to enable dartdoc on the mock library, as it's not picked up normally. |
| 163 import 'mock.dart'; | 164 import 'mock.dart'; |
| 164 | 165 |
| 165 part 'src/config.dart'; | 166 part 'src/config.dart'; |
| 166 part 'src/test_case.dart'; | 167 part 'src/test_case.dart'; |
| 167 | 168 |
| (...skipping 21 matching lines...) Expand all Loading... |
| 189 /** | 190 /** |
| 190 * Description text of the current test group. If multiple groups are nested, | 191 * Description text of the current test group. If multiple groups are nested, |
| 191 * this will contain all of their text concatenated. | 192 * this will contain all of their text concatenated. |
| 192 */ | 193 */ |
| 193 String _currentGroup = ''; | 194 String _currentGroup = ''; |
| 194 | 195 |
| 195 /** Separator used between group names and test names. */ | 196 /** Separator used between group names and test names. */ |
| 196 String groupSep = ' '; | 197 String groupSep = ' '; |
| 197 | 198 |
| 198 /** Tests executed in this suite. */ | 199 /** Tests executed in this suite. */ |
| 199 List<TestCase> _tests; | 200 final List<TestCase> _testCases = new List<TestCase>(); |
| 200 | 201 |
| 201 /** Get the list of tests. */ | 202 /** Get the list of tests. */ |
| 202 List<TestCase> get testCases => _tests; | 203 final List<TestCase> testCases = new UnmodifiableListView(_testCases); |
| 203 | 204 |
| 204 /** Setup function called before each test in a group */ | 205 /** Setup function called before each test in a group */ |
| 205 Function _testSetup; | 206 Function _testSetup; |
| 206 | 207 |
| 207 /** Teardown function called after each test in a group */ | 208 /** Teardown function called after each test in a group */ |
| 208 Function _testTeardown; | 209 Function _testTeardown; |
| 209 | 210 |
| 210 /** Current test being executed. */ | 211 int _currentTestCaseIndex = 0; |
| 211 int _currentTest = 0; | |
| 212 TestCase _currentTestCase; | |
| 213 | 212 |
| 213 /** [TestCase] currently being executed. */ |
| 214 TestCase get currentTestCase => | 214 TestCase get currentTestCase => |
| 215 (_currentTest >= 0 && _currentTest < _tests.length) | 215 (_currentTestCaseIndex >= 0 && _currentTestCaseIndex < _testCases.length) |
| 216 ? _tests[_currentTest] | 216 ? _testCases[_currentTestCaseIndex] |
| 217 : null; | 217 : null; |
| 218 | 218 |
| 219 /** Whether the framework is in an initialized state. */ | 219 /** Whether the framework is in an initialized state. */ |
| 220 bool _initialized = false; | 220 bool _initialized = false; |
| 221 | 221 |
| 222 String _uncaughtErrorMessage = null; | 222 String _uncaughtErrorMessage = null; |
| 223 | 223 |
| 224 /** Test case result strings. */ | 224 /** Test case result strings. */ |
| 225 // TODO(gram) we should change these constants to use a different string | 225 // TODO(gram) we should change these constants to use a different string |
| 226 // (so that writing 'FAIL' in the middle of a test doesn't | 226 // (so that writing 'FAIL' in the middle of a test doesn't |
| (...skipping 15 matching lines...) Expand all Loading... |
| 242 */ | 242 */ |
| 243 Map testState = {}; | 243 Map testState = {}; |
| 244 | 244 |
| 245 /** | 245 /** |
| 246 * Creates a new test case with the given description and body. The | 246 * Creates a new test case with the given description and body. The |
| 247 * description will include the descriptions of any surrounding group() | 247 * description will include the descriptions of any surrounding group() |
| 248 * calls. | 248 * calls. |
| 249 */ | 249 */ |
| 250 void test(String spec, TestFunction body) { | 250 void test(String spec, TestFunction body) { |
| 251 ensureInitialized(); | 251 ensureInitialized(); |
| 252 _tests.add(new TestCase._internal(_tests.length + 1, _fullSpec(spec), body)); | 252 _testCases.add(new TestCase._internal(_testCases.length + 1, _fullSpec(spec), |
| 253 body)); |
| 253 } | 254 } |
| 254 | 255 |
| 255 /** | 256 /** |
| 256 * Creates a new test case with the given description and body. The | 257 * Creates a new test case with the given description and body. The |
| 257 * description will include the descriptions of any surrounding group() | 258 * description will include the descriptions of any surrounding group() |
| 258 * calls. | 259 * calls. |
| 259 * | 260 * |
| 260 * "solo_" means that this will be the only test that is run. All other tests | 261 * "solo_" means that this will be the only test that is run. All other tests |
| 261 * will be skipped. This is a convenience function to let you quickly isolate | 262 * will be skipped. This is a convenience function to let you quickly isolate |
| 262 * a single test by adding "solo_" before it to temporarily disable all other | 263 * a single test by adding "solo_" before it to temporarily disable all other |
| 263 * tests. | 264 * tests. |
| 264 */ | 265 */ |
| 265 void solo_test(String spec, TestFunction body) { | 266 void solo_test(String spec, TestFunction body) { |
| 266 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, | 267 // TODO(rnystrom): Support multiple solos. If more than one test is solo-ed, |
| 267 // all of the solo-ed tests and none of the non-solo-ed ones should run. | 268 // all of the solo-ed tests and none of the non-solo-ed ones should run. |
| 268 if (_soloTest != null) { | 269 if (_soloTest != null) { |
| 269 throw new Exception('Only one test can be soloed right now.'); | 270 throw new Exception('Only one test can be soloed right now.'); |
| 270 } | 271 } |
| 271 | 272 |
| 272 ensureInitialized(); | 273 ensureInitialized(); |
| 273 | 274 |
| 274 _soloTest = new TestCase._internal(_tests.length + 1, _fullSpec(spec), body); | 275 _soloTest = new TestCase._internal(_testCases.length + 1, _fullSpec(spec), bod
y); |
| 275 _tests.add(_soloTest); | 276 _testCases.add(_soloTest); |
| 276 } | 277 } |
| 277 | 278 |
| 278 /** Sentinel value for [_SpreadArgsHelper]. */ | 279 /** Sentinel value for [_SpreadArgsHelper]. */ |
| 279 class _Sentinel { | 280 class _Sentinel { |
| 280 const _Sentinel(); | 281 const _Sentinel(); |
| 281 } | 282 } |
| 282 | 283 |
| 283 /** Simulates spread arguments using named arguments. */ | 284 /** Simulates spread arguments using named arguments. */ |
| 284 // TODO(sigmund): remove this class and simply use a closure with named | 285 // TODO(sigmund): remove this class and simply use a closure with named |
| 285 // arguments (if still applicable). | 286 // arguments (if still applicable). |
| (...skipping 10 matching lines...) Expand all Loading... |
| 296 static const sentinel = const _Sentinel(); | 297 static const sentinel = const _Sentinel(); |
| 297 | 298 |
| 298 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, | 299 _SpreadArgsHelper(Function callback, int minExpected, int maxExpected, |
| 299 Function isDone, String id) | 300 Function isDone, String id) |
| 300 : this.callback = callback, | 301 : this.callback = callback, |
| 301 minExpectedCalls = minExpected, | 302 minExpectedCalls = minExpected, |
| 302 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) | 303 maxExpectedCalls = (maxExpected == 0 && minExpected > 0) |
| 303 ? minExpected | 304 ? minExpected |
| 304 : maxExpected, | 305 : maxExpected, |
| 305 this.isDone = isDone, | 306 this.isDone = isDone, |
| 306 testNum = _currentTest, | 307 testNum = _currentTestCaseIndex, |
| 307 this.id = _makeCallbackId(id, callback) { | 308 this.id = _makeCallbackId(id, callback) { |
| 308 ensureInitialized(); | 309 ensureInitialized(); |
| 309 if (!(_currentTest >= 0 && | 310 if (!(_currentTestCaseIndex >= 0 && |
| 310 _currentTest < _tests.length && | 311 _currentTestCaseIndex < _testCases.length && |
| 311 _tests[_currentTest] != null)) { | 312 _testCases[_currentTestCaseIndex] != null)) { |
| 312 print("No valid test, did you forget to run your test inside a call " | 313 print("No valid test, did you forget to run your test inside a call " |
| 313 "to test()?"); | 314 "to test()?"); |
| 314 } | 315 } |
| 315 assert(_currentTest >= 0 && | 316 assert(_currentTestCaseIndex >= 0 && |
| 316 _currentTest < _tests.length && | 317 _currentTestCaseIndex < _testCases.length && |
| 317 _tests[_currentTest] != null); | 318 _testCases[_currentTestCaseIndex] != null); |
| 318 testCase = _tests[_currentTest]; | 319 testCase = _testCases[_currentTestCaseIndex]; |
| 319 if (isDone != null || minExpected > 0) { | 320 if (isDone != null || minExpected > 0) { |
| 320 testCase._callbackFunctionsOutstanding++; | 321 testCase._callbackFunctionsOutstanding++; |
| 321 complete = false; | 322 complete = false; |
| 322 } else { | 323 } else { |
| 323 complete = true; | 324 complete = true; |
| 324 } | 325 } |
| 325 } | 326 } |
| 326 | 327 |
| 327 static _makeCallbackId(String id, Function callback) { | 328 static _makeCallbackId(String id, Function callback) { |
| 328 // Try to create a reasonable id. | 329 // Try to create a reasonable id. |
| (...skipping 298 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 627 * calls to [test]. The [teardownTest] function can be asynchronous; in this | 628 * calls to [test]. The [teardownTest] function can be asynchronous; in this |
| 628 * case it must return a [Future]. | 629 * case it must return a [Future]. |
| 629 */ | 630 */ |
| 630 void tearDown(Function teardownTest) { | 631 void tearDown(Function teardownTest) { |
| 631 _testTeardown = teardownTest; | 632 _testTeardown = teardownTest; |
| 632 } | 633 } |
| 633 | 634 |
| 634 /** Advance to the next test case. */ | 635 /** Advance to the next test case. */ |
| 635 void _nextTestCase() { | 636 void _nextTestCase() { |
| 636 _defer(() { | 637 _defer(() { |
| 637 _currentTest++; | 638 _currentTestCaseIndex++; |
| 638 _nextBatch(); | 639 _nextBatch(); |
| 639 }); | 640 }); |
| 640 } | 641 } |
| 641 | 642 |
| 642 /** | 643 /** |
| 643 * Utility function that can be used to notify the test framework that an | 644 * Utility function that can be used to notify the test framework that an |
| 644 * error was caught outside of this library. | 645 * error was caught outside of this library. |
| 645 */ | 646 */ |
| 646 void _reportTestError(String msg, String trace) { | 647 void _reportTestError(String msg, String trace) { |
| 647 if (_currentTest < _tests.length) { | 648 if (_currentTestCaseIndex < _testCases.length) { |
| 648 final testCase = _tests[_currentTest]; | 649 final testCase = _testCases[_currentTestCaseIndex]; |
| 649 testCase.error(msg, trace); | 650 testCase.error(msg, trace); |
| 650 } else { | 651 } else { |
| 651 _uncaughtErrorMessage = "$msg: $trace"; | 652 _uncaughtErrorMessage = "$msg: $trace"; |
| 652 } | 653 } |
| 653 } | 654 } |
| 654 | 655 |
| 655 /** | 656 /** |
| 656 * Runs [callback] at the end of the event loop. Note that we don't wrap | 657 * Runs [callback] at the end of the event loop. Note that we don't wrap |
| 657 * the callback in guardAsync; this is for test framework functions which | 658 * the callback in guardAsync; this is for test framework functions which |
| 658 * should not be throwing unexpected exceptions that end up failing test | 659 * should not be throwing unexpected exceptions that end up failing test |
| (...skipping 20 matching lines...) Expand all Loading... |
| 679 void filterTests(testFilter) { | 680 void filterTests(testFilter) { |
| 680 var filterFunction; | 681 var filterFunction; |
| 681 if (testFilter is String) { | 682 if (testFilter is String) { |
| 682 RegExp re = new RegExp(testFilter); | 683 RegExp re = new RegExp(testFilter); |
| 683 filterFunction = (t) => re.hasMatch(t.description); | 684 filterFunction = (t) => re.hasMatch(t.description); |
| 684 } else if (testFilter is RegExp) { | 685 } else if (testFilter is RegExp) { |
| 685 filterFunction = (t) => testFilter.hasMatch(t.description); | 686 filterFunction = (t) => testFilter.hasMatch(t.description); |
| 686 } else if (testFilter is Function) { | 687 } else if (testFilter is Function) { |
| 687 filterFunction = testFilter; | 688 filterFunction = testFilter; |
| 688 } | 689 } |
| 689 _tests.retainWhere(filterFunction); | 690 _testCases.retainWhere(filterFunction); |
| 690 } | 691 } |
| 691 | 692 |
| 692 /** Runs all queued tests, one at a time. */ | 693 /** Runs all queued tests, one at a time. */ |
| 693 void runTests() { | 694 void runTests() { |
| 694 _currentTest = 0; | 695 _currentTestCaseIndex = 0; |
| 695 _currentGroup = ''; | 696 _currentGroup = ''; |
| 696 | 697 |
| 697 // If we are soloing a test, remove all the others. | 698 // If we are soloing a test, remove all the others. |
| 698 if (_soloTest != null) { | 699 if (_soloTest != null) { |
| 699 filterTests((t) => t == _soloTest); | 700 filterTests((t) => t == _soloTest); |
| 700 } | 701 } |
| 701 | 702 |
| 702 _config.onStart(); | 703 _config.onStart(); |
| 703 | 704 |
| 704 _defer(() { | 705 _defer(() { |
| 705 _nextBatch(); | 706 _nextBatch(); |
| 706 }); | 707 }); |
| 707 } | 708 } |
| 708 | 709 |
| 709 /** | 710 /** |
| 710 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is | 711 * Run [tryBody] guarded in a try-catch block. If an exception is thrown, it is |
| 711 * passed to the corresponding test. | 712 * passed to the corresponding test. |
| 712 * | 713 * |
| 713 * The value returned by [tryBody] (if any) is returned by [guardAsync]. | 714 * The value returned by [tryBody] (if any) is returned by [guardAsync]. |
| 714 */ | 715 */ |
| 715 guardAsync(Function tryBody) { | 716 guardAsync(Function tryBody) { |
| 716 return _guardAsync(tryBody, null, _currentTest); | 717 return _guardAsync(tryBody, null, _currentTestCaseIndex); |
| 717 } | 718 } |
| 718 | 719 |
| 719 _guardAsync(Function tryBody, Function finallyBody, int testNum) { | 720 _guardAsync(Function tryBody, Function finallyBody, int testNum) { |
| 720 assert(testNum >= 0); | 721 assert(testNum >= 0); |
| 721 try { | 722 try { |
| 722 return tryBody(); | 723 return tryBody(); |
| 723 } catch (e, trace) { | 724 } catch (e, trace) { |
| 724 _registerException(testNum, e, trace); | 725 _registerException(testNum, e, trace); |
| 725 } finally { | 726 } finally { |
| 726 if (finallyBody != null) finallyBody(); | 727 if (finallyBody != null) finallyBody(); |
| 727 } | 728 } |
| 728 } | 729 } |
| 729 | 730 |
| 730 /** | 731 /** |
| 731 * Registers that an exception was caught for the current test. | 732 * Registers that an exception was caught for the current test. |
| 732 */ | 733 */ |
| 733 void registerException(e, [trace]) { | 734 void registerException(e, [trace]) { |
| 734 _registerException(_currentTest, e, trace); | 735 _registerException(_currentTestCaseIndex, e, trace); |
| 735 } | 736 } |
| 736 | 737 |
| 737 /** | 738 /** |
| 738 * Registers that an exception was caught for the current test. | 739 * Registers that an exception was caught for the current test. |
| 739 */ | 740 */ |
| 740 void _registerException(testNum, e, [trace]) { | 741 void _registerException(testNum, e, [trace]) { |
| 741 trace = trace == null ? '' : trace.toString(); | 742 trace = trace == null ? '' : trace.toString(); |
| 742 String message = (e is TestFailure) ? e.message : 'Caught $e'; | 743 String message = (e is TestFailure) ? e.message : 'Caught $e'; |
| 743 if (_tests[testNum].result == null) { | 744 if (_testCases[testNum].result == null) { |
| 744 _tests[testNum].fail(message, trace); | 745 _testCases[testNum].fail(message, trace); |
| 745 } else { | 746 } else { |
| 746 _tests[testNum].error(message, trace); | 747 _testCases[testNum].error(message, trace); |
| 747 } | 748 } |
| 748 } | 749 } |
| 749 | 750 |
| 750 /** | 751 /** |
| 751 * Runs a batch of tests, yielding whenever an asynchronous test starts | 752 * Runs a batch of tests, yielding whenever an asynchronous test starts |
| 752 * running. Tests will resume executing when such asynchronous test calls | 753 * running. Tests will resume executing when such asynchronous test calls |
| 753 * [done] or if it fails with an exception. | 754 * [done] or if it fails with an exception. |
| 754 */ | 755 */ |
| 755 void _nextBatch() { | 756 void _nextBatch() { |
| 756 while (true) { | 757 while (true) { |
| 757 if (_currentTest >= _tests.length) { | 758 if (_currentTestCaseIndex >= _testCases.length) { |
| 758 _completeTests(); | 759 _completeTests(); |
| 759 break; | 760 break; |
| 760 } | 761 } |
| 761 final testCase = _tests[_currentTest]; | 762 final testCase = _testCases[_currentTestCaseIndex]; |
| 762 var f = _guardAsync(testCase._run, null, _currentTest); | 763 var f = _guardAsync(testCase._run, null, _currentTestCaseIndex); |
| 763 if (f != null) { | 764 if (f != null) { |
| 764 f.whenComplete(() { | 765 f.whenComplete(() { |
| 765 _nextTestCase(); // Schedule the next test. | 766 _nextTestCase(); // Schedule the next test. |
| 766 }); | 767 }); |
| 767 break; | 768 break; |
| 768 } | 769 } |
| 769 _currentTest++; | 770 _currentTestCaseIndex++; |
| 770 } | 771 } |
| 771 } | 772 } |
| 772 | 773 |
| 773 /** Publish results on the page and notify controller. */ | 774 /** Publish results on the page and notify controller. */ |
| 774 void _completeTests() { | 775 void _completeTests() { |
| 775 if (!_initialized) return; | 776 if (!_initialized) return; |
| 776 int passed = 0; | 777 int passed = 0; |
| 777 int failed = 0; | 778 int failed = 0; |
| 778 int errors = 0; | 779 int errors = 0; |
| 779 | 780 |
| 780 for (TestCase t in _tests) { | 781 for (TestCase t in _testCases) { |
| 781 switch (t.result) { | 782 switch (t.result) { |
| 782 case PASS: passed++; break; | 783 case PASS: passed++; break; |
| 783 case FAIL: failed++; break; | 784 case FAIL: failed++; break; |
| 784 case ERROR: errors++; break; | 785 case ERROR: errors++; break; |
| 785 } | 786 } |
| 786 } | 787 } |
| 787 _config.onSummary(passed, failed, errors, _tests, _uncaughtErrorMessage); | 788 _config.onSummary(passed, failed, errors, _testCases, _uncaughtErrorMessage); |
| 788 _config.onDone(passed > 0 && failed == 0 && errors == 0 && | 789 _config.onDone(passed > 0 && failed == 0 && errors == 0 && |
| 789 _uncaughtErrorMessage == null); | 790 _uncaughtErrorMessage == null); |
| 790 _initialized = false; | 791 _initialized = false; |
| 791 } | 792 } |
| 792 | 793 |
| 793 String _fullSpec(String spec) { | 794 String _fullSpec(String spec) { |
| 794 if (spec == null) return '$_currentGroup'; | 795 if (spec == null) return '$_currentGroup'; |
| 795 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; | 796 return _currentGroup != '' ? '$_currentGroup$groupSep$spec' : spec; |
| 796 } | 797 } |
| 797 | 798 |
| 798 /** | 799 /** |
| 799 * Lazily initializes the test library if not already initialized. | 800 * Lazily initializes the test library if not already initialized. |
| 800 */ | 801 */ |
| 801 void ensureInitialized() { | 802 void ensureInitialized() { |
| 802 if (_initialized) { | 803 if (_initialized) { |
| 803 return; | 804 return; |
| 804 } | 805 } |
| 805 _initialized = true; | 806 _initialized = true; |
| 806 // Hook our async guard into the matcher library. | 807 // Hook our async guard into the matcher library. |
| 807 wrapAsync = (f, [id]) => expectAsync1(f, id: id); | 808 wrapAsync = (f, [id]) => expectAsync1(f, id: id); |
| 808 | 809 |
| 809 _tests = <TestCase>[]; | |
| 810 _uncaughtErrorMessage = null; | 810 _uncaughtErrorMessage = null; |
| 811 | 811 |
| 812 if (_config == null) { | 812 if (_config == null) { |
| 813 unittestConfiguration = new Configuration(); | 813 unittestConfiguration = new Configuration(); |
| 814 } | 814 } |
| 815 _config.onInit(); | 815 _config.onInit(); |
| 816 | 816 |
| 817 if (_config.autoStart) { | 817 if (_config.autoStart) { |
| 818 // Immediately queue the suite up. It will run after a timeout (i.e. after | 818 // Immediately queue the suite up. It will run after a timeout (i.e. after |
| 819 // main() has returned). | 819 // main() has returned). |
| 820 _defer(runTests); | 820 _defer(runTests); |
| 821 } | 821 } |
| 822 } | 822 } |
| 823 | 823 |
| 824 /** Select a solo test by ID. */ | 824 /** Select a solo test by ID. */ |
| 825 void setSoloTest(int id) { | 825 void setSoloTest(int id) { |
| 826 for (var i = 0; i < _tests.length; i++) { | 826 for (var i = 0; i < _testCases.length; i++) { |
| 827 if (_tests[i].id == id) { | 827 if (_testCases[i].id == id) { |
| 828 _soloTest = _tests[i]; | 828 _soloTest = _testCases[i]; |
| 829 break; | 829 break; |
| 830 } | 830 } |
| 831 } | 831 } |
| 832 } | 832 } |
| 833 | 833 |
| 834 /** Enable/disable a test by ID. */ | 834 /** Enable/disable a test by ID. */ |
| 835 void _setTestEnabledState(int testId, bool state) { | 835 void _setTestEnabledState(int testId, bool state) { |
| 836 // Try fast path first. | 836 // Try fast path first. |
| 837 if (_tests.length > testId && _tests[testId].id == testId) { | 837 if (_testCases.length > testId && _testCases[testId].id == testId) { |
| 838 _tests[testId].enabled = state; | 838 _testCases[testId].enabled = state; |
| 839 } else { | 839 } else { |
| 840 for (var i = 0; i < _tests.length; i++) { | 840 for (var i = 0; i < _testCases.length; i++) { |
| 841 if (_tests[i].id == testId) { | 841 if (_testCases[i].id == testId) { |
| 842 _tests[i].enabled = state; | 842 _testCases[i].enabled = state; |
| 843 break; | 843 break; |
| 844 } | 844 } |
| 845 } | 845 } |
| 846 } | 846 } |
| 847 } | 847 } |
| 848 | 848 |
| 849 /** Enable a test by ID. */ | 849 /** Enable a test by ID. */ |
| 850 void enableTest(int testId) => _setTestEnabledState(testId, true); | 850 void enableTest(int testId) => _setTestEnabledState(testId, true); |
| 851 | 851 |
| 852 /** Disable a test by ID. */ | 852 /** Disable a test by ID. */ |
| 853 void disableTest(int testId) => _setTestEnabledState(testId, false); | 853 void disableTest(int testId) => _setTestEnabledState(testId, false); |
| 854 | 854 |
| 855 /** Signature for a test function. */ | 855 /** Signature for a test function. */ |
| 856 typedef dynamic TestFunction(); | 856 typedef dynamic TestFunction(); |
| OLD | NEW |