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 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 193 // exceptions. | 193 // exceptions. |
| 194 return (!timedOut && ((exitCode < 0) || (exitCode == 253))); | 194 return (!timedOut && ((exitCode < 0) || (exitCode == 253))); |
| 195 } | 195 } |
| 196 | 196 |
| 197 bool get hasTimedOut() => timedOut; | 197 bool get hasTimedOut() => timedOut; |
| 198 | 198 |
| 199 bool get didFail() { | 199 bool get didFail() { |
| 200 if (testCase is !BrowserTestCase) return (exitCode != 0 && !hasCrashed); | 200 if (testCase is !BrowserTestCase) return (exitCode != 0 && !hasCrashed); |
| 201 | 201 |
| 202 // Browser case: | 202 // Browser case: |
| 203 // Browser tests fail unless stdout contains | |
| 204 // 'Content-Type: text/plain\nPASS'. | |
| 205 String previous_line = ''; | |
| 206 for (String line in stdout) { | |
| 207 if (line == 'PASS' && previous_line == 'Content-Type: text/plain') { | |
| 208 return (exitCode != 0 && !hasCrashed); | |
| 209 } | |
| 210 previous_line = line; | |
| 211 } | |
| 212 | |
| 213 // If the browser test failed, it may have been because DumpRenderTree | 203 // If the browser test failed, it may have been because DumpRenderTree |
| 214 // and the virtual framebuffer X server didn't hook up, or DRT crashed with | 204 // and the virtual framebuffer X server didn't hook up, or DRT crashed with |
| 215 // a core dump. | 205 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, |
| 206 // so we have to do this check first. | |
| 216 for (String line in stderr) { | 207 for (String line in stderr) { |
| 217 if (line.contains('Gtk-WARNING **: cannot open display: :99') || | 208 if (line.contains('Gtk-WARNING **: cannot open display: :99') || |
| 218 line.contains('Failed to run command. return code=1')) { | 209 line.contains('Failed to run command. return code=1')) { |
| 219 // If we get the X server error, or DRT crashes with a core dump, retry | 210 // If we get the X server error, or DRT crashes with a core dump, retry |
| 220 // the test. | 211 // the test. |
| 221 requestRetry = true; | 212 requestRetry = true; |
| 222 return true; | 213 return true; |
| 223 } | 214 } |
| 224 } | 215 } |
| 216 | |
| 217 // Browser tests fail unless stdout contains | |
| 218 // 'Content-Type: text/plain\nPASS'. | |
| 219 String previous_line = ''; | |
| 220 for (String line in stdout) { | |
| 221 if (line == 'PASS' && previous_line == 'Content-Type: text/plain') { | |
| 222 return (exitCode != 0 && !hasCrashed); | |
| 223 } | |
| 224 previous_line = line; | |
| 225 } | |
| 226 | |
| 225 return true; | 227 return true; |
| 226 } | 228 } |
| 227 | 229 |
| 228 // Reverse result of a negative test. | 230 // Reverse result of a negative test. |
| 229 bool get hasFailed() => (testCase.isNegative ? !didFail : didFail); | 231 bool get hasFailed() => (testCase.isNegative ? !didFail : didFail); |
| 230 } | 232 } |
| 231 | 233 |
| 232 /** | 234 /** |
| 233 * A RunningProcess actually runs a test, getting the command lines from | 235 * A RunningProcess actually runs a test, getting the command lines from |
| 234 * its [TestCase], starting the test process (and first, a compilation | 236 * its [TestCase], starting the test process (and first, a compilation |
| 235 * process if the TestCase is a [BrowserTestCase]), creating a timeout | 237 * process if the TestCase is a [BrowserTestCase]), creating a timeout |
| 236 * timer, and recording the results in a new [TestOutput] object, which it | 238 * timer, and recording the results in a new [TestOutput] object, which it |
| 237 * attaches to the TestCase. The lifetime of the RunningProcess is limited | 239 * attaches to the TestCase. The lifetime of the RunningProcess is limited |
| 238 * to the time it takes to start the process, run the process, and record | 240 * to the time it takes to start the process, run the process, and record |
| 239 * the result; there are no pointers to it, so it should be available to | 241 * the result; there are no pointers to it, so it should be available to |
| 240 * be garbage collected as soon as it is done. | 242 * be garbage collected as soon as it is done. |
| 241 */ | 243 */ |
| 242 class RunningProcess { | 244 class RunningProcess { |
| 243 Process process; | 245 Process process; |
| 244 TestCase testCase; | 246 TestCase testCase; |
| 245 bool timedOut = false; | 247 bool timedOut = false; |
| 246 Date startTime; | 248 Date startTime; |
| 247 Timer timeoutTimer; | 249 Timer timeoutTimer; |
| 248 List<String> stdout; | 250 List<String> stdout; |
| 249 List<String> stderr; | 251 List<String> stderr; |
| 250 List<Function> handlers; | 252 List<Function> handlers; |
| 253 bool allowRetries; | |
| 251 | 254 |
| 252 RunningProcess(TestCase this.testCase); | 255 RunningProcess(TestCase this.testCase, this.allowRetries); |
| 253 | 256 |
| 254 void exitHandler(int exitCode) { | 257 void exitHandler(int exitCode) { |
| 255 new TestOutput(testCase, exitCode, timedOut, stdout, | 258 new TestOutput(testCase, exitCode, timedOut, stdout, |
| 256 stderr, new Date.now().difference(startTime)); | 259 stderr, new Date.now().difference(startTime)); |
| 257 process.close(); | 260 process.close(); |
| 258 timeoutTimer.cancel(); | 261 timeoutTimer.cancel(); |
| 259 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { | 262 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { |
| 260 print(testCase.displayName); | 263 print(testCase.displayName); |
| 261 for (var line in testCase.output.stderr) print(line); | 264 for (var line in testCase.output.stderr) print(line); |
| 262 for (var line in testCase.output.stdout) print(line); | 265 for (var line in testCase.output.stdout) print(line); |
| 263 } | 266 } |
| 264 if (testCase.configuration['component'] == 'webdriver' && | 267 if (allowRetries && testCase.configuration['component'] == 'webdriver' && |
| 265 testCase.output.unexpectedOutput && testCase.numRetries > 0) { | 268 testCase.output.unexpectedOutput && testCase.numRetries > 0) { |
| 266 // Selenium tests can be flaky. Try rerunning. | 269 // Selenium tests can be flaky. Try rerunning. |
| 267 testCase.output.requestRetry = true; | 270 testCase.output.requestRetry = true; |
| 268 } | 271 } |
| 269 if (testCase.output.requestRetry) { | 272 if (testCase.output.requestRetry) { |
| 270 testCase.output.requestRetry = false; | 273 testCase.output.requestRetry = false; |
| 271 this.timedOut = false; | 274 this.timedOut = false; |
| 272 testCase.dynamic.numRetries--; | 275 testCase.dynamic.numRetries--; |
| 273 print("Potential flake. Re-running " + testCase.displayName); | 276 print("Potential flake. Re-running " + testCase.displayName); |
| 274 this.start(); | 277 this.start(); |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 493 * | 496 * |
| 494 * Because multiple configurations may be run on each test suite, the | 497 * Because multiple configurations may be run on each test suite, the |
| 495 * ProcessQueue contains a cache in which a test suite may record information | 498 * ProcessQueue contains a cache in which a test suite may record information |
| 496 * about its list of tests, and may retrieve that information when it is called | 499 * about its list of tests, and may retrieve that information when it is called |
| 497 * upon to enqueue its tests again. | 500 * upon to enqueue its tests again. |
| 498 */ | 501 */ |
| 499 class ProcessQueue { | 502 class ProcessQueue { |
| 500 int _numProcesses = 0; | 503 int _numProcesses = 0; |
| 501 int _activeTestListers = 0; | 504 int _activeTestListers = 0; |
| 502 int _maxProcesses; | 505 int _maxProcesses; |
| 506 /** The number of tests we allow to actually fail before we stop retrying. */ | |
| 507 int MAX_FAILED_NO_RETRY = 4; | |
|
Siggi Cherem (dart-lang)
2012/02/17 21:34:17
make it private (_MAX...)
| |
| 503 bool _verbose; | 508 bool _verbose; |
| 504 bool _listTests; | 509 bool _listTests; |
| 505 bool _keepGeneratedTests; | 510 bool _keepGeneratedTests; |
| 506 Function _enqueueMoreWork; | 511 Function _enqueueMoreWork; |
| 507 Queue<TestCase> _tests; | 512 Queue<TestCase> _tests; |
| 508 ProgressIndicator _progress; | 513 ProgressIndicator _progress; |
| 509 String _temporaryDirectory; | 514 String _temporaryDirectory; |
| 510 // For dartc batch processing we keep a list of batch processes. | 515 // For dartc batch processing we keep a list of batch processes. |
| 511 List<DartcBatchRunnerProcess> _batchProcesses; | 516 List<DartcBatchRunnerProcess> _batchProcesses; |
| 512 // Cache information about test cases per test suite. For multiple | 517 // Cache information about test cases per test suite. For multiple |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 697 _progress.done(test_arg); | 702 _progress.done(test_arg); |
| 698 _tryRunTest(); | 703 _tryRunTest(); |
| 699 oldCallback(test_arg); | 704 oldCallback(test_arg); |
| 700 }; | 705 }; |
| 701 test.completedHandler = wrapper; | 706 test.completedHandler = wrapper; |
| 702 if (test.configuration['component'] == 'dartc' && | 707 if (test.configuration['component'] == 'dartc' && |
| 703 test.displayName != 'dartc/junit_tests') { | 708 test.displayName != 'dartc/junit_tests') { |
| 704 _ensureDartcBatchRunnersStarted(test.executablePath); | 709 _ensureDartcBatchRunnersStarted(test.executablePath); |
| 705 _getDartcBatchRunnerProcess().startTest(test); | 710 _getDartcBatchRunnerProcess().startTest(test); |
| 706 } else { | 711 } else { |
| 707 new RunningProcess(test).start(); | 712 // Once we've actually failed a test, technically, we wouldn't need to |
| 713 // bother retrying any subsequent tests since the bot is already red. | |
| 714 // However, we continue to retry tests until we have actually failed | |
| 715 // four tests (arbitrarily chosen) for more debugable output, so that | |
| 716 // the developer doesn't waste his or her time trying to fix a bunch of | |
| 717 // tests that appear to be broken but were actually just flakes that | |
| 718 // didn't get retried because there had already been one failure. | |
| 719 new RunningProcess(test, | |
| 720 (MAX_FAILED_NO_RETRY - _progress.numFailedTests) > 0).start(); | |
|
Siggi Cherem (dart-lang)
2012/02/17 21:34:17
nit =)
_progress.numFailedTests < _MAX_FAILED_NO_
| |
| 708 } | 721 } |
| 709 _numProcesses++; | 722 _numProcesses++; |
| 710 } | 723 } |
| 711 } | 724 } |
| 712 } | 725 } |
| OLD | NEW |