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 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 159 } | 159 } |
| 160 | 160 |
| 161 | 161 |
| 162 /** | 162 /** |
| 163 * TestOutput records the output of a completed test: the process's exit code, | 163 * TestOutput records the output of a completed test: the process's exit code, |
| 164 * the standard output and standard error, whether the process timed out, and | 164 * the standard output and standard error, whether the process timed out, and |
| 165 * the time the process took to run. It also contains a pointer to the | 165 * the time the process took to run. It also contains a pointer to the |
| 166 * [TestCase] this is the output of. | 166 * [TestCase] this is the output of. |
| 167 */ | 167 */ |
| 168 class TestOutput { | 168 class TestOutput { |
| 169 TestCase testCase; | 169 final TestCase testCase; |
| 170 int exitCode; | 170 final int exitCode; |
| 171 bool timedOut; | 171 final bool timedOut; |
| 172 final List<String> stdout; | |
| 173 final List<String> stderr; | |
| 174 final Duration time; | |
| 172 bool failed = false; | 175 bool failed = false; |
| 173 List<String> stdout; | |
| 174 List<String> stderr; | |
| 175 Duration time; | |
| 176 /** | 176 /** |
| 177 * Set to true if we encounter a condition in the output that indicates we | 177 * Set to true if we encounter a condition in the output that indicates we |
| 178 * need to rerun this test. | 178 * need to rerun this test. |
| 179 */ | 179 */ |
| 180 bool requestRetry; | 180 bool requestRetry = false; |
| 181 | 181 |
| 182 TestOutput(this.testCase, this.exitCode, this.timedOut, this.stdout, | 182 TestOutput(this.testCase, this.exitCode, this.timedOut, this.stdout, |
| 183 this.stderr, this.time) { | 183 this.stderr, this.time) { |
| 184 testCase.output = this; | 184 testCase.output = this; |
| 185 requestRetry = false; | |
| 186 } | 185 } |
| 187 | 186 |
| 188 String get result() => | 187 String get result() => |
| 189 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); | 188 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); |
| 190 | 189 |
| 191 bool get unexpectedOutput() => !testCase.expectedOutcomes.contains(result); | 190 bool get unexpectedOutput() => !testCase.expectedOutcomes.contains(result); |
| 192 | 191 |
| 193 bool get hasCrashed() { | 192 bool get hasCrashed() { |
| 194 if (new Platform().operatingSystem() == 'windows') { | 193 if (new Platform().operatingSystem() == 'windows') { |
| 195 // The VM uses std::abort to terminate on asserts. | 194 // The VM uses std::abort to terminate on asserts. |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 265 List<Function> handlers; | 264 List<Function> handlers; |
| 266 bool allowRetries = false; | 265 bool allowRetries = false; |
| 267 | 266 |
| 268 /** Which command of [testCase.commands] is currently being executed. */ | 267 /** Which command of [testCase.commands] is currently being executed. */ |
| 269 int currentStep; | 268 int currentStep; |
| 270 | 269 |
| 271 RunningProcess(TestCase this.testCase, | 270 RunningProcess(TestCase this.testCase, |
| 272 [this.allowRetries, this.processQueue]); | 271 [this.allowRetries, this.processQueue]); |
| 273 | 272 |
| 274 /** | 273 /** |
| 275 * Called when all commands are executed. [exitCode] is 0 if all command | 274 * Called when all commands are executed. [exitCode] is 0 if all commands |
| 276 * succeded, otherwise it will have the exit code of the first failing | 275 * succeded, otherwise it will have the exit code of the first failing |
| 277 * command. | 276 * command. |
| 278 */ | 277 */ |
| 279 void testComplete(int exitCode) { | 278 void testComplete(int exitCode) { |
| 280 new TestOutput(testCase, exitCode, timedOut, stdout, | 279 new TestOutput(testCase, exitCode, timedOut, stdout, |
| 281 stderr, new Date.now().difference(startTime)); | 280 stderr, new Date.now().difference(startTime)); |
| 282 timeoutTimer.cancel(); | 281 timeoutTimer.cancel(); |
| 283 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { | 282 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { |
| 284 print(testCase.displayName); | 283 print(testCase.displayName); |
| 285 for (var line in testCase.output.stderr) print(line); | 284 for (var line in testCase.output.stderr) print(line); |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 338 var line = source.readLine(); | 337 var line = source.readLine(); |
| 339 while (null != line) { | 338 while (null != line) { |
| 340 destination.add(line); | 339 destination.add(line); |
| 341 line = source.readLine(); | 340 line = source.readLine(); |
| 342 } | 341 } |
| 343 }; | 342 }; |
| 344 } | 343 } |
| 345 | 344 |
| 346 void start() { | 345 void start() { |
| 347 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); | 346 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); |
| 348 stdout = new List<String>(); | 347 stdout = []; |
| 349 stderr = new List<String>(); | 348 stderr = []; |
| 350 currentStep = 0; | 349 currentStep = 0; |
| 351 runCommand(testCase.commands[currentStep++], stepExitHandler); | 350 runCommand(testCase.commands[currentStep++], stepExitHandler); |
| 352 } | 351 } |
| 353 | 352 |
| 354 void runCommand(Command command, | 353 void runCommand(Command command, |
| 355 void exitHandler(int exitCode)) { | 354 void exitHandler(int exitCode)) { |
| 356 if (new Platform().operatingSystem() == 'windows') { | 355 if (new Platform().operatingSystem() == 'windows') { |
| 357 // Windows can't handle the first command if it is a .bat file or the like | 356 // Windows can't handle the first command if it is a .bat file or the like |
| 358 // with the slashes going the other direction. | 357 // with the slashes going the other direction. |
| 359 // TODO(efortuna): Remove this when fixed (Issue 1306). | 358 // TODO(efortuna): Remove this when fixed (Issue 1306). |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 445 bool shutdownMillisecs = 30000; | 444 bool shutdownMillisecs = 30000; |
| 446 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs); | 445 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs); |
| 447 } else { | 446 } else { |
| 448 _process.kill(); | 447 _process.kill(); |
| 449 } | 448 } |
| 450 } | 449 } |
| 451 } | 450 } |
| 452 | 451 |
| 453 void doStartTest(TestCase testCase) { | 452 void doStartTest(TestCase testCase) { |
| 454 _startTime = new Date.now(); | 453 _startTime = new Date.now(); |
| 455 _testStdout = new List<String>(); | 454 _testStdout = []; |
| 456 _testStderr = new List<String>(); | 455 _testStderr = []; |
| 457 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); | 456 _stdoutStream.lineHandler = _readStdout(_stdoutStream, _testStdout); |
| 458 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); | 457 _stderrStream.lineHandler = _readStderr(_stderrStream, _testStderr); |
| 459 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000); | 458 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000); |
| 460 var line = _createArgumentsLine(testCase.batchTestArguments); | 459 var line = _createArgumentsLine(testCase.batchTestArguments); |
| 461 _process.stdin.write(line.charCodes()); | 460 _process.stdin.write(line.charCodes()); |
| 462 } | 461 } |
| 463 | 462 |
| 464 String _createArgumentsLine(List<String> arguments) { | 463 String _createArgumentsLine(List<String> arguments) { |
| 465 return Strings.join(arguments, ' ') + '\n'; | 464 return Strings.join(arguments, ' ') + '\n'; |
| 466 } | 465 } |
| 467 | 466 |
| 467 // This removes the line handler from stderr and reads all | |
| 468 // remaining bytes from it. | |
| 469 // TODO(zundel): This is pretty bad - streams need flush() | |
|
Emily Fortuna
2012/02/29 19:59:03
Add issue id # (1407) in comment
(also possibly le
zundel
2012/02/29 20:19:08
Done.
| |
| 470 _drainStderr() { | |
| 471 _stderrStream.lineHandler = null; | |
| 472 while(true) { | |
| 473 var available = 0; | |
| 474 try { | |
| 475 available = _process.stderr.available(); | |
| 476 } catch (SocketIOException ex) { | |
| 477 break; | |
| 478 } | |
| 479 if (available <= 0) break; | |
| 480 String result = _stderrStream.readLine(); | |
| 481 if (result == null) { | |
| 482 // This is intended to catch the last line, but might foul up | |
| 483 // if more bytes immediately come available after read() and before | |
| 484 // the test for available() | |
| 485 result = _stderrStream.read(); | |
|
Emily Fortuna
2012/02/29 19:59:03
Perhaps I'm misunderstanding the API, but would th
zundel
2012/02/29 20:19:08
(see next comment).
| |
| 486 if (result == null) { | |
| 487 var buf = new List<int>(available); | |
| 488 _process.stderr.readInto(buf, 0, available);; | |
|
Siggi Cherem (dart-lang)
2012/02/29 19:13:32
extra ;
Emily Fortuna
2012/02/29 19:59:03
_stderrStream was instantiated from new StringInpu
zundel
2012/02/29 20:19:08
I didn't know about 1407 and filed this bug to try
| |
| 489 result = new String.fromCharCodes(buf); | |
| 490 _testStderr.add(result); | |
| 491 break; | |
| 492 } | |
| 493 } | |
| 494 _testStderr.add(result); | |
| 495 } | |
| 496 } | |
| 497 | |
| 468 int _reportResult(String output) { | 498 int _reportResult(String output) { |
| 499 _drainStderr(); | |
| 469 var test = _currentTest; | 500 var test = _currentTest; |
| 470 _currentTest = null; | 501 _currentTest = null; |
| 471 | 502 |
| 472 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' | 503 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' |
| 473 var outcome = output.split(" ")[2]; | 504 var outcome = output.split(" ")[2]; |
| 474 var exitCode = 0; | 505 var exitCode = 0; |
| 475 if (outcome == "CRASH") exitCode = -10; | 506 if (outcome == "CRASH") exitCode = -10; |
| 476 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; | 507 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; |
| 477 new TestOutput(test, exitCode, outcome == "TIMEOUT", _testStdout, | 508 new TestOutput(test, exitCode, outcome == "TIMEOUT", _testStdout, |
| 478 _testStderr, new Date.now().difference(_startTime)); | 509 _testStderr, new Date.now().difference(_startTime)); |
| 479 test.completed(); | 510 test.completed(); |
| 480 } | 511 } |
| 481 | 512 |
| 482 Function _readOutput(StringInputStream stream, List<String> buffer) { | 513 Function _readStdout(StringInputStream stream, List<String> buffer) { |
| 483 return () { | 514 return () { |
| 484 var status; | 515 var status; |
| 485 var line = stream.readLine(); | 516 var line = stream.readLine(); |
| 486 // Drain the input stream to get the error output. | 517 // Drain the input stream to get the error output. |
| 487 while (line != null) { | 518 while (line != null) { |
| 488 if (line.startsWith('>>> TEST')) { | 519 if (line.startsWith('>>> TEST')) { |
| 489 status = line; | 520 status = line; |
| 490 } else if (line.startsWith('>>> BATCH START')) { | 521 } else if (line.startsWith('>>> BATCH START')) { |
| 491 // ignore | 522 // ignore |
| 492 } else if (line.startsWith('>>> ')) { | 523 } else if (line.startsWith('>>> ')) { |
| 493 throw new Exception('Unexpected command from dartc batch runner.'); | 524 throw new Exception('Unexpected command from dartc batch runner.'); |
| 494 } else { | 525 } else { |
| 495 buffer.add(line); | 526 buffer.add(line); |
| 496 } | 527 } |
| 497 line = stream.readLine(); | 528 line = stream.readLine(); |
| 498 } | 529 } |
| 499 if (status != null) { | 530 if (status != null) { |
| 500 _timer.cancel(); | 531 _timer.cancel(); |
| 501 // For crashing processes, let the exit handler deal with it. | 532 // For crashing processes, let the exit handler deal with it. |
| 502 if (!status.contains("CRASH")) { | 533 if (!status.contains("CRASH")) { |
| 503 _reportResult(status); | 534 _reportResult(status); |
| 504 } | 535 } |
| 505 } | 536 } |
| 506 }; | 537 }; |
| 507 } | 538 } |
| 539 | |
| 540 Function _readStderr(StringInputStream stream, List<String> buffer) { | |
| 541 return () { | |
|
Emily Fortuna
2012/02/29 19:59:03
Check out makeReadHandler on line 334.
whesse als
zundel
2012/02/29 20:19:08
I just got rid of _readStdout() and replaced it wi
| |
| 542 var line; | |
| 543 while ((line = stream.readLine()) != null) { | |
| 544 buffer.add(line); | |
| 545 } | |
| 546 }; | |
| 547 } | |
| 508 | 548 |
| 509 void _exitHandler(exitCode) { | 549 void _exitHandler(exitCode) { |
| 510 if (_timer != null) _timer.cancel(); | 550 if (_timer != null) _timer.cancel(); |
| 551 _reportResult(">>> TEST CRASH"); | |
| 511 _process.close(); | 552 _process.close(); |
| 512 _startProcess(() { | 553 _startProcess(); |
| 513 _reportResult(">>> TEST CRASH"); | |
| 514 }); | |
| 515 } | 554 } |
| 516 | 555 |
| 517 void _timeoutHandler(ignore) { | 556 void _timeoutHandler(ignore) { |
| 518 _process.exitHandler = (exitCode) { | 557 _process.exitHandler = (exitCode) {_ |
| 558 reportResult(">>> TEST TIMEOUT"); | |
| 519 _process.close(); | 559 _process.close(); |
| 520 _startProcess(() { | 560 _startProcess(); |
| 521 _reportResult(">>> TEST TIMEOUT"); | |
| 522 }); | |
| 523 }; | 561 }; |
| 524 _process.kill(); | 562 _process.kill(); |
| 525 } | 563 } |
| 526 | 564 |
| 527 void _startProcess(then) { | 565 void _startProcess([Function then = null]) { |
| 528 _process = new Process.start(_executable, _batchArguments); | 566 _process = new Process.start(_executable, _batchArguments); |
| 529 _stdoutStream = new StringInputStream(_process.stdout); | 567 _stdoutStream = new StringInputStream(_process.stdout); |
| 530 _stderrStream = new StringInputStream(_process.stderr); | 568 _stderrStream = new StringInputStream(_process.stderr); |
| 531 _testStdout = new List<String>(); | 569 _testStdout = []; |
| 532 _testStderr = new List<String>(); | 570 _testStderr = []; |
| 533 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); | 571 _stdoutStream.lineHandler = _readStdout(_stdoutStream, _testStdout); |
| 534 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); | 572 _stderrStream.lineHandler = _readStderr(_stderrStream, _testStderr); |
| 535 _process.exitHandler = _exitHandler; | 573 _process.exitHandler = _exitHandler; |
| 536 _process.startHandler = then; | 574 _process.startHandler = then; |
| 537 } | 575 } |
| 538 } | 576 } |
| 539 | 577 |
| 540 /** | 578 /** |
| 541 * ProcessQueue is the master control class, responsible for running all | 579 * ProcessQueue is the master control class, responsible for running all |
| 542 * the tests in all the TestSuites that have been registered. It includes | 580 * the tests in all the TestSuites that have been registered. It includes |
| 543 * a rate-limited queue to run a limited number of tests in parallel, | 581 * a rate-limited queue to run a limited number of tests in parallel, |
| 544 * a ProgressIndicator which prints output when tests are started and | 582 * a ProgressIndicator which prints output when tests are started and |
| (...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 775 // the developer doesn't waste his or her time trying to fix a bunch of | 813 // the developer doesn't waste his or her time trying to fix a bunch of |
| 776 // tests that appear to be broken but were actually just flakes that | 814 // tests that appear to be broken but were actually just flakes that |
| 777 // didn't get retried because there had already been one failure. | 815 // didn't get retried because there had already been one failure. |
| 778 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; | 816 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 779 new RunningProcess(test, allowRetry, this).start(); | 817 new RunningProcess(test, allowRetry, this).start(); |
| 780 } | 818 } |
| 781 _numProcesses++; | 819 _numProcesses++; |
| 782 } | 820 } |
| 783 } | 821 } |
| 784 } | 822 } |
| OLD | NEW |