Chromium Code Reviews| 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. |
| (...skipping 536 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 | |
| OLD | NEW |