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

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, 10 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.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 907 matching lines...) Expand 10 before | Expand all | Expand 10 after
918 * A RunningProcess actually runs a test, getting the command lines from 918 * A RunningProcess actually runs a test, getting the command lines from
919 * its [TestCase], starting the test process (and first, a compilation 919 * its [TestCase], starting the test process (and first, a compilation
920 * process if the TestCase is a [BrowserTestCase]), creating a timeout 920 * process if the TestCase is a [BrowserTestCase]), creating a timeout
921 * timer, and recording the results in a new [CommandOutput] object, which it 921 * timer, and recording the results in a new [CommandOutput] object, which it
922 * attaches to the TestCase. The lifetime of the RunningProcess is limited 922 * 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 923 * 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 924 * the result; there are no pointers to it, so it should be available to
925 * be garbage collected as soon as it is done. 925 * be garbage collected as soon as it is done.
926 */ 926 */
927 class RunningProcess { 927 class RunningProcess {
928 ProcessQueue processQueue;
929 io.Process process;
930 TestCase testCase; 928 TestCase testCase;
929 Command command;
931 bool timedOut = false; 930 bool timedOut = false;
932 Date startTime; 931 Date startTime;
933 Timer timeoutTimer; 932 Timer timeoutTimer;
934 List<int> stdout; 933 List<int> stdout = <int>[];
935 List<int> stderr; 934 List<int> stderr = <int>[];
936 List<String> notifications; 935 bool compilationSkipped = false;
937 bool compilationSkipped; 936 Completer<CommandOutput> completer;
938 bool allowRetries;
939 937
940 /** Which command of [testCase.commands] is currently being executed. */ 938 RunningProcess(TestCase this.testCase, Command this.command);
941 int currentStep;
942 939
943 RunningProcess(TestCase this.testCase, 940 Future<CommandOutput> start() {
944 [this.allowRetries = false, this.processQueue]); 941 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP));
945 942
946 /** 943 completer = new Completer<CommandOutput>();
947 * Called when all commands are executed. 944 startTime = new Date.now();
948 */ 945 _runCommand();
949 void testComplete(CommandOutput lastCommandOutput) { 946 return completer.future;
950 var command = lastCommandOutput.command; 947 }
951 948
949 void _runCommand() {
950 command.outputIsUpToDate.then((bool isUpToDate) {
951 if (isUpToDate) {
952 compilationSkipped = true;
953 _commandComplete(0);
954 } else {
955 var processOptions = _createProcessOptions();
956 Future processFuture = io.Process.start(command.executable,
957 command.arguments,
958 processOptions);
959 processFuture.then((io.Process process) {
960 void timeoutHandler(Timer unusedTimer) {
961 timedOut = true;
962 if (process != null) {
963 try {
964 process.kill();
965 } on io.ProcessException {
966 // Hopefully, this means that the process died on its own.
967 }
968 }
969 }
970 process.onExit = _commandComplete;
971 _drainStream(process.stdout, stdout);
972 _drainStream(process.stderr, stderr);
973 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler);
974 }).catchError((e) {
975 print("Process error:");
976 print(" Command: $command");
977 print(" Error: $e");
978 _commandComplete(-1);
979 return true;
980 });
981 }
982 });
983 }
984
985 void _commandComplete(int exitCode) {
952 if (timeoutTimer != null) { 986 if (timeoutTimer != null) {
953 timeoutTimer.cancel(); 987 timeoutTimer.cancel();
954 } 988 }
955 if (lastCommandOutput.unexpectedOutput 989 var commandOutput = _createCommandOutput(command, exitCode);
956 && testCase.configuration['verbose'] != null 990 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 } 991 }
996 992
997 /** 993 CommandOutput _createCommandOutput(Command command, int exitCode) {
998 * Process exit handler called at the end of every command. It internally 994 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( 995 var commandOutput = new CommandOutput.fromCase(
1047 testCase, 996 testCase,
1048 command, 997 command,
1049 exitCode, 998 exitCode,
1050 incomplete, 999 incomplete,
1051 timedOut, 1000 timedOut,
1052 stdout, 1001 stdout,
1053 stderr, 1002 stderr,
1054 new Date.now().difference(startTime), 1003 new Date.now().difference(startTime),
1055 compilationSkipped); 1004 compilationSkipped);
1056 resetLocalOutputInformation();
1057 return commandOutput; 1005 return commandOutput;
1058 } 1006 }
1059 1007
1060 void resetLocalOutputInformation() { 1008 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 () { 1009 void onDataHandler () {
1069 if (source.closed) { 1010 if (source.closed) {
1070 return; // TODO(whesse): Remove when bug is fixed. 1011 return; // TODO(whesse): Remove when bug is fixed.
1071 } 1012 }
1072 var data = source.read(); 1013 var data = source.read();
1073 while (data != null) { 1014 while (data != null) {
1074 destination.addAll(data); 1015 destination.addAll(data);
1075 data = source.read(); 1016 data = source.read();
1076 } 1017 }
1077 } 1018 }
1078 source.onData = onDataHandler; 1019 source.onData = onDataHandler;
1079 source.onClosed = onDataHandler; 1020 source.onClosed = onDataHandler;
1080 } 1021 }
1081 1022
1082 void start() { 1023 io.ProcessOptions _createProcessOptions() {
1083 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); 1024 var baseEnvironment = command.environment != null ?
1084 resetLocalOutputInformation(); 1025 command.environment : io.Platform.environment;
1085 currentStep = 0; 1026 io.ProcessOptions options = new io.ProcessOptions();
1086 startTime = new Date.now(); 1027 options.environment = new Map<String, String>.from(baseEnvironment);
1087 runCommand(testCase.commands[currentStep++], commandComplete); 1028 options.environment['DART_CONFIGURATION'] =
1088 } 1029 TestUtils.configurationDir(testCase.configuration);
1089 1030 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 } 1031 }
1154 } 1032 }
1155 1033
1156 /** 1034 /**
1157 * This class holds a value, that can be changed. It is used when 1035 * 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. 1036 * closures need a shared value, that they can all change and read.
1159 */ 1037 */
1160 class MutableValue<T> { 1038 class MutableValue<T> {
1161 MutableValue(T this.value); 1039 MutableValue(T this.value);
1162 T value; 1040 T value;
(...skipping 264 matching lines...) Expand 10 before | Expand all | Expand 10 after
1427 * have completed. 1305 * have completed.
1428 * 1306 *
1429 * Because multiple configurations may be run on each test suite, the 1307 * Because multiple configurations may be run on each test suite, the
1430 * ProcessQueue contains a cache in which a test suite may record information 1308 * 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 1309 * about its list of tests, and may retrieve that information when it is called
1432 * upon to enqueue its tests again. 1310 * upon to enqueue its tests again.
1433 */ 1311 */
1434 class ProcessQueue { 1312 class ProcessQueue {
1435 int _numProcesses = 0; 1313 int _numProcesses = 0;
1436 int _maxProcesses; 1314 int _maxProcesses;
1315 int _numBrowserProcesses = 0;
Mads Ager (google) 2013/01/29 10:47:48 Maybe split this into numIEProcesses and a utility
kustermann 2013/01/29 12:10:49 I could make "_maxBrowserProcesses" a map from bro
1316 int _maxBrowserProcesses;
1437 bool _allTestsWereEnqueued = false; 1317 bool _allTestsWereEnqueued = false;
1438 1318
1439 /** The number of tests we allow to actually fail before we stop retrying. */ 1319 /** The number of tests we allow to actually fail before we stop retrying. */
1440 int _MAX_FAILED_NO_RETRY = 4; 1320 int _MAX_FAILED_NO_RETRY = 4;
1441 bool _verbose; 1321 bool _verbose;
1442 bool _listTests; 1322 bool _listTests;
1443 Function _allDone; 1323 Function _allDone;
1444 Queue<TestCase> _tests; 1324 Queue<TestCase> _tests;
1445 ProgressIndicator _progress; 1325 ProgressIndicator _progress;
1446 1326
(...skipping 16 matching lines...) Expand all
1463 * tests.) 1343 * tests.)
1464 */ 1344 */
1465 io.Process _seleniumServer = null; 1345 io.Process _seleniumServer = null;
1466 1346
1467 /** True if we are in the process of starting the server. */ 1347 /** True if we are in the process of starting the server. */
1468 bool _startingServer = false; 1348 bool _startingServer = false;
1469 1349
1470 /** True if we find that there is already a selenium jar running. */ 1350 /** True if we find that there is already a selenium jar running. */
1471 bool _seleniumAlreadyRunning = false; 1351 bool _seleniumAlreadyRunning = false;
1472 1352
1473 ProcessQueue(int this._maxProcesses, 1353 ProcessQueue(this._maxProcesses,
1354 this._maxBrowserProcesses,
1474 String progress, 1355 String progress,
1475 Date startTime, 1356 Date startTime,
1476 bool printTiming, 1357 bool printTiming,
1477 testSuites, 1358 testSuites,
1478 this._allDone, 1359 this._allDone,
1479 [bool verbose = false, 1360 [bool verbose = false,
1480 bool listTests = false]) 1361 bool listTests = false])
1481 : _verbose = verbose, 1362 : _verbose = verbose,
1482 _listTests = listTests, 1363 _listTests = listTests,
1483 _tests = new Queue<TestCase>(), 1364 _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. 1589 // the queue. Avoid spin-polling by using a timeout.
1709 _tests.add(test); 1590 _tests.add(test);
1710 new Timer(100, (timer) {_tryRunTest();}); // Don't lose a process. 1591 new Timer(100, (timer) {_tryRunTest();}); // Don't lose a process.
1711 return; 1592 return;
1712 } 1593 }
1713 if (_verbose) { 1594 if (_verbose) {
1714 int i = 1; 1595 int i = 1;
1715 if (test is BrowserTestCase) { 1596 if (test is BrowserTestCase) {
1716 // Additional command for rerunning the steps locally after the fact. 1597 // Additional command for rerunning the steps locally after the fact.
1717 print('$i. ${TestUtils.dartTestExecutable.toNativePath()} ' 1598 print('$i. ${TestUtils.dartTestExecutable.toNativePath()} '
1718 '${TestUtils.dartDir().toNativePath()}/tools/testing/dart/' 1599 '${TestUtils.dartDir().toNativePath()}/tools/testing/dart/'
1719 'http_server.dart -m ${test.configuration["mode"]} ' 1600 'http_server.dart -m ${test.configuration["mode"]} '
1720 '-a ${test.configuration["arch"]} ' 1601 '-a ${test.configuration["arch"]} '
1721 '-p ${http_server.TestingServerRunner.serverList[0].port} ' 1602 '-p ${http_server.TestingServerRunner.serverList[0].port} '
1722 '-c ${http_server.TestingServerRunner.serverList[1].port}'); 1603 '-c ${http_server.TestingServerRunner.serverList[1].port}');
1723 i++; 1604 i++;
1724 } 1605 }
1725 for (Command command in test.commands) { 1606 for (Command command in test.commands) {
1726 print('$i. ${command.commandLine}'); 1607 print('$i. ${command.commandLine}');
1727 i++; 1608 i++;
1728 } 1609 }
1729 } 1610 }
1611
1612 var isLastCommand =
1613 ((test.commands.length-1) == test.commandOutputs.length);
1614 var isBrowserCommand = isLastCommand && (test is BrowserTestCase);
1615 if (isBrowserCommand && _numBrowserProcesses == _maxBrowserProcesses) {
1616 // If there is no free browser runner, put it back into the queue.
1617 _tests.add(test);
1618 new Timer(100, (timer) {_tryRunTest();}); // Don't lose a process.
Mads Ager (google) 2013/01/29 10:47:48 Is there a reason for waiting 100ms for this? Woul
kustermann 2013/01/29 12:10:49 Yes there is a reason for that: Let's assume that
1619
1620 return;
1621 }
1622
1730 _progress.start(test); 1623 _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 1624
1741 if ((test.configuration['compiler'] == 'dartc' && 1625 // Dartc and browser test commands can be run by a [BatchRunnerProcess]
1742 test.displayName != 'dartc/junit_tests') || 1626 var nextCommandIndex = test.commandOutputs.keys.length;
1743 (test.commands.length == 1 && test.usesWebDriver && 1627 var numberOfCommands = test.commands.length;
1744 !test.configuration['noBatch'])) { 1628 var useBatchRunnerForDartc = test.configuration['compiler'] == 'dartc' &&
1745 // Dartc and browser test cases that do not require a precompilation 1629 test.displayName != 'dartc/junit_tests';
1746 // step, start with the batch runner right away. 1630 var isWebdriverCommand = nextCommandIndex == (numberOfCommands - 1) &&
1631 test.usesWebDriver &&
1632 !test.configuration['noBatch'];
1633 if (useBatchRunnerForDartc || isWebdriverCommand) {
1634 TestCaseEvent oldCallback = test.completedHandler;
1635 void wrapper(TestCase test_arg) {
1636 _numProcesses--;
1637 if (isBrowserCommand) {
1638 _numBrowserProcesses--;
1639 }
1640 _progress.done(test_arg);
1641 if (test_arg is BrowserTestCase) test_arg.notifyObservers();
1642 oldCallback(test_arg);
1643 _tryRunTest();
1644 };
1645 test.completedHandler = wrapper;
1747 _getBatchRunner(test).startTest(test); 1646 _getBatchRunner(test).startTest(test);
1748 } else { 1647 } else {
1749 // Once we've actually failed a test, technically, we wouldn't need to 1648 // 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. 1649 // bother retrying any subsequent tests since the bot is already red.
1751 // However, we continue to retry tests until we have actually failed 1650 // However, we continue to retry tests until we have actually failed
1752 // four tests (arbitrarily chosen) for more debugable output, so that 1651 // 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 1652 // 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 1653 // tests that appear to be broken but were actually just flakes that
1755 // didn't get retried because there had already been one failure. 1654 // didn't get retried because there had already been one failure.
1756 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; 1655 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests;
1757 new RunningProcess(test, allowRetry, this).start(); 1656 runNextCommandWithRetries(test, allowRetry).then((TestCase testCase) {
1657 _numProcesses--;
1658 if (isBrowserCommand) {
1659 _numBrowserProcesses--;
1660 }
1661 if (isTestCaseFinished(testCase)) {
1662 testCase.completed();
1663 _progress.done(testCase);
1664 if (testCase is BrowserTestCase) testCase.notifyObservers();
1665 } else {
1666 _tests.add(testCase);
1667 }
1668 _tryRunTest();
1669 });
1758 } 1670 }
1671
1759 _numProcesses++; 1672 _numProcesses++;
1673 if (isBrowserCommand) {
1674 _numBrowserProcesses++;
1675 }
1760 } 1676 }
1761 } 1677 }
1678
1679 bool isTestCaseFinished(TestCase testCase) {
1680 var numberOfCommandOutputs = testCase.commandOutputs.keys.length;
1681 var numberOfCommands = testCase.commands.length;
1682
1683 var lastCommandCompleted = (numberOfCommandOutputs == numberOfCommands);
1684 var lastCommandOutput = testCase.lastCommandOutput;
1685 var lastCommand = lastCommandOutput.command;
1686 var timedOut = lastCommandOutput.hasTimedOut;
1687 var nonZeroExitCode = lastCommandOutput.exitCode != 0;
1688 // NOTE: If this was the last command or there was unexpected output
1689 // we're done with the test.
1690 // Otherwise we need to enqueue it again into the test queue.
1691 if (lastCommandCompleted || timedOut || nonZeroExitCode) {
1692 var verbose = testCase.configuration['verbose'];
1693 if (nonZeroExitCode && verbose != null && verbose) {
1694 print(testCase.displayName);
1695 print("stderr:");
1696 print(decodeUtf8(lastCommandOutput.stderr));
1697 if (!lastCommand.isPixelTest) {
1698 print("stdout:");
1699 print(decodeUtf8(lastCommandOutput.stdout));
1700 } else {
1701 print("");
1702 print("DRT pixel test failed! stdout is not printed because it "
1703 "contains binary data!");
1704 }
1705 }
1706 return true;
1707 } else {
1708 return false;
1709 }
1710 }
1711
1712 Future runNextCommandWithRetries(TestCase testCase, bool allowRetry) {
1713 var completer = new Completer();
1714
1715 var nextCommandIndex = testCase.commandOutputs.keys.length;
1716 var numberOfCommands = testCase.commands.length;
1717 Expect.isTrue(nextCommandIndex < numberOfCommands);
1718 var command = testCase.commands[nextCommandIndex];
1719 var isLastCommand = nextCommandIndex == (numberOfCommands - 1);
1720
1721 void runCommand() {
1722 var runningProcess = new RunningProcess(testCase, command);
1723 runningProcess.start().then((CommandOutput commandOutput) {
1724 if (isLastCommand) {
1725 if (allowRetry && testCase.usesWebDriver
1726 && commandOutput.unexpectedOutput
1727 && (testCase as BrowserTestCase).numRetries > 0) {
1728 // Selenium tests can be flaky. Try rerunning.
1729 commandOutput.requestRetry = true;
1730 }
1731 }
1732 if (commandOutput.requestRetry) {
1733 commandOutput.requestRetry = false;
1734 (testCase as BrowserTestCase).numRetries--;
1735 print("Potential flake. Re-running ${testCase.displayName} "
1736 "(${(testCase as BrowserTestCase).numRetries} "
1737 "attempt(s) remains)");
1738 print("[cmd:$command]");
1739 runCommand();
1740 } else {
1741 completer.complete(testCase);
1742 }
1743 });
1744 }
1745 runCommand();
1746
1747 return completer.future;
1748 }
1762 } 1749 }
1750
OLDNEW
« tools/test.dart ('K') | « tools/test-runtime.dart ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698