| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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 * Classes and methods for executing tests. | 6 * Classes and methods for executing tests. |
| 7 * | 7 * |
| 8 * This module includes: | 8 * This module includes: |
| 9 * - Managing parallel execution of tests, including timeout checks. | 9 * - Managing parallel execution of tests, including timeout checks. |
| 10 * - Evaluating the output of each test as pass/fail/crash/timeout. | 10 * - Evaluating the output of each test as pass/fail/crash/timeout. |
| 11 */ | 11 */ |
| 12 library test_runner; | 12 library test_runner; |
| 13 | 13 |
| 14 import "dart:async"; | 14 import "dart:async"; |
| 15 import "dart:collection" show Queue; | 15 import "dart:collection" show Queue; |
| 16 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow | 16 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow |
| 17 // CommandOutput.exitCode in subclasses of CommandOutput. | 17 // CommandOutput.exitCode in subclasses of CommandOutput. |
| 18 import "dart:io" as io; | 18 import "dart:io" as io; |
| 19 import "dart:isolate"; | 19 import "dart:isolate"; |
| 20 import "dart:uri"; | 20 import "dart:uri"; |
| 21 import "browser_controller.dart"; |
| 21 import "http_server.dart" as http_server; | 22 import "http_server.dart" as http_server; |
| 22 import "status_file_parser.dart"; | 23 import "status_file_parser.dart"; |
| 23 import "test_progress.dart"; | 24 import "test_progress.dart"; |
| 24 import "test_suite.dart"; | 25 import "test_suite.dart"; |
| 25 import "utils.dart"; | 26 import "utils.dart"; |
| 26 | 27 |
| 27 const int NO_TIMEOUT = 0; | 28 const int NO_TIMEOUT = 0; |
| 28 const int SLOW_TIMEOUT_MULTIPLIER = 4; | 29 const int SLOW_TIMEOUT_MULTIPLIER = 4; |
| 29 | 30 |
| 30 const int CRASHING_BROWSER_EXITCODE = -10; | 31 const int CRASHING_BROWSER_EXITCODE = -10; |
| 31 | 32 |
| 33 const int NUMBER_OF_BROWSERCONTROLLER_BROWSERS = 4; |
| 34 |
| 32 typedef void TestCaseEvent(TestCase testCase); | 35 typedef void TestCaseEvent(TestCase testCase); |
| 33 typedef void ExitCodeEvent(int exitCode); | 36 typedef void ExitCodeEvent(int exitCode); |
| 34 typedef void EnqueueMoreWork(ProcessQueue queue); | 37 typedef void EnqueueMoreWork(ProcessQueue queue); |
| 35 | 38 |
| 36 // Some IO tests use these variables and get confused if the host environment | 39 // Some IO tests use these variables and get confused if the host environment |
| 37 // variables are inherited so they are excluded. | 40 // variables are inherited so they are excluded. |
| 38 const List<String> EXCLUDED_ENVIRONMENT_VARIABLES = | 41 const List<String> EXCLUDED_ENVIRONMENT_VARIABLES = |
| 39 const ['http_proxy', 'https_proxy', 'no_proxy', | 42 const ['http_proxy', 'https_proxy', 'no_proxy', |
| 40 'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY']; | 43 'HTTP_PROXY', 'HTTPS_PROXY', 'NO_PROXY']; |
| 41 | 44 |
| (...skipping 299 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 341 final arch = configuration['arch']; | 344 final arch = configuration['arch']; |
| 342 final checked = configuration['checked'] ? '-checked' : ''; | 345 final checked = configuration['checked'] ? '-checked' : ''; |
| 343 return "$compiler-$runtime$checked ${mode}_$arch"; | 346 return "$compiler-$runtime$checked ${mode}_$arch"; |
| 344 } | 347 } |
| 345 | 348 |
| 346 List<String> get batchRunnerArguments => ['-batch']; | 349 List<String> get batchRunnerArguments => ['-batch']; |
| 347 List<String> get batchTestArguments => commands.last.arguments; | 350 List<String> get batchTestArguments => commands.last.arguments; |
| 348 | 351 |
| 349 bool get usesWebDriver => TestUtils.usesWebDriver(configuration['runtime']); | 352 bool get usesWebDriver => TestUtils.usesWebDriver(configuration['runtime']); |
| 350 | 353 |
| 354 bool get usesBrowserController => configuration['use_browser_controller']; |
| 355 |
| 351 void completed() { completedHandler(this); } | 356 void completed() { completedHandler(this); } |
| 352 | 357 |
| 353 bool get isFlaky { | 358 bool get isFlaky { |
| 354 if (expectedOutcomes.contains(SKIP)) { | 359 if (expectedOutcomes.contains(SKIP)) { |
| 355 return false; | 360 return false; |
| 356 } | 361 } |
| 357 | 362 |
| 358 var flags = new Set.from(expectedOutcomes); | 363 var flags = new Set.from(expectedOutcomes); |
| 359 flags..remove(TIMEOUT) | 364 flags..remove(TIMEOUT) |
| 360 ..remove(SLOW); | 365 ..remove(SLOW); |
| (...skipping 158 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 519 } | 524 } |
| 520 factory CommandOutputImpl.fromCase(TestCase testCase, | 525 factory CommandOutputImpl.fromCase(TestCase testCase, |
| 521 Command command, | 526 Command command, |
| 522 int exitCode, | 527 int exitCode, |
| 523 bool incomplete, | 528 bool incomplete, |
| 524 bool timedOut, | 529 bool timedOut, |
| 525 List<int> stdout, | 530 List<int> stdout, |
| 526 List<int> stderr, | 531 List<int> stderr, |
| 527 Duration time, | 532 Duration time, |
| 528 bool compilationSkipped) { | 533 bool compilationSkipped) { |
| 529 if (testCase is BrowserTestCase) { | 534 if (testCase.usesBrowserController) { |
| 535 return new HTMLBrowserCommandOutputImpl(testCase, |
| 536 command, |
| 537 exitCode, |
| 538 incomplete, |
| 539 timedOut, |
| 540 stdout, |
| 541 stderr, |
| 542 time, |
| 543 compilationSkipped); |
| 544 } else if (testCase is BrowserTestCase) { |
| 530 return new BrowserCommandOutputImpl(testCase, | 545 return new BrowserCommandOutputImpl(testCase, |
| 531 command, | 546 command, |
| 532 exitCode, | 547 exitCode, |
| 533 incomplete, | 548 incomplete, |
| 534 timedOut, | 549 timedOut, |
| 535 stdout, | 550 stdout, |
| 536 stderr, | 551 stderr, |
| 537 time, | 552 time, |
| 538 compilationSkipped); | 553 compilationSkipped); |
| 539 } else if (testCase.configuration['analyzer']) { | 554 } else if (testCase.configuration['analyzer']) { |
| (...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 739 return (exitCode != 0 && !hasCrashed); | 754 return (exitCode != 0 && !hasCrashed); |
| 740 } | 755 } |
| 741 } | 756 } |
| 742 break; | 757 break; |
| 743 } | 758 } |
| 744 } | 759 } |
| 745 return true; | 760 return true; |
| 746 } | 761 } |
| 747 } | 762 } |
| 748 | 763 |
| 764 class HTMLBrowserCommandOutputImpl extends BrowserCommandOutputImpl { |
| 765 HTMLBrowserCommandOutputImpl( |
| 766 testCase, |
| 767 command, |
| 768 exitCode, |
| 769 incomplete, |
| 770 timedOut, |
| 771 stdout, |
| 772 stderr, |
| 773 time, |
| 774 compilationSkipped) : |
| 775 super(testCase, |
| 776 command, |
| 777 exitCode, |
| 778 incomplete, |
| 779 timedOut, |
| 780 stdout, |
| 781 stderr, |
| 782 time, |
| 783 compilationSkipped); |
| 784 |
| 785 bool get _browserTestFailure { |
| 786 // We should not need to convert back and forward. |
| 787 var output = decodeUtf8(super.stdout); |
| 788 if (output.contains("FAIL")) return true; |
| 789 return !output.contains("PASS"); |
| 790 } |
| 791 } |
| 792 |
| 793 |
| 749 // The static analyzer does not actually execute code, so | 794 // The static analyzer does not actually execute code, so |
| 750 // the criteria for success now depend on the text sent | 795 // the criteria for success now depend on the text sent |
| 751 // to stderr. | 796 // to stderr. |
| 752 class AnalysisCommandOutputImpl extends CommandOutputImpl { | 797 class AnalysisCommandOutputImpl extends CommandOutputImpl { |
| 753 // An error line has 8 fields that look like: | 798 // An error line has 8 fields that look like: |
| 754 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. | 799 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. |
| 755 final int ERROR_LEVEL = 0; | 800 final int ERROR_LEVEL = 0; |
| 756 final int ERROR_TYPE = 1; | 801 final int ERROR_TYPE = 1; |
| 757 final int FORMATTED_ERROR = 7; | 802 final int FORMATTED_ERROR = 7; |
| 758 | 803 |
| (...skipping 529 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1288 List<EventListener> _eventListener; | 1333 List<EventListener> _eventListener; |
| 1289 | 1334 |
| 1290 // For dartc/selenium batch processing we keep a list of batch processes. | 1335 // For dartc/selenium batch processing we keep a list of batch processes. |
| 1291 Map<String, List<BatchRunnerProcess>> _batchProcesses; | 1336 Map<String, List<BatchRunnerProcess>> _batchProcesses; |
| 1292 | 1337 |
| 1293 // Cache information about test cases per test suite. For multiple | 1338 // Cache information about test cases per test suite. For multiple |
| 1294 // configurations there is no need to repeatedly search the file | 1339 // configurations there is no need to repeatedly search the file |
| 1295 // system, generate tests, and search test files for options. | 1340 // system, generate tests, and search test files for options. |
| 1296 Map<String, List<TestInformation>> _testCache; | 1341 Map<String, List<TestInformation>> _testCache; |
| 1297 | 1342 |
| 1343 Map<String, BrowserTestRunner> _browserTestRunners; |
| 1344 |
| 1298 /** | 1345 /** |
| 1299 * String indicating the browser used to run the tests. Empty if no browser | 1346 * String indicating the browser used to run the tests. Empty if no browser |
| 1300 * used. | 1347 * used. |
| 1301 */ | 1348 */ |
| 1302 String browserUsed = ''; | 1349 String browserUsed = ''; |
| 1303 | 1350 |
| 1304 /** | 1351 /** |
| 1305 * Process running the selenium server .jar (only used for Safari and Opera | 1352 * Process running the selenium server .jar (only used for Safari and Opera |
| 1306 * tests.) | 1353 * tests.) |
| 1307 */ | 1354 */ |
| (...skipping 10 matching lines...) Expand all Loading... |
| 1318 DateTime startTime, | 1365 DateTime startTime, |
| 1319 testSuites, | 1366 testSuites, |
| 1320 this._eventListener, | 1367 this._eventListener, |
| 1321 this._allDone, | 1368 this._allDone, |
| 1322 [bool verbose = false, | 1369 [bool verbose = false, |
| 1323 bool listTests = false]) | 1370 bool listTests = false]) |
| 1324 : _verbose = verbose, | 1371 : _verbose = verbose, |
| 1325 _listTests = listTests, | 1372 _listTests = listTests, |
| 1326 _tests = new Queue<TestCase>(), | 1373 _tests = new Queue<TestCase>(), |
| 1327 _batchProcesses = new Map<String, List<BatchRunnerProcess>>(), | 1374 _batchProcesses = new Map<String, List<BatchRunnerProcess>>(), |
| 1328 _testCache = new Map<String, List<TestInformation>>() { | 1375 _testCache = new Map<String, List<TestInformation>>(), |
| 1376 _browserTestRunners = new Map<String, BrowserTestRunner>() { |
| 1329 _runTests(testSuites); | 1377 _runTests(testSuites); |
| 1330 } | 1378 } |
| 1331 | 1379 |
| 1332 /** | 1380 /** |
| 1333 * Perform any cleanup needed once all tests in a TestSuite have completed | 1381 * Perform any cleanup needed once all tests in a TestSuite have completed |
| 1334 * and notify our progress indicator that we are done. | 1382 * and notify our progress indicator that we are done. |
| 1335 */ | 1383 */ |
| 1336 void _cleanupAndMarkDone() { | 1384 void _cleanupAndMarkDone() { |
| 1337 _allDone(); | 1385 _allDone(); |
| 1338 if (browserUsed != '' && _seleniumServer != null) { | 1386 if (browserUsed != '' && _seleniumServer != null) { |
| 1339 _seleniumServer.kill(); | 1387 _seleniumServer.kill(); |
| 1340 } | 1388 } |
| 1341 eventAllTestsDone(); | 1389 eventAllTestsDone(); |
| 1342 } | 1390 } |
| 1343 | 1391 |
| 1344 void _checkDone() { | 1392 void _checkDone() { |
| 1345 if (_allTestsWereEnqueued && _tests.isEmpty && _numProcesses == 0) { | 1393 if (_allTestsWereEnqueued && _tests.isEmpty && _numProcesses == 0) { |
| 1346 _terminateBatchRunners().then((_) => _cleanupAndMarkDone()); | 1394 _terminateBatchRunners().then((_) { |
| 1395 _terminateBrowserRunners().then((_) => _cleanupAndMarkDone()); |
| 1396 }); |
| 1347 } | 1397 } |
| 1348 } | 1398 } |
| 1349 | 1399 |
| 1350 void _runTests(List<TestSuite> testSuites) { | 1400 void _runTests(List<TestSuite> testSuites) { |
| 1351 // FIXME: For some reason we cannot call this method on all test suites | 1401 // FIXME: For some reason we cannot call this method on all test suites |
| 1352 // in parallel. | 1402 // in parallel. |
| 1353 // If we do, not all tests get enqueued (if --arch=all was specified, | 1403 // If we do, not all tests get enqueued (if --arch=all was specified, |
| 1354 // we don't get twice the number of tests [tested on -rvm -cnone]) | 1404 // we don't get twice the number of tests [tested on -rvm -cnone]) |
| 1355 // Issue: 7927 | 1405 // Issue: 7927 |
| 1356 Iterator<TestSuite> iterator = testSuites.iterator; | 1406 Iterator<TestSuite> iterator = testSuites.iterator; |
| (...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1499 var futures = new List(); | 1549 var futures = new List(); |
| 1500 for (var runners in _batchProcesses.values) { | 1550 for (var runners in _batchProcesses.values) { |
| 1501 for (var runner in runners) { | 1551 for (var runner in runners) { |
| 1502 futures.add(runner.terminate()); | 1552 futures.add(runner.terminate()); |
| 1503 } | 1553 } |
| 1504 } | 1554 } |
| 1505 // Change to Future.wait when updating binaries. | 1555 // Change to Future.wait when updating binaries. |
| 1506 return Future.wait(futures); | 1556 return Future.wait(futures); |
| 1507 } | 1557 } |
| 1508 | 1558 |
| 1559 Future _terminateBrowserRunners() { |
| 1560 var futures = []; |
| 1561 for (BrowserTestRunner runner in _browserTestRunners.values) { |
| 1562 futures.add(runner.terminate()); |
| 1563 } |
| 1564 return Future.wait(futures); |
| 1565 } |
| 1566 |
| 1509 BatchRunnerProcess _getBatchRunner(TestCase test) { | 1567 BatchRunnerProcess _getBatchRunner(TestCase test) { |
| 1510 // Start batch processes if needed | 1568 // Start batch processes if needed |
| 1511 var compiler = test.configuration['compiler']; | 1569 var compiler = test.configuration['compiler']; |
| 1512 var runners = _batchProcesses[compiler]; | 1570 var runners = _batchProcesses[compiler]; |
| 1513 if (runners == null) { | 1571 if (runners == null) { |
| 1514 runners = new List<BatchRunnerProcess>(_maxProcesses); | 1572 runners = new List<BatchRunnerProcess>(_maxProcesses); |
| 1515 for (int i = 0; i < _maxProcesses; i++) { | 1573 for (int i = 0; i < _maxProcesses; i++) { |
| 1516 runners[i] = new BatchRunnerProcess(test); | 1574 runners[i] = new BatchRunnerProcess(test); |
| 1517 } | 1575 } |
| 1518 _batchProcesses[compiler] = runners; | 1576 _batchProcesses[compiler] = runners; |
| 1519 } | 1577 } |
| 1520 | 1578 |
| 1521 for (var runner in runners) { | 1579 for (var runner in runners) { |
| 1522 if (!runner.active) return runner; | 1580 if (!runner.active) return runner; |
| 1523 } | 1581 } |
| 1524 throw new Exception('Unable to find inactive batch runner.'); | 1582 throw new Exception('Unable to find inactive batch runner.'); |
| 1525 } | 1583 } |
| 1526 | 1584 |
| 1585 Future<BrowserTestRunner> _getBrowserTestRunner(TestCase test) { |
| 1586 var runtime = test.configuration['runtime']; |
| 1587 if (_browserTestRunners[runtime] == null) { |
| 1588 var testRunner = |
| 1589 new BrowserTestRunner(runtime, NUMBER_OF_BROWSERCONTROLLER_BROWSERS); |
| 1590 _browserTestRunners[runtime] = testRunner; |
| 1591 return testRunner.start().then((started) { |
| 1592 if (started) { |
| 1593 return testRunner; |
| 1594 } |
| 1595 print("Issue starting browser test runner"); |
| 1596 exit(1); |
| 1597 }); |
| 1598 } |
| 1599 return new Future.immediate(_browserTestRunners[runtime]); |
| 1600 } |
| 1601 |
| 1602 void _startBrowserControllerTest(var test) { |
| 1603 // Get the url. |
| 1604 // TODO(ricow): This is not needed when we have eliminated selenium. |
| 1605 var nextCommandIndex = test.commandOutputs.keys.length; |
| 1606 var url = test.commands[nextCommandIndex].toString().split("--out=")[1]; |
| 1607 // Remove trailing " |
| 1608 url = url.split('"')[0]; |
| 1609 var callback = (var output) { |
| 1610 new CommandOutput.fromCase(test, |
| 1611 test.commands[nextCommandIndex], |
| 1612 0, |
| 1613 false, |
| 1614 output == "TIMEOUT", |
| 1615 encodeUtf8(output), |
| 1616 [], |
| 1617 const Duration(seconds: 1), |
| 1618 false); |
| 1619 test.completedHandler(test); |
| 1620 }; |
| 1621 BrowserTest browserTest = new BrowserTest(url, callback, test.timeout); |
| 1622 _getBrowserTestRunner(test).then((testRunner) { |
| 1623 testRunner.queueTest(browserTest); |
| 1624 }); |
| 1625 } |
| 1626 |
| 1527 void _tryRunTest() { | 1627 void _tryRunTest() { |
| 1528 _checkDone(); | 1628 _checkDone(); |
| 1629 // TODO(ricow): remove most of the hacked selenium code below when |
| 1630 // we have eliminated the need. |
| 1631 |
| 1529 if (_numProcesses < _maxProcesses && !_tests.isEmpty) { | 1632 if (_numProcesses < _maxProcesses && !_tests.isEmpty) { |
| 1530 TestCase test = _tests.removeFirst(); | 1633 TestCase test = _tests.removeFirst(); |
| 1531 if (_listTests) { | 1634 if (_listTests) { |
| 1532 var fields = [test.displayName, | 1635 var fields = [test.displayName, |
| 1533 test.expectedOutcomes.join(','), | 1636 test.expectedOutcomes.join(','), |
| 1534 test.isNegative.toString()]; | 1637 test.isNegative.toString()]; |
| 1535 fields.addAll(test.commands.last.arguments); | 1638 fields.addAll(test.commands.last.arguments); |
| 1536 print(fields.join('\t')); | 1639 print(fields.join('\t')); |
| 1537 return; | 1640 return; |
| 1538 } | 1641 } |
| 1539 if (test.usesWebDriver && _needsSelenium && !_isSeleniumAvailable || (test | 1642 if (test.usesWebDriver && _needsSelenium && !_isSeleniumAvailable || |
| 1540 is BrowserTestCase && test.waitingForOtherTest)) { | 1643 (test is BrowserTestCase && test.waitingForOtherTest)) { |
| 1541 // The test is not yet ready to run. Put the test back in | 1644 // The test is not yet ready to run. Put the test back in |
| 1542 // the queue. Avoid spin-polling by using a timeout. | 1645 // the queue. Avoid spin-polling by using a timeout. |
| 1543 _tests.add(test); | 1646 _tests.add(test); |
| 1544 new Timer(new Duration(milliseconds: 100), | 1647 new Timer(new Duration(milliseconds: 100), |
| 1545 _tryRunTest); // Don't lose a process. | 1648 _tryRunTest); // Don't lose a process. |
| 1546 return; | 1649 return; |
| 1547 } | 1650 } |
| 1548 // Before running any commands, we print out all commands if '--verbose' | 1651 // Before running any commands, we print out all commands if '--verbose' |
| 1549 // was specified. | 1652 // was specified. |
| 1550 if (_verbose && test.commandOutputs.length == 0) { | 1653 if (_verbose && test.commandOutputs.length == 0) { |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1591 _numProcesses--; | 1694 _numProcesses--; |
| 1592 if (isBrowserCommand) { | 1695 if (isBrowserCommand) { |
| 1593 _numBrowserProcesses--; | 1696 _numBrowserProcesses--; |
| 1594 } | 1697 } |
| 1595 eventFinishedTestCase(test_arg); | 1698 eventFinishedTestCase(test_arg); |
| 1596 if (test_arg is BrowserTestCase) test_arg.notifyObservers(); | 1699 if (test_arg is BrowserTestCase) test_arg.notifyObservers(); |
| 1597 oldCallback(test_arg); | 1700 oldCallback(test_arg); |
| 1598 _tryRunTest(); | 1701 _tryRunTest(); |
| 1599 }; | 1702 }; |
| 1600 test.completedHandler = testCompleted; | 1703 test.completedHandler = testCompleted; |
| 1601 _getBatchRunner(test).startTest(test); | 1704 |
| 1705 if (test.usesBrowserController) { |
| 1706 _startBrowserControllerTest(test); |
| 1707 } else { |
| 1708 _getBatchRunner(test).startTest(test); |
| 1709 } |
| 1602 } else { | 1710 } else { |
| 1603 // Once we've actually failed a test, technically, we wouldn't need to | 1711 // Once we've actually failed a test, technically, we wouldn't need to |
| 1604 // bother retrying any subsequent tests since the bot is already red. | 1712 // bother retrying any subsequent tests since the bot is already red. |
| 1605 // However, we continue to retry tests until we have actually failed | 1713 // However, we continue to retry tests until we have actually failed |
| 1606 // four tests (arbitrarily chosen) for more debugable output, so that | 1714 // four tests (arbitrarily chosen) for more debugable output, so that |
| 1607 // the developer doesn't waste his or her time trying to fix a bunch of | 1715 // the developer doesn't waste his or her time trying to fix a bunch of |
| 1608 // tests that appear to be broken but were actually just flakes that | 1716 // tests that appear to be broken but were actually just flakes that |
| 1609 // didn't get retried because there had already been one failure. | 1717 // didn't get retried because there had already been one failure. |
| 1610 bool allowRetry = _MAX_FAILED_NO_RETRY > _numFailedTests; | 1718 bool allowRetry = _MAX_FAILED_NO_RETRY > _numFailedTests; |
| 1611 runNextCommandWithRetries(test, allowRetry).then((TestCase testCase) { | 1719 runNextCommandWithRetries(test, allowRetry).then((TestCase testCase) { |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1737 } | 1845 } |
| 1738 } | 1846 } |
| 1739 | 1847 |
| 1740 void eventAllTestsDone() { | 1848 void eventAllTestsDone() { |
| 1741 for (var listener in _eventListener) { | 1849 for (var listener in _eventListener) { |
| 1742 listener.allDone(); | 1850 listener.allDone(); |
| 1743 } | 1851 } |
| 1744 } | 1852 } |
| 1745 } | 1853 } |
| 1746 | 1854 |
| OLD | NEW |