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

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

Issue 9569003: Add token to stderr as well as stdout for batch processing. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Don't revert status file change. Created 8 years, 9 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
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 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
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 final TestCase testCase; 169 TestCase testCase;
170 final int exitCode; 170 int exitCode;
171 final bool timedOut; 171 bool timedOut;
172 final List<String> stdout;
173 final List<String> stderr;
174 final Duration time;
175 bool failed = false; 172 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 = false; 180 bool requestRetry;
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;
185 } 186 }
186 187
187 String get result() => 188 String get result() =>
188 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); 189 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS));
189 190
190 bool get unexpectedOutput() => !testCase.expectedOutcomes.contains(result); 191 bool get unexpectedOutput() => !testCase.expectedOutcomes.contains(result);
191 192
192 bool get hasCrashed() { 193 bool get hasCrashed() {
193 if (new Platform().operatingSystem() == 'windows') { 194 if (new Platform().operatingSystem() == 'windows') {
194 // The VM uses std::abort to terminate on asserts. 195 // The VM uses std::abort to terminate on asserts.
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after
264 List<Function> handlers; 265 List<Function> handlers;
265 bool allowRetries = false; 266 bool allowRetries = false;
266 267
267 /** Which command of [testCase.commands] is currently being executed. */ 268 /** Which command of [testCase.commands] is currently being executed. */
268 int currentStep; 269 int currentStep;
269 270
270 RunningProcess(TestCase this.testCase, 271 RunningProcess(TestCase this.testCase,
271 [this.allowRetries, this.processQueue]); 272 [this.allowRetries, this.processQueue]);
272 273
273 /** 274 /**
274 * Called when all commands are executed. [exitCode] is 0 if all commands 275 * Called when all commands are executed. [exitCode] is 0 if all command
275 * succeded, otherwise it will have the exit code of the first failing 276 * succeded, otherwise it will have the exit code of the first failing
276 * command. 277 * command.
277 */ 278 */
278 void testComplete(int exitCode) { 279 void testComplete(int exitCode) {
279 new TestOutput(testCase, exitCode, timedOut, stdout, 280 new TestOutput(testCase, exitCode, timedOut, stdout,
280 stderr, new Date.now().difference(startTime)); 281 stderr, new Date.now().difference(startTime));
281 timeoutTimer.cancel(); 282 timeoutTimer.cancel();
282 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { 283 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) {
283 print(testCase.displayName); 284 print(testCase.displayName);
284 for (var line in testCase.output.stderr) print(line); 285 for (var line in testCase.output.stderr) print(line);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
324 && testCase.configuration['component'] == 'webdriver') { 325 && testCase.configuration['component'] == 'webdriver') {
325 // Note: processQueue will always be non-null for component == webdriver 326 // Note: processQueue will always be non-null for component == webdriver
326 // (It is only null for component == vm) 327 // (It is only null for component == vm)
327 processQueue._getBatchRunner(testCase).startTest(testCase); 328 processQueue._getBatchRunner(testCase).startTest(testCase);
328 } else { 329 } else {
329 runCommand(testCase.commands[currentStep++], stepExitHandler); 330 runCommand(testCase.commands[currentStep++], stepExitHandler);
330 } 331 }
331 } 332 }
332 } 333 }
333 334
335 Function makeReadHandler(StringInputStream source, List<String> destination) {
336 return () {
337 if (source.closed) return; // TODO(whesse): Remove when bug is fixed.
338 var line = source.readLine();
339 while (null != line) {
340 destination.add(line);
341 line = source.readLine();
342 }
343 };
344 }
345
334 void start() { 346 void start() {
335 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); 347 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP));
336 stdout = []; 348 stdout = new List<String>();
337 stderr = []; 349 stderr = new List<String>();
338 currentStep = 0; 350 currentStep = 0;
339 runCommand(testCase.commands[currentStep++], stepExitHandler); 351 runCommand(testCase.commands[currentStep++], stepExitHandler);
340 } 352 }
341 353
342 void runCommand(Command command, 354 void runCommand(Command command,
343 void exitHandler(int exitCode)) { 355 void exitHandler(int exitCode)) {
344 if (new Platform().operatingSystem() == 'windows') { 356 if (new Platform().operatingSystem() == 'windows') {
345 // Windows can't handle the first command if it is a .bat file or the like 357 // Windows can't handle the first command if it is a .bat file or the like
346 // with the slashes going the other direction. 358 // with the slashes going the other direction.
347 // TODO(efortuna): Remove this when fixed (Issue 1306). 359 // TODO(efortuna): Remove this when fixed (Issue 1306).
348 command.executable = command.executable.replaceAll('/', '\\'); 360 command.executable = command.executable.replaceAll('/', '\\');
349 } 361 }
350 process = new Process.start(command.executable, command.arguments); 362 process = new Process.start(command.executable, command.arguments);
351 process.exitHandler = exitHandler; 363 process.exitHandler = exitHandler;
352 startTime = new Date.now(); 364 startTime = new Date.now();
353 InputStream stdoutStream = process.stdout; 365 InputStream stdoutStream = process.stdout;
354 InputStream stderrStream = process.stderr; 366 InputStream stderrStream = process.stderr;
355 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream); 367 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream);
356 StringInputStream stderrStringStream = new StringInputStream(stderrStream); 368 StringInputStream stderrStringStream = new StringInputStream(stderrStream);
357 stdoutStringStream.lineHandler = 369 stdoutStringStream.lineHandler =
358 _makeReadHandler(stdoutStringStream, stdout); 370 makeReadHandler(stdoutStringStream, stdout);
359 stderrStringStream.lineHandler = 371 stderrStringStream.lineHandler =
360 _makeReadHandler(stderrStringStream, stderr); 372 makeReadHandler(stderrStringStream, stderr);
361 timeoutTimer = new Timer(timeoutHandler, 1000 * testCase.timeout); 373 timeoutTimer = new Timer(timeoutHandler, 1000 * testCase.timeout);
362 } 374 }
363 375
364 void timeoutHandler(Timer unusedTimer) { 376 void timeoutHandler(Timer unusedTimer) {
365 timedOut = true; 377 timedOut = true;
366 process.kill(); 378 process.kill();
367 } 379 }
368 } 380 }
369 381
370 class BatchRunnerProcess { 382 class BatchRunnerProcess {
371 String _executable; 383 String _executable;
372 List<String> _batchArguments; 384 List<String> _batchArguments;
373 385
374 Process _process; 386 Process _process;
375 StringInputStream _stdoutStream; 387 StringInputStream _stdoutStream;
376 StringInputStream _stderrStream; 388 StringInputStream _stderrStream;
377 389
378 TestCase _currentTest; 390 TestCase _currentTest;
379 List<String> _testStdout; 391 List<String> _testStdout;
380 List<String> _testStderr; 392 List<String> _testStderr;
393 bool _stderrDrained = false;
381 Date _startTime; 394 Date _startTime;
382 Timer _timer; 395 Timer _timer;
383 396
384 bool _isWebDriver; 397 bool _isWebDriver;
385 398
386 BatchRunnerProcess(TestCase testCase) { 399 BatchRunnerProcess(TestCase testCase) {
387 _executable = testCase.commands.last().executable; 400 _executable = testCase.commands.last().executable;
388 _batchArguments = testCase.batchRunnerArguments; 401 _batchArguments = testCase.batchRunnerArguments;
389 _isWebDriver = testCase.configuration['component'] == 'webdriver'; 402 _isWebDriver = testCase.configuration['component'] == 'webdriver';
390 } 403 }
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
433 bool shutdownMillisecs = 30000; 446 bool shutdownMillisecs = 30000;
434 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs); 447 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs);
435 } else { 448 } else {
436 _process.kill(); 449 _process.kill();
437 } 450 }
438 } 451 }
439 } 452 }
440 453
441 void doStartTest(TestCase testCase) { 454 void doStartTest(TestCase testCase) {
442 _startTime = new Date.now(); 455 _startTime = new Date.now();
443 _testStdout = []; 456 _testStdout = new List<String>();
444 _testStderr = []; 457 _testStderr = new List<String>();
458 _stderrDrained = false;
445 _stdoutStream.lineHandler = _readStdout(_stdoutStream, _testStdout); 459 _stdoutStream.lineHandler = _readStdout(_stdoutStream, _testStdout);
446 _stderrStream.lineHandler = _makeReadHandler(_stderrStream, _testStderr); 460 _stderrStream.lineHandler = _readStderr(_stderrStream, _testStderr);
447 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000); 461 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000);
448 var line = _createArgumentsLine(testCase.batchTestArguments); 462 var line = _createArgumentsLine(testCase.batchTestArguments);
449 _process.stdin.write(line.charCodes()); 463 _process.stdin.write(line.charCodes());
450 } 464 }
451 465
452 String _createArgumentsLine(List<String> arguments) { 466 String _createArgumentsLine(List<String> arguments) {
453 return Strings.join(arguments, ' ') + '\n'; 467 return Strings.join(arguments, ' ') + '\n';
454 } 468 }
455 469
456 // This removes the line handler from stderr and reads all 470 void _testCompleted() {
457 // remaining bytes from it.
458 // TODO(zundel): dart:io stream apis need flush()? issue 1407
459 _drainStderr() {
460 _stderrStream.lineHandler = null;
461 while(true) {
462 var available = 0;
463 try {
464 available = _process.stderr.available();
465 } catch (SocketIOException ex) {
466 break;
467 }
468 if (available <= 0) break;
469 String result = _stderrStream.readLine();
470 if (result == null) {
471 // This is intended to catch the last line, but might foul up
472 // if more bytes immediately come available after read() and before
473 // the test for available()
474 result = _stderrStream.read();
475 if (result == null) {
476 var buf = new List<int>(available);
477 _process.stderr.readInto(buf, 0, available);
478 result = new String.fromCharCodes(buf);
479 _testStderr.add(result);
480 break;
481 }
482 }
483 _testStderr.add(result);
484 }
485 }
486
487 int _reportResult(String output) {
488 _drainStderr();
489 var test = _currentTest; 471 var test = _currentTest;
490 _currentTest = null; 472 _currentTest = null;
473 test.completed();
474 }
491 475
476 int _reportResult(String output) {
492 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' 477 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}'
493 var outcome = output.split(" ")[2]; 478 var outcome = output.split(" ")[2];
494 var exitCode = 0; 479 var exitCode = 0;
495 if (outcome == "CRASH") exitCode = -10; 480 if (outcome == "CRASH") exitCode = -10;
496 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; 481 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1;
497 new TestOutput(test, exitCode, outcome == "TIMEOUT", _testStdout, 482 new TestOutput(_currentTest, exitCode, outcome == "TIMEOUT", _testStdout,
498 _testStderr, new Date.now().difference(_startTime)); 483 _testStderr, new Date.now().difference(_startTime));
499 test.completed(); 484 // Move on when both stdout and stderr has been drained.
485 if (_stderrDrained) _testCompleted();
486 }
487
488 void _stderrDone() {
489 _stderrDrained = true;
490 // Move on when both stdout and stderr has been drained.
491 if (_currentTest.output != null) _testCompleted();
500 } 492 }
501 493
502 Function _readStdout(StringInputStream stream, List<String> buffer) { 494 Function _readStdout(StringInputStream stream, List<String> buffer) {
503 return () { 495 return () {
504 var status; 496 var status;
505 if (stream.closed) return; // TODO(whesse): Remove when bug is fixed.
506 var line = stream.readLine(); 497 var line = stream.readLine();
507 // Drain the input stream to get the error output.
508 while (line != null) { 498 while (line != null) {
509 if (line.startsWith('>>> TEST')) { 499 if (line.startsWith('>>> TEST')) {
510 status = line; 500 status = line;
511 } else if (line.startsWith('>>> BATCH START')) { 501 } else if (line.startsWith('>>> BATCH START')) {
512 // ignore 502 // ignore
513 } else if (line.startsWith('>>> ')) { 503 } else if (line.startsWith('>>> ')) {
514 throw new Exception('Unexpected command from dartc batch runner.'); 504 throw new Exception('Unexpected command from dartc batch runner.');
515 } else { 505 } else {
516 buffer.add(line); 506 buffer.add(line);
517 } 507 }
518 line = stream.readLine(); 508 line = stream.readLine();
519 } 509 }
520 if (status != null) { 510 if (status != null) {
521 _timer.cancel(); 511 _timer.cancel();
522 // For crashing processes, let the exit handler deal with it. 512 // For crashing processes, let the exit handler deal with it.
523 if (!status.contains("CRASH")) { 513 if (!status.contains("CRASH")) {
524 _reportResult(status); 514 _reportResult(status);
525 } 515 }
526 } 516 }
527 }; 517 };
528 } 518 }
529 519
520 Function _readStderr(StringInputStream stream, List<String> buffer) {
521 return () {
522 var line = stream.readLine();
523 while (line != null) {
524 if (line.startsWith('>>> EOF STDERR')) {
525 _stderrDone();
526 } else {
527 buffer.add(line);
528 }
529 line = stream.readLine();
530 }
531 };
532 }
533
530 void _exitHandler(exitCode) { 534 void _exitHandler(exitCode) {
531 if (_timer != null) _timer.cancel(); 535 if (_timer != null) _timer.cancel();
532 _reportResult(">>> TEST CRASH");
533 _process.close(); 536 _process.close();
534 _startProcess(); 537 _startProcess(() {
538 _reportResult(">>> TEST CRASH");
539 });
535 } 540 }
536 541
537 void _timeoutHandler(ignore) { 542 void _timeoutHandler(ignore) {
538 _process.exitHandler = (exitCode) {_ 543 _process.exitHandler = (exitCode) {
539 reportResult(">>> TEST TIMEOUT");
540 _process.close(); 544 _process.close();
541 _startProcess(); 545 _startProcess(() {
546 _reportResult(">>> TEST TIMEOUT");
547 });
542 }; 548 };
543 _process.kill(); 549 _process.kill();
544 } 550 }
545 551
546 void _startProcess([Function then = null]) { 552 void _startProcess(then) {
547 _process = new Process.start(_executable, _batchArguments); 553 _process = new Process.start(_executable, _batchArguments);
548 _stdoutStream = new StringInputStream(_process.stdout); 554 _stdoutStream = new StringInputStream(_process.stdout);
549 _stderrStream = new StringInputStream(_process.stderr); 555 _stderrStream = new StringInputStream(_process.stderr);
550 _testStdout = []; 556 _testStdout = new List<String>();
551 _testStderr = []; 557 _testStderr = new List<String>();
558 _stderrDrained = false;
552 _stdoutStream.lineHandler = _readStdout(_stdoutStream, _testStdout); 559 _stdoutStream.lineHandler = _readStdout(_stdoutStream, _testStdout);
553 _stderrStream.lineHandler =_makeReadHandler(_stderrStream, _testStderr); 560 _stderrStream.lineHandler = _readStderr(_stderrStream, _testStderr);
554 _process.exitHandler = _exitHandler; 561 _process.exitHandler = _exitHandler;
555 _process.startHandler = then; 562 _process.startHandler = then;
556 } 563 }
557 } 564 }
558 565
559 /** 566 /**
560 * ProcessQueue is the master control class, responsible for running all 567 * ProcessQueue is the master control class, responsible for running all
561 * the tests in all the TestSuites that have been registered. It includes 568 * the tests in all the TestSuites that have been registered. It includes
562 * a rate-limited queue to run a limited number of tests in parallel, 569 * a rate-limited queue to run a limited number of tests in parallel,
563 * a ProgressIndicator which prints output when tests are started and 570 * a ProgressIndicator which prints output when tests are started and
(...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after
794 // the developer doesn't waste his or her time trying to fix a bunch of 801 // the developer doesn't waste his or her time trying to fix a bunch of
795 // tests that appear to be broken but were actually just flakes that 802 // tests that appear to be broken but were actually just flakes that
796 // didn't get retried because there had already been one failure. 803 // didn't get retried because there had already been one failure.
797 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; 804 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests;
798 new RunningProcess(test, allowRetry, this).start(); 805 new RunningProcess(test, allowRetry, this).start();
799 } 806 }
800 _numProcesses++; 807 _numProcesses++;
801 } 808 }
802 } 809 }
803 } 810 }
804
805 Function _makeReadHandler(StringInputStream source, List<String> destination) {
806 return () {
807 if (source.closed) return; // TODO(whesse): Remove when bug is fixed.
808 var line = source.readLine();
809 while (null != line) {
810 destination.add(line);
811 line = source.readLine();
812 }
813 };
814 }
815
816
OLDNEW
« no previous file with comments | « compiler/java/com/google/dart/compiler/UnitTestBatchRunner.java ('k') | tools/testing/run_selenium.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698