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

Side by Side Diff: tools/testing/dart/test_runner.dart

Issue 12035053: Support for running a limited amount of browser tests in parallel (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« tools/test-runtime.dart ('K') | « tools/test-runtime.dart ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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.
(...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after
547 timedOut, 547 timedOut,
548 stdout, 548 stdout,
549 stderr, 549 stderr,
550 time, 550 time,
551 compilationSkipped); 551 compilationSkipped);
552 } 552 }
553 553
554 String get result => 554 String get result =>
555 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); 555 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS));
556 556
557 bool get unexpectedOutput => !testCase.expectedOutcomes.contains(result); 557 bool get unexpectedOutput {
ricow1 2013/01/24 09:04:28 can't we just override this in the BrowserCommandO
kustermann 2013/01/29 08:43:32 I got rid of this code now.
558 if (testCase.commandOutputs[testCase.commands.last] == this) {
559 // This [this] is the CommandOutput instance for the browser command
560 // (the last one)
561 return !testCase.expectedOutcomes.contains(result);
562 } else {
563 // This has to be the compilation command.
564 return exitCode != 0;
ricow1 2013/01/24 09:04:28 was this wrong before?
kustermann 2013/01/29 08:43:32 I'm not 100% sure. My guess: We only used 'testCas
565 }
566 }
558 567
559 bool get hasCrashed { 568 bool get hasCrashed {
560 // The Java dartc runner and dart2js exits with code 253 in case 569 // The Java dartc runner and dart2js exits with code 253 in case
561 // of unhandled exceptions. 570 // of unhandled exceptions.
562 if (exitCode == 253) return true; 571 if (exitCode == 253) return true;
563 if (io.Platform.operatingSystem == 'windows') { 572 if (io.Platform.operatingSystem == 'windows') {
564 // The VM uses std::abort to terminate on asserts. 573 // The VM uses std::abort to terminate on asserts.
565 // std::abort terminates with exit code 3 on Windows. 574 // std::abort terminates with exit code 3 on Windows.
566 if (exitCode == 3) { 575 if (exitCode == 3) {
567 return !timedOut; 576 return !timedOut;
(...skipping 350 matching lines...) Expand 10 before | Expand all | Expand 10 after
918 * A RunningProcess actually runs a test, getting the command lines from 927 * A RunningProcess actually runs a test, getting the command lines from
919 * its [TestCase], starting the test process (and first, a compilation 928 * its [TestCase], starting the test process (and first, a compilation
920 * process if the TestCase is a [BrowserTestCase]), creating a timeout 929 * process if the TestCase is a [BrowserTestCase]), creating a timeout
921 * timer, and recording the results in a new [CommandOutput] object, which it 930 * timer, and recording the results in a new [CommandOutput] object, which it
922 * attaches to the TestCase. The lifetime of the RunningProcess is limited 931 * attaches to the TestCase. The lifetime of the RunningProcess is limited
923 * to the time it takes to start the process, run the process, and record 932 * to the time it takes to start the process, run the process, and record
924 * the result; there are no pointers to it, so it should be available to 933 * the result; there are no pointers to it, so it should be available to
925 * be garbage collected as soon as it is done. 934 * be garbage collected as soon as it is done.
926 */ 935 */
927 class RunningProcess { 936 class RunningProcess {
928 ProcessQueue processQueue;
929 io.Process process;
930 TestCase testCase; 937 TestCase testCase;
938 Command command;
931 bool timedOut = false; 939 bool timedOut = false;
932 Date startTime; 940 Date startTime;
933 Timer timeoutTimer; 941 Timer timeoutTimer;
934 List<int> stdout; 942 List<int> stdout = <int>[];
935 List<int> stderr; 943 List<int> stderr = <int>[];
936 List<String> notifications; 944 bool compilationSkipped = false;
937 bool compilationSkipped; 945 Completer<CommandOutput> completer;
938 bool allowRetries;
939 946
940 /** Which command of [testCase.commands] is currently being executed. */ 947 RunningProcess(TestCase this.testCase, Command this.command);
941 int currentStep;
942 948
943 RunningProcess(TestCase this.testCase, 949 Future<CommandOutput> start() {
944 [this.allowRetries = false, this.processQueue]); 950 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP));
945 951
946 /** 952 completer = new Completer<CommandOutput>();
947 * Called when all commands are executed. 953 startTime = new Date.now();
948 */ 954 _runCommand();
949 void testComplete(CommandOutput lastCommandOutput) { 955 return completer.future;
950 var command = lastCommandOutput.command; 956 }
951 957
958 void _runCommand() {
959 command.outputIsUpToDate.then((bool isUpToDate) {
ricow1 2013/01/24 09:04:28 I would just make this function return the future,
kustermann 2013/01/29 08:43:32 We need a completer anyway because we get the 'pro
960 if (isUpToDate) {
961 compilationSkipped = true;
962 _commandComplete(0);
963 } else {
964 var processOptions = _createProcessOptions();
965 Future processFuture = io.Process.start(command.executable,
966 command.arguments,
967 processOptions);
968 processFuture.then((io.Process process) {
969 void timeoutHandler(Timer unusedTimer) {
970 timedOut = true;
971 if (process != null) {
972 try {
973 process.kill();
974 } on io.ProcessException {
975 // Hopefully, this means that the process died on its own.
976 }
977 }
978 }
979 process.onExit = _commandComplete;
980 _drainStream(process.stdout, stdout);
981 _drainStream(process.stderr, stderr);
982 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler);
983 }).catchError((e) {
984 print("Process error:");
985 print(" Command: $command");
986 print(" Error: $e");
987 _commandComplete(-1);
988 return true;
989 });
990 }
991 });
992 }
993
994 void _commandComplete(int exitCode) {
952 if (timeoutTimer != null) { 995 if (timeoutTimer != null) {
953 timeoutTimer.cancel(); 996 timeoutTimer.cancel();
954 } 997 }
955 if (lastCommandOutput.unexpectedOutput 998 var commandOutput = _createCommandOutput(command, exitCode);
956 && testCase.configuration['verbose'] != null 999 completer.complete(commandOutput);
957 && testCase.configuration['verbose']) {
958 print(testCase.displayName);
959
960 print(decodeUtf8(lastCommandOutput.stderr));
961 if (!lastCommandOutput.command.isPixelTest) {
962 print(decodeUtf8(lastCommandOutput.stdout));
963 } else {
964 print("DRT pixel test failed! stdout is not printed because it "
965 "contains binary data!");
966 }
967 print('');
968 if (notifications.length > 0) {
969 print("Notifications:");
970 for (var line in notifications) {
971 print(notifications);
972 }
973 print('');
974 }
975 }
976 if (allowRetries && testCase.usesWebDriver
977 && lastCommandOutput.unexpectedOutput
978 && (testCase as BrowserTestCase).numRetries > 0) {
979 // Selenium tests can be flaky. Try rerunning.
980 lastCommandOutput.requestRetry = true;
981 }
982 if (lastCommandOutput.requestRetry) {
983 lastCommandOutput.requestRetry = false;
984 this.timedOut = false;
985 (testCase as BrowserTestCase).numRetries--;
986 print("Potential flake. Re-running ${testCase.displayName} "
987 "(${(testCase as BrowserTestCase).numRetries} attempt(s) remains)");
988 // When retrying we need to reset the timeout as well.
989 // Otherwise there will be no timeout handling for the retry.
990 timeoutTimer = null;
991 this.start();
992 } else {
993 testCase.completed();
994 }
995 } 1000 }
996 1001
997 /** 1002 CommandOutput _createCommandOutput(Command command, int exitCode) {
998 * Process exit handler called at the end of every command. It internally 1003 var incomplete = command != testCase.commands.last;
999 * treats all but the last command as compilation steps. The last command is
1000 * the actual test and its output is analyzed in [testComplete].
1001 */
1002 void commandComplete(Command command, int exitCode) {
1003 process = null;
1004 int totalSteps = testCase.commands.length;
1005 String suffix =' (step $currentStep of $totalSteps)';
1006 if (timedOut) {
1007 // Non-webdriver test timed out before it could complete. Webdriver tests
1008 // run their own timeouts by timing from the launch of the browser (which
1009 // could be delayed).
1010 testComplete(createCommandOutput(command, 0, true));
1011 } else if (currentStep == totalSteps) {
1012 // Done with all test commands.
1013 testComplete(createCommandOutput(command, exitCode, false));
1014 } else if (exitCode != 0) {
1015 // One of the steps failed.
1016 notifications.add('test.dart: Compilation failed$suffix, '
1017 'exit code $exitCode\n');
1018 testComplete(createCommandOutput(command, exitCode, true));
1019 } else {
1020 createCommandOutput(command, exitCode, true);
1021 // One compilation step successfully completed, move on to the
1022 // next step.
1023 notifications.add('test.dart: Compilation finished $suffix\n\n');
1024 if (currentStep == totalSteps - 1 && testCase.usesWebDriver &&
1025 !testCase.configuration['noBatch']) {
1026 // Note: processQueue will always be non-null for runtime == ie9, ie10,
1027 // ff, safari, chrome, opera. (It is only null for runtime == vm)
1028 // This RunningProcess object is done, and hands over control to
1029 // BatchRunner.startTest(), which handles reporting, etc.
1030 if (timeoutTimer != null) {
1031 timeoutTimer.cancel();
1032 }
1033 processQueue._getBatchRunner(testCase).startTest(testCase);
1034 } else {
1035 runCommand(testCase.commands[currentStep++], commandComplete);
1036 }
1037 }
1038 }
1039
1040 /**
1041 * Called for all executed commands.
1042 */
1043 CommandOutput createCommandOutput(Command command,
1044 int exitCode,
1045 bool incomplete) {
1046 var commandOutput = new CommandOutput.fromCase( 1004 var commandOutput = new CommandOutput.fromCase(
1047 testCase, 1005 testCase,
1048 command, 1006 command,
1049 exitCode, 1007 exitCode,
1050 incomplete, 1008 incomplete,
1051 timedOut, 1009 timedOut,
1052 stdout, 1010 stdout,
1053 stderr, 1011 stderr,
1054 new Date.now().difference(startTime), 1012 new Date.now().difference(startTime),
1055 compilationSkipped); 1013 compilationSkipped);
1056 resetLocalOutputInformation();
1057 return commandOutput; 1014 return commandOutput;
1058 } 1015 }
1059 1016
1060 void resetLocalOutputInformation() { 1017 void _drainStream(io.InputStream source, List<int> destination) {
1061 stdout = new List<int>();
1062 stderr = new List<int>();
1063 notifications = new List<String>();
1064 compilationSkipped = false;
1065 }
1066
1067 void drainStream(io.InputStream source, List<int> destination) {
1068 void onDataHandler () { 1018 void onDataHandler () {
1069 if (source.closed) { 1019 if (source.closed) {
1070 return; // TODO(whesse): Remove when bug is fixed. 1020 return; // TODO(whesse): Remove when bug is fixed.
1071 } 1021 }
1072 var data = source.read(); 1022 var data = source.read();
1073 while (data != null) { 1023 while (data != null) {
1074 destination.addAll(data); 1024 destination.addAll(data);
1075 data = source.read(); 1025 data = source.read();
1076 } 1026 }
1077 } 1027 }
1078 source.onData = onDataHandler; 1028 source.onData = onDataHandler;
1079 source.onClosed = onDataHandler; 1029 source.onClosed = onDataHandler;
1080 } 1030 }
1081 1031
1082 void start() { 1032 io.ProcessOptions _createProcessOptions() {
1083 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); 1033 var baseEnvironment = command.environment != null ?
1084 resetLocalOutputInformation(); 1034 command.environment : io.Platform.environment;
1085 currentStep = 0; 1035 io.ProcessOptions options = new io.ProcessOptions();
1086 startTime = new Date.now(); 1036 options.environment = new Map<String, String>.from(baseEnvironment);
1087 runCommand(testCase.commands[currentStep++], commandComplete); 1037 options.environment['DART_CONFIGURATION'] =
1088 } 1038 TestUtils.configurationDir(testCase.configuration);
1089 1039 return options;
1090 void runCommand(Command command, void commandCompleteHandler(Command, int)) {
1091 void processExitHandler(int returnCode) {
1092 commandCompleteHandler(command, returnCode);
1093 }
1094
1095 command.outputIsUpToDate.then((bool isUpToDate) {
1096 if (isUpToDate) {
1097 notifications.add("Skipped compilation because the old output is "
1098 "still up to date!");
1099 compilationSkipped = true;
1100 commandComplete(command, 0);
1101 } else {
1102 io.ProcessOptions options = new io.ProcessOptions();
1103 if (command.environment != null) {
1104 options.environment =
1105 new Map<String, String>.from(command.environment);
1106 } else {
1107 options.environment =
1108 new Map<String, String>.from(io.Platform.environment);
1109 }
1110
1111 options.environment['DART_CONFIGURATION'] =
1112 TestUtils.configurationDir(testCase.configuration);
1113 Future processFuture = io.Process.start(command.executable,
1114 command.arguments,
1115 options);
1116 processFuture.then((io.Process p) {
1117 process = p;
1118 process.onExit = processExitHandler;
1119 drainStream(process.stdout, stdout);
1120 drainStream(process.stderr, stderr);
1121 if (timeoutTimer == null) {
1122 // Create one timeout timer when starting test case, remove it at
1123 // the end.
1124 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler);
1125 }
1126 // If the timeout fired in between two commands, kill the just
1127 // started process immediately.
1128 if (timedOut) safeKill(process);
1129 }).catchError((e) {
1130 print("Process error:");
1131 print(" Command: $command");
1132 print(" Error: $e");
1133 testComplete(createCommandOutput(command, -1, false));
1134 return true;
1135 });
1136 }
1137 });
1138 }
1139
1140 void timeoutHandler(Timer unusedTimer) {
1141 timedOut = true;
1142 safeKill(process);
1143 }
1144
1145 void safeKill(io.Process p) {
1146 if (p != null) {
1147 try {
1148 p.kill();
1149 } on io.ProcessException {
1150 // Hopefully, this means that the process died on its own.
1151 }
1152 }
1153 } 1040 }
1154 } 1041 }
1155 1042
1156 /** 1043 /**
1157 * This class holds a value, that can be changed. It is used when 1044 * This class holds a value, that can be changed. It is used when
1158 * closures need a shared value, that they can all change and read. 1045 * closures need a shared value, that they can all change and read.
1159 */ 1046 */
1160 class MutableValue<T> { 1047 class MutableValue<T> {
1161 MutableValue(T this.value); 1048 MutableValue(T this.value);
1162 T value; 1049 T value;
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
1427 * have completed. 1314 * have completed.
1428 * 1315 *
1429 * Because multiple configurations may be run on each test suite, the 1316 * Because multiple configurations may be run on each test suite, the
1430 * ProcessQueue contains a cache in which a test suite may record information 1317 * ProcessQueue contains a cache in which a test suite may record information
1431 * about its list of tests, and may retrieve that information when it is called 1318 * about its list of tests, and may retrieve that information when it is called
1432 * upon to enqueue its tests again. 1319 * upon to enqueue its tests again.
1433 */ 1320 */
1434 class ProcessQueue { 1321 class ProcessQueue {
1435 int _numProcesses = 0; 1322 int _numProcesses = 0;
1436 int _maxProcesses; 1323 int _maxProcesses;
1324 int _numBrowserProcesses = 0;
1325 int _maxBrowserProcesses;
1437 bool _allTestsWereEnqueued = false; 1326 bool _allTestsWereEnqueued = false;
1438 1327
1439 /** The number of tests we allow to actually fail before we stop retrying. */ 1328 /** The number of tests we allow to actually fail before we stop retrying. */
1440 int _MAX_FAILED_NO_RETRY = 4; 1329 int _MAX_FAILED_NO_RETRY = 4;
1441 bool _verbose; 1330 bool _verbose;
1442 bool _listTests; 1331 bool _listTests;
1443 Function _allDone; 1332 Function _allDone;
1444 Queue<TestCase> _tests; 1333 Queue<TestCase> _tests;
1445 ProgressIndicator _progress; 1334 ProgressIndicator _progress;
1446 1335
(...skipping 16 matching lines...) Expand all
1463 * tests.) 1352 * tests.)
1464 */ 1353 */
1465 io.Process _seleniumServer = null; 1354 io.Process _seleniumServer = null;
1466 1355
1467 /** True if we are in the process of starting the server. */ 1356 /** True if we are in the process of starting the server. */
1468 bool _startingServer = false; 1357 bool _startingServer = false;
1469 1358
1470 /** True if we find that there is already a selenium jar running. */ 1359 /** True if we find that there is already a selenium jar running. */
1471 bool _seleniumAlreadyRunning = false; 1360 bool _seleniumAlreadyRunning = false;
1472 1361
1473 ProcessQueue(int this._maxProcesses, 1362 ProcessQueue(this._maxProcesses,
1363 this._maxBrowserProcesses,
1474 String progress, 1364 String progress,
1475 Date startTime, 1365 Date startTime,
1476 bool printTiming, 1366 bool printTiming,
1477 testSuites, 1367 testSuites,
1478 this._allDone, 1368 this._allDone,
1479 [bool verbose = false, 1369 [bool verbose = false,
1480 bool listTests = false]) 1370 bool listTests = false])
1481 : _verbose = verbose, 1371 : _verbose = verbose,
1482 _listTests = listTests, 1372 _listTests = listTests,
1483 _tests = new Queue<TestCase>(), 1373 _tests = new Queue<TestCase>(),
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after
1708 // the queue. Avoid spin-polling by using a timeout. 1598 // the queue. Avoid spin-polling by using a timeout.
1709 _tests.add(test); 1599 _tests.add(test);
1710 new Timer(100, (timer) {_tryRunTest();}); // Don't lose a process. 1600 new Timer(100, (timer) {_tryRunTest();}); // Don't lose a process.
1711 return; 1601 return;
1712 } 1602 }
1713 if (_verbose) { 1603 if (_verbose) {
1714 int i = 1; 1604 int i = 1;
1715 if (test is BrowserTestCase) { 1605 if (test is BrowserTestCase) {
1716 // Additional command for rerunning the steps locally after the fact. 1606 // Additional command for rerunning the steps locally after the fact.
1717 print('$i. ${TestUtils.dartTestExecutable.toNativePath()} ' 1607 print('$i. ${TestUtils.dartTestExecutable.toNativePath()} '
1718 '${TestUtils.dartDir().toNativePath()}/tools/testing/dart/' 1608 '${TestUtils.dartDir().toNativePath()}/tools/testing/dart/'
1719 'http_server.dart -m ${test.configuration["mode"]} ' 1609 'http_server.dart -m ${test.configuration["mode"]} '
1720 '-a ${test.configuration["arch"]} ' 1610 '-a ${test.configuration["arch"]} '
1721 '-p ${http_server.TestingServerRunner.serverList[0].port} ' 1611 '-p ${http_server.TestingServerRunner.serverList[0].port} '
1722 '-c ${http_server.TestingServerRunner.serverList[1].port}'); 1612 '-c ${http_server.TestingServerRunner.serverList[1].port}');
1723 i++; 1613 i++;
1724 } 1614 }
1725 for (Command command in test.commands) { 1615 for (Command command in test.commands) {
1726 print('$i. ${command.commandLine}'); 1616 print('$i. ${command.commandLine}');
1727 i++; 1617 i++;
1728 } 1618 }
1729 } 1619 }
1620
1621 var isLastCommand = ((test.commands.length-1) == test.commandOutputs.lengt h);
ricow1 2013/01/24 09:04:28 long line
kustermann 2013/01/29 08:43:32 Done.
1622 var isBrowserCommand = isLastCommand && (test is BrowserTestCase);
1623 if (isBrowserCommand && _numBrowserProcesses == _maxBrowserProcesses) {
1624 // If there is no free browser runner, put it back into the queue.
ricow1 2013/01/24 09:04:28 I don't think this is an optimal solution: Before:
kustermann 2013/01/29 08:43:32 As discussed offline, we'll keep it that way.
1625 _tests.add(test);
1626 new Timer(100, (timer) {_tryRunTest();}); // Don't lose a process.
1627
1628 return;
1629 }
1630
1730 _progress.start(test); 1631 _progress.start(test);
1731 TestCaseEvent oldCallback = test.completedHandler;
1732 void wrapper(TestCase test_arg) {
1733 _numProcesses--;
1734 _progress.done(test_arg);
1735 if (test_arg is BrowserTestCase) test_arg.notifyObservers();
1736 _tryRunTest();
1737 oldCallback(test_arg);
1738 };
1739 test.completedHandler = wrapper;
1740 1632
1741 if ((test.configuration['compiler'] == 'dartc' && 1633 // Dartc and browser test commands can be run by a [BatchRunnerProcess]
1742 test.displayName != 'dartc/junit_tests') || 1634 var nextCommandIndex = test.commandOutputs.keys.length;
1743 (test.commands.length == 1 && test.usesWebDriver && 1635 var numberOfCommands = test.commands.length;
1744 !test.configuration['noBatch'])) { 1636 var useBatchRunnerForDartc = test.configuration['compiler'] == 'dartc' &&
1745 // Dartc and browser test cases that do not require a precompilation 1637 test.displayName != 'dartc/junit_tests';
1746 // step, start with the batch runner right away. 1638 var isWebdriverCommand = nextCommandIndex == (numberOfCommands - 1) &&
1639 test.usesWebDriver &&
1640 !test.configuration['noBatch'];
1641 if (useBatchRunnerForDartc || isWebdriverCommand) {
1642 TestCaseEvent oldCallback = test.completedHandler;
1643 void wrapper(TestCase test_arg) {
1644 _numProcesses--;
1645 if (isBrowserCommand) {
1646 _numBrowserProcesses--;
1647 }
1648 _progress.done(test_arg);
1649 if (test_arg is BrowserTestCase) test_arg.notifyObservers();
1650 oldCallback(test_arg);
1651 _tryRunTest();
1652 };
1653 test.completedHandler = wrapper;
1747 _getBatchRunner(test).startTest(test); 1654 _getBatchRunner(test).startTest(test);
1748 } else { 1655 } else {
1749 // Once we've actually failed a test, technically, we wouldn't need to 1656 // Once we've actually failed a test, technically, we wouldn't need to
1750 // bother retrying any subsequent tests since the bot is already red. 1657 // bother retrying any subsequent tests since the bot is already red.
1751 // However, we continue to retry tests until we have actually failed 1658 // However, we continue to retry tests until we have actually failed
1752 // four tests (arbitrarily chosen) for more debugable output, so that 1659 // four tests (arbitrarily chosen) for more debugable output, so that
1753 // the developer doesn't waste his or her time trying to fix a bunch of 1660 // the developer doesn't waste his or her time trying to fix a bunch of
1754 // tests that appear to be broken but were actually just flakes that 1661 // tests that appear to be broken but were actually just flakes that
1755 // didn't get retried because there had already been one failure. 1662 // didn't get retried because there had already been one failure.
1756 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; 1663 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests;
1757 new RunningProcess(test, allowRetry, this).start(); 1664 runNextCommandWithRetries(test, allowRetry).then((TestCase testCase) {
1665 _numProcesses--;
1666 if (isBrowserCommand) {
1667 _numBrowserProcesses--;
1668 }
1669 if (isTestCaseFinished(testCase)) {
1670 testCase.completed();
1671 _progress.done(testCase);
1672 if (testCase is BrowserTestCase) testCase.notifyObservers();
1673 } else {
1674 _tests.add(testCase);
1675 }
1676 _tryRunTest();
1677 });
1758 } 1678 }
1679
1759 _numProcesses++; 1680 _numProcesses++;
1681 if (isBrowserCommand) {
1682 _numBrowserProcesses++;
1683 }
1760 } 1684 }
1761 } 1685 }
1686
1687 bool isTestCaseFinished(TestCase testCase) {
1688 var numberOfCommandOutputs= testCase.commandOutputs.keys.length;
1689 var numberOfCommands = testCase.commands.length;
1690
1691 var lastCommandCompleted = (numberOfCommandOutputs == numberOfCommands);
1692 var unexpectedOutput = testCase.lastCommandOutput.unexpectedOutput;
1693 // NOTE: If this was the last command or there was unexpected output
1694 // we're done with the test.
1695 // Otherwise we need to enqueue it again into the test queue.
1696 if (lastCommandCompleted || unexpectedOutput) {
1697 var verbose = testCase.configuration['verbose'];
1698 if (unexpectedOutput && verbose != null && verbose) {
1699 print(testCase.displayName);
1700 print("stderr:");
1701 print(decodeUtf8(commandOutput.stderr));
1702 if (!command.isPixelTest) {
1703 print("stdout:");
1704 print(decodeUtf8(commandOutput.stdout));
1705 } else {
1706 print("");
1707 print("DRT pixel test failed! stdout is not printed because it "
1708 "contains binary data!");
1709 }
1710 }
1711 return true;
1712 } else {
1713 return false;
1714 }
1715 }
1716
1717 Future runNextCommandWithRetries(TestCase testCase, bool allowRetry) {
1718 var completer = new Completer();
1719
1720 var nextCommandIndex = testCase.commandOutputs.keys.length;
1721 var numberOfCommands = testCase.commands.length;
1722 Expect.isTrue(nextCommandIndex < numberOfCommands);
1723 var command = testCase.commands[nextCommandIndex];
1724
1725 void runCommand() {
1726 var runningProcess = new RunningProcess(testCase, command);
1727 runningProcess.start().then((CommandOutput commandOutput) {
1728 if (allowRetry && testCase.usesWebDriver
1729 && commandOutput.unexpectedOutput
1730 && (testCase as BrowserTestCase).numRetries > 0) {
1731 // Selenium tests can be flaky. Try rerunning.
1732 commandOutput.requestRetry = true;
1733 }
1734 if (commandOutput.requestRetry) {
1735 commandOutput.requestRetry = false;
1736 (testCase as BrowserTestCase).numRetries--;
1737 print("Potential flake. Re-running ${testCase.displayName} "
1738 "(${(testCase as BrowserTestCase).numRetries} "
1739 "attempt(s) remains)");
1740 runCommand();
1741 } else {
1742 completer.complete(testCase);
1743 }
1744 });
1745 }
1746 runCommand();
1747
1748 return completer.future;
1749 }
1762 } 1750 }
1751
OLDNEW
« tools/test-runtime.dart ('K') | « tools/test-runtime.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698