| 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 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 49 } | 49 } |
| 50 | 50 |
| 51 /** | 51 /** |
| 52 * TestCase contains all the information needed to run a test and evaluate | 52 * TestCase contains all the information needed to run a test and evaluate |
| 53 * its output. Running a test involves starting a separate process, with | 53 * its output. Running a test involves starting a separate process, with |
| 54 * the executable and arguments given by the TestCase, and recording its | 54 * the executable and arguments given by the TestCase, and recording its |
| 55 * stdout and stderr output streams, and its exit code. TestCase only | 55 * stdout and stderr output streams, and its exit code. TestCase only |
| 56 * contains static information about the test; actually running the test is | 56 * contains static information about the test; actually running the test is |
| 57 * performed by [ProcessQueue] using a [RunningProcess] object. | 57 * performed by [ProcessQueue] using a [RunningProcess] object. |
| 58 * | 58 * |
| 59 * The output information is stored in a [TestOutput] instance contained | 59 * The output information is stored in a [CommandOutput] instance contained |
| 60 * in the TestCase. The TestOutput instance is responsible for evaluating | 60 * in TestCase.commandOutputs. The last CommandOutput instance is responsible |
| 61 * if the test has passed, failed, crashed, or timed out, and the TestCase | 61 * for evaluating if the test has passed, failed, crashed, or timed out, and the |
| 62 * has information about what the expected result of the test should be. | 62 * TestCase has information about what the expected result of the test should |
| 63 * be. |
| 63 * | 64 * |
| 64 * The TestCase has a callback function, [completedHandler], that is run when | 65 * The TestCase has a callback function, [completedHandler], that is run when |
| 65 * the test is completed. | 66 * the test is completed. |
| 66 */ | 67 */ |
| 67 class TestCase { | 68 class TestCase { |
| 68 /** | 69 /** |
| 69 * A list of commands to execute. Most test cases have a single command. Frog | 70 * A list of commands to execute. Most test cases have a single command. |
| 70 * tests have two commands, one to compilate the source and another to execute | 71 * Dart2js tests have two commands, one to compile the source and another |
| 71 * it. Some isolate tests might even have three, if they require compiling | 72 * to execute it. Some isolate tests might even have three, if they require |
| 72 * multiple sources that are run in isolation. | 73 * compiling multiple sources that are run in isolation. |
| 73 */ | 74 */ |
| 74 List<Command> commands; | 75 List<Command> commands; |
| 76 Map<Command, CommandOutput> commandOutputs = new Map<Command,CommandOutput>(); |
| 75 | 77 |
| 76 Map configuration; | 78 Map configuration; |
| 77 String displayName; | 79 String displayName; |
| 78 TestOutput output; | |
| 79 bool isNegative; | 80 bool isNegative; |
| 80 Set<String> expectedOutcomes; | 81 Set<String> expectedOutcomes; |
| 81 TestCaseEvent completedHandler; | 82 TestCaseEvent completedHandler; |
| 82 TestInformation info; | 83 TestInformation info; |
| 83 | 84 |
| 84 TestCase(this.displayName, | 85 TestCase(this.displayName, |
| 85 this.commands, | 86 this.commands, |
| 86 this.configuration, | 87 this.configuration, |
| 87 this.completedHandler, | 88 this.completedHandler, |
| 88 this.expectedOutcomes, | 89 this.expectedOutcomes, |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 137 newCommands.add(newCommand); | 138 newCommands.add(newCommand); |
| 138 // If there are extra spaces inside the prefix or suffix, this fails. | 139 // If there are extra spaces inside the prefix or suffix, this fails. |
| 139 String expected = | 140 String expected = |
| 140 '$prefix ${c.executable} $suffix ${Strings.join(c.arguments, ' ')}'; | 141 '$prefix ${c.executable} $suffix ${Strings.join(c.arguments, ' ')}'; |
| 141 Expect.stringEquals(expected.trim(), newCommand.commandLine); | 142 Expect.stringEquals(expected.trim(), newCommand.commandLine); |
| 142 } | 143 } |
| 143 commands = newCommands; | 144 commands = newCommands; |
| 144 } | 145 } |
| 145 } | 146 } |
| 146 | 147 |
| 148 CommandOutput get lastCommandOutput { |
| 149 if (commandOutputs.length == 0) { |
| 150 throw new Exception("CommandOutputs is empty, maybe no command was run? (" |
| 151 "displayName: '$displayName', " |
| 152 "configurationString: '$configurationString')"); |
| 153 } |
| 154 return commandOutputs[commands[commandOutputs.length - 1]]; |
| 155 } |
| 156 |
| 147 int get timeout { | 157 int get timeout { |
| 148 if (expectedOutcomes.contains(SLOW)) { | 158 if (expectedOutcomes.contains(SLOW)) { |
| 149 return configuration['timeout'] * SLOW_TIMEOUT_MULTIPLIER; | 159 return configuration['timeout'] * SLOW_TIMEOUT_MULTIPLIER; |
| 150 } else { | 160 } else { |
| 151 return configuration['timeout']; | 161 return configuration['timeout']; |
| 152 } | 162 } |
| 153 } | 163 } |
| 154 | 164 |
| 155 String get configurationString { | 165 String get configurationString { |
| 156 final compiler = configuration['compiler']; | 166 final compiler = configuration['compiler']; |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 232 */ | 242 */ |
| 233 void notifyObservers() { | 243 void notifyObservers() { |
| 234 for (BrowserTestCase testCase in observers) { | 244 for (BrowserTestCase testCase in observers) { |
| 235 testCase.waitingForOtherTest = false; | 245 testCase.waitingForOtherTest = false; |
| 236 } | 246 } |
| 237 } | 247 } |
| 238 } | 248 } |
| 239 | 249 |
| 240 | 250 |
| 241 /** | 251 /** |
| 242 * TestOutput records the output of a completed test: the process's exit code, | 252 * CommandOutput records the output of a completed command: the process's exit |
| 243 * the standard output and standard error, whether the process timed out, and | 253 * code, the standard output and standard error, whether the process timed out, |
| 244 * the time the process took to run. It also contains a pointer to the | 254 * and the time the process took to run. It also contains a pointer to the |
| 245 * [TestCase] this is the output of. | 255 * [TestCase] this is the output of. |
| 246 */ | 256 */ |
| 247 abstract class TestOutput { | 257 abstract class CommandOutput { |
| 248 factory TestOutput.fromCase(TestCase testCase, | 258 factory CommandOutput.fromCase(TestCase testCase, |
| 249 int exitCode, | 259 Command command, |
| 250 bool incomplete, | 260 int exitCode, |
| 251 bool timedOut, | 261 bool incomplete, |
| 252 List<String> stdout, | 262 bool timedOut, |
| 253 List<String> stderr, | 263 List<String> stdout, |
| 254 Duration time) { | 264 List<String> stderr, |
| 255 return new TestOutputImpl.fromCase( | 265 Duration time) { |
| 256 testCase, exitCode, incomplete, timedOut, stdout, stderr, time); | 266 return new CommandOutputImpl.fromCase(testCase, |
| 267 command, |
| 268 exitCode, |
| 269 incomplete, |
| 270 timedOut, |
| 271 stdout, |
| 272 stderr, |
| 273 time); |
| 257 } | 274 } |
| 258 | 275 |
| 259 bool get incomplete; | 276 bool get incomplete; |
| 260 | 277 |
| 261 String get result; | 278 String get result; |
| 262 | 279 |
| 263 bool get unexpectedOutput; | 280 bool get unexpectedOutput; |
| 264 | 281 |
| 265 bool get hasCrashed; | 282 bool get hasCrashed; |
| 266 | 283 |
| 267 bool get hasTimedOut; | 284 bool get hasTimedOut; |
| 268 | 285 |
| 269 bool get didFail; | 286 bool get didFail; |
| 270 | 287 |
| 271 bool requestRetry; | 288 bool requestRetry; |
| 272 | 289 |
| 273 Duration get time; | 290 Duration get time; |
| 274 | 291 |
| 275 int get exitCode; | 292 int get exitCode; |
| 276 | 293 |
| 277 List<String> get stdout; | 294 List<String> get stdout; |
| 278 | 295 |
| 279 List<String> get stderr; | 296 List<String> get stderr; |
| 280 | 297 |
| 281 List<String> get diagnostics; | 298 List<String> get diagnostics; |
| 282 } | 299 } |
| 283 | 300 |
| 284 class TestOutputImpl implements TestOutput { | 301 class CommandOutputImpl implements CommandOutput { |
| 285 TestCase testCase; | 302 TestCase testCase; |
| 286 int exitCode; | 303 int exitCode; |
| 287 | 304 |
| 288 /// Records if all commands were run, true if they weren't. | 305 /// Records if all commands were run, true if they weren't. |
| 289 final bool incomplete; | 306 final bool incomplete; |
| 290 | 307 |
| 291 bool timedOut; | 308 bool timedOut; |
| 292 bool failed = false; | 309 bool failed = false; |
| 293 List<String> stdout; | 310 List<String> stdout; |
| 294 List<String> stderr; | 311 List<String> stderr; |
| 295 Duration time; | 312 Duration time; |
| 296 List<String> diagnostics; | 313 List<String> diagnostics; |
| 297 | 314 |
| 298 /** | 315 /** |
| 299 * A flag to indicate we have already printed a warning about ignoring the VM | 316 * A flag to indicate we have already printed a warning about ignoring the VM |
| 300 * crash, to limit the amount of output produced per test. | 317 * crash, to limit the amount of output produced per test. |
| 301 */ | 318 */ |
| 302 bool alreadyPrintedWarning = false; | 319 bool alreadyPrintedWarning = false; |
| 303 | 320 |
| 304 /** | 321 /** |
| 305 * Set to true if we encounter a condition in the output that indicates we | 322 * Set to true if we encounter a condition in the output that indicates we |
| 306 * need to rerun this test. | 323 * need to rerun this test. |
| 307 */ | 324 */ |
| 308 bool requestRetry = false; | 325 bool requestRetry = false; |
| 309 | 326 |
| 310 // Don't call this constructor, call TestOutput.fromCase() to | 327 // Don't call this constructor, call CommandOutput.fromCase() to |
| 311 // get a new TestOutput instance. | 328 // get a new TestOutput instance. |
| 312 TestOutputImpl(TestCase this.testCase, | 329 CommandOutputImpl(TestCase this.testCase, |
| 313 int this.exitCode, | 330 Command command, |
| 314 bool this.incomplete, | 331 int this.exitCode, |
| 315 bool this.timedOut, | 332 bool this.incomplete, |
| 316 List<String> this.stdout, | 333 bool this.timedOut, |
| 317 List<String> this.stderr, | 334 List<String> this.stdout, |
| 318 Duration this.time) { | 335 List<String> this.stderr, |
| 319 testCase.output = this; | 336 Duration this.time) { |
| 337 testCase.commandOutputs[command] = this; |
| 320 diagnostics = []; | 338 diagnostics = []; |
| 321 } | 339 } |
| 322 factory TestOutputImpl.fromCase(TestCase testCase, | 340 factory CommandOutputImpl.fromCase(TestCase testCase, |
| 323 int exitCode, | 341 Command command, |
| 324 bool incomplete, | 342 int exitCode, |
| 325 bool timedOut, | 343 bool incomplete, |
| 326 List<String> stdout, | 344 bool timedOut, |
| 327 List<String> stderr, | 345 List<String> stdout, |
| 328 Duration time) { | 346 List<String> stderr, |
| 347 Duration time) { |
| 329 if (testCase is BrowserTestCase) { | 348 if (testCase is BrowserTestCase) { |
| 330 return new BrowserTestOutputImpl(testCase, exitCode, incomplete, | 349 return new BrowserCommandOutputImpl(testCase, |
| 331 timedOut, stdout, stderr, time); | 350 command, |
| 351 exitCode, |
| 352 incomplete, |
| 353 timedOut, |
| 354 stdout, |
| 355 stderr, |
| 356 time); |
| 332 } else if (testCase.configuration['compiler'] == 'dartc') { | 357 } else if (testCase.configuration['compiler'] == 'dartc') { |
| 333 return new AnalysisTestOutputImpl(testCase, exitCode, timedOut, | 358 return new AnalysisCommandOutputImpl(testCase, |
| 334 stdout, stderr, time); | 359 command, |
| 360 exitCode, |
| 361 timedOut, |
| 362 stdout, |
| 363 stderr, |
| 364 time); |
| 335 } | 365 } |
| 336 return new TestOutputImpl(testCase, exitCode, incomplete, timedOut, | 366 return new CommandOutputImpl(testCase, |
| 337 stdout, stderr, time); | 367 command, |
| 368 exitCode, |
| 369 incomplete, |
| 370 timedOut, |
| 371 stdout, |
| 372 stderr, |
| 373 time); |
| 338 } | 374 } |
| 339 | 375 |
| 340 String get result => | 376 String get result => |
| 341 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); | 377 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); |
| 342 | 378 |
| 343 bool get unexpectedOutput => !testCase.expectedOutcomes.contains(result); | 379 bool get unexpectedOutput => !testCase.expectedOutcomes.contains(result); |
| 344 | 380 |
| 345 bool get hasCrashed { | 381 bool get hasCrashed { |
| 346 // The Java dartc runner and dart2js exits with code 253 in case | 382 // The Java dartc runner and dart2js exits with code 253 in case |
| 347 // of unhandled exceptions. | 383 // of unhandled exceptions. |
| (...skipping 19 matching lines...) Expand all Loading... |
| 367 bool get hasFailed { | 403 bool get hasFailed { |
| 368 // Always fail if a runtime-error is expected and compilation failed. | 404 // Always fail if a runtime-error is expected and compilation failed. |
| 369 if (testCase.info != null && testCase.info.hasRuntimeError && incomplete) { | 405 if (testCase.info != null && testCase.info.hasRuntimeError && incomplete) { |
| 370 return true; | 406 return true; |
| 371 } | 407 } |
| 372 return testCase.isNegative ? !didFail : didFail; | 408 return testCase.isNegative ? !didFail : didFail; |
| 373 } | 409 } |
| 374 | 410 |
| 375 } | 411 } |
| 376 | 412 |
| 377 class BrowserTestOutputImpl extends TestOutputImpl { | 413 class BrowserCommandOutputImpl extends CommandOutputImpl { |
| 378 BrowserTestOutputImpl(testCase, exitCode, incomplete, | 414 BrowserCommandOutputImpl( |
| 379 timedOut, stdout, stderr, time) : | 415 testCase, |
| 380 super(testCase, exitCode, incomplete, timedOut, stdout, stderr, time); | 416 command, |
| 417 exitCode, |
| 418 incomplete, |
| 419 timedOut, |
| 420 stdout, |
| 421 stderr, |
| 422 time) : |
| 423 super(testCase, |
| 424 command, |
| 425 exitCode, |
| 426 incomplete, |
| 427 timedOut, |
| 428 stdout, |
| 429 stderr, |
| 430 time); |
| 381 | 431 |
| 382 bool get didFail { | 432 bool get didFail { |
| 383 // Browser case: | 433 // Browser case: |
| 384 // If the browser test failed, it may have been because DumpRenderTree | 434 // If the browser test failed, it may have been because DumpRenderTree |
| 385 // and the virtual framebuffer X server didn't hook up, or DRT crashed with | 435 // and the virtual framebuffer X server didn't hook up, or DRT crashed with |
| 386 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, | 436 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, |
| 387 // so we have to do this check first. | 437 // so we have to do this check first. |
| 388 for (String line in super.stderr) { | 438 for (String line in super.stderr) { |
| 389 if (line.contains('Gtk-WARNING **: cannot open display: :99') || | 439 if (line.contains('Gtk-WARNING **: cannot open display: :99') || |
| 390 line.contains('Failed to run command. return code=1')) { | 440 line.contains('Failed to run command. return code=1')) { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 413 break; | 463 break; |
| 414 } | 464 } |
| 415 } | 465 } |
| 416 return true; | 466 return true; |
| 417 } | 467 } |
| 418 } | 468 } |
| 419 | 469 |
| 420 // The static analyzer does not actually execute code, so | 470 // The static analyzer does not actually execute code, so |
| 421 // the criteria for success now depend on the text sent | 471 // the criteria for success now depend on the text sent |
| 422 // to stderr. | 472 // to stderr. |
| 423 class AnalysisTestOutputImpl extends TestOutputImpl { | 473 class AnalysisCommandOutputImpl extends CommandOutputImpl { |
| 424 // An error line has 8 fields that look like: | 474 // An error line has 8 fields that look like: |
| 425 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. | 475 // ERROR|COMPILER|MISSING_SOURCE|file:/tmp/t.dart|15|1|24|Missing source. |
| 426 final int ERROR_LEVEL = 0; | 476 final int ERROR_LEVEL = 0; |
| 427 final int ERROR_TYPE = 1; | 477 final int ERROR_TYPE = 1; |
| 428 final int FORMATTED_ERROR = 7; | 478 final int FORMATTED_ERROR = 7; |
| 429 | 479 |
| 430 bool alreadyComputed = false; | 480 bool alreadyComputed = false; |
| 431 bool failResult; | 481 bool failResult; |
| 432 AnalysisTestOutputImpl(testCase, exitCode, timedOut, stdout, stderr, time) : | 482 AnalysisCommandOutputImpl(testCase, |
| 433 super(testCase, exitCode, false, timedOut, stdout, stderr, time); | 483 command, |
| 484 exitCode, |
| 485 timedOut, |
| 486 stdout, |
| 487 stderr, |
| 488 time) : |
| 489 super(testCase, command, exitCode, false, timedOut, stdout, stderr, time); |
| 434 | 490 |
| 435 bool get didFail { | 491 bool get didFail { |
| 436 if (!alreadyComputed) { | 492 if (!alreadyComputed) { |
| 437 failResult = _didFail(); | 493 failResult = _didFail(); |
| 438 alreadyComputed = true; | 494 alreadyComputed = true; |
| 439 } | 495 } |
| 440 return failResult; | 496 return failResult; |
| 441 } | 497 } |
| 442 | 498 |
| 443 bool _didFail() { | 499 bool _didFail() { |
| (...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 576 } | 632 } |
| 577 result.add(field.toString()); | 633 result.add(field.toString()); |
| 578 return result; | 634 return result; |
| 579 } | 635 } |
| 580 } | 636 } |
| 581 | 637 |
| 582 /** | 638 /** |
| 583 * A RunningProcess actually runs a test, getting the command lines from | 639 * A RunningProcess actually runs a test, getting the command lines from |
| 584 * its [TestCase], starting the test process (and first, a compilation | 640 * its [TestCase], starting the test process (and first, a compilation |
| 585 * process if the TestCase is a [BrowserTestCase]), creating a timeout | 641 * process if the TestCase is a [BrowserTestCase]), creating a timeout |
| 586 * timer, and recording the results in a new [TestOutput] object, which it | 642 * timer, and recording the results in a new [CommandOutput] object, which it |
| 587 * attaches to the TestCase. The lifetime of the RunningProcess is limited | 643 * attaches to the TestCase. The lifetime of the RunningProcess is limited |
| 588 * to the time it takes to start the process, run the process, and record | 644 * to the time it takes to start the process, run the process, and record |
| 589 * the result; there are no pointers to it, so it should be available to | 645 * the result; there are no pointers to it, so it should be available to |
| 590 * be garbage collected as soon as it is done. | 646 * be garbage collected as soon as it is done. |
| 591 */ | 647 */ |
| 592 class RunningProcess { | 648 class RunningProcess { |
| 593 ProcessQueue processQueue; | 649 ProcessQueue processQueue; |
| 594 Process process; | 650 Process process; |
| 595 TestCase testCase; | 651 TestCase testCase; |
| 596 bool timedOut = false; | 652 bool timedOut = false; |
| 597 Date startTime; | 653 Date startTime; |
| 598 Timer timeoutTimer; | 654 Timer timeoutTimer; |
| 599 List<String> stdout; | 655 List<String> stdout; |
| 600 List<String> stderr; | 656 List<String> stderr; |
| 601 bool allowRetries; | 657 bool allowRetries; |
| 602 | 658 |
| 603 /** Which command of [testCase.commands] is currently being executed. */ | 659 /** Which command of [testCase.commands] is currently being executed. */ |
| 604 int currentStep; | 660 int currentStep; |
| 605 | 661 |
| 606 RunningProcess(TestCase this.testCase, | 662 RunningProcess(TestCase this.testCase, |
| 607 [this.allowRetries = false, this.processQueue]); | 663 [this.allowRetries = false, this.processQueue]); |
| 608 | 664 |
| 609 /** | 665 /** |
| 610 * Called when all commands are executed. [exitCode] is 0 if all command | 666 * Called when all commands are executed. |
| 611 * succeded, otherwise it will have the exit code of the first failing | |
| 612 * command. | |
| 613 */ | 667 */ |
| 614 void testComplete(int exitCode, bool incomplete) { | 668 void testComplete(CommandOutput lastCommandOutput) { |
| 615 new TestOutput.fromCase(testCase, exitCode, incomplete, timedOut, stdout, | |
| 616 stderr, new Date.now().difference(startTime)); | |
| 617 timeoutTimer.cancel(); | 669 timeoutTimer.cancel(); |
| 618 if (testCase.output.unexpectedOutput | 670 if (lastCommandOutput.unexpectedOutput |
| 619 && testCase.configuration['verbose'] != null | 671 && testCase.configuration['verbose'] != null |
| 620 && testCase.configuration['verbose']) { | 672 && testCase.configuration['verbose']) { |
| 621 print(testCase.displayName); | 673 print(testCase.displayName); |
| 622 for (var line in testCase.output.stderr) print(line); | 674 for (var line in lastCommandOutput.stderr) print(line); |
| 623 for (var line in testCase.output.stdout) print(line); | 675 for (var line in lastCommandOutput.stdout) print(line); |
| 624 } | 676 } |
| 625 if (allowRetries && testCase.usesWebDriver | 677 if (allowRetries && testCase.usesWebDriver |
| 626 && testCase.output.unexpectedOutput | 678 && lastCommandOutput.unexpectedOutput |
| 627 && (testCase as BrowserTestCase).numRetries > 0) { | 679 && (testCase as BrowserTestCase).numRetries > 0) { |
| 628 // Selenium tests can be flaky. Try rerunning. | 680 // Selenium tests can be flaky. Try rerunning. |
| 629 testCase.output.requestRetry = true; | 681 lastCommandOutput.requestRetry = true; |
| 630 } | 682 } |
| 631 if (testCase.output.requestRetry) { | 683 if (lastCommandOutput.requestRetry) { |
| 632 testCase.output.requestRetry = false; | 684 lastCommandOutput.requestRetry = false; |
| 633 this.timedOut = false; | 685 this.timedOut = false; |
| 634 (testCase as BrowserTestCase).numRetries--; | 686 (testCase as BrowserTestCase).numRetries--; |
| 635 print("Potential flake. Re-running ${testCase.displayName} " | 687 print("Potential flake. Re-running ${testCase.displayName} " |
| 636 "(${(testCase as BrowserTestCase).numRetries} attempt(s) remains)"); | 688 "(${(testCase as BrowserTestCase).numRetries} attempt(s) remains)"); |
| 637 // When retrying we need to reset the timeout as well. | 689 // When retrying we need to reset the timeout as well. |
| 638 // Otherwise there will be no timeout handling for the retry. | 690 // Otherwise there will be no timeout handling for the retry. |
| 639 timeoutTimer = null; | 691 timeoutTimer = null; |
| 640 this.start(); | 692 this.start(); |
| 641 } else { | 693 } else { |
| 642 testCase.completed(); | 694 testCase.completed(); |
| 643 } | 695 } |
| 644 } | 696 } |
| 645 | 697 |
| 646 /** | 698 /** |
| 647 * Process exit handler called at the end of every command. It internally | 699 * Process exit handler called at the end of every command. It internally |
| 648 * treats all but the last command as compilation steps. The last command is | 700 * treats all but the last command as compilation steps. The last command is |
| 649 * the actual test and its output is analyzed in [testComplete]. | 701 * the actual test and its output is analyzed in [testComplete]. |
| 650 */ | 702 */ |
| 651 void stepExitHandler(int exitCode) { | 703 void commandComplete(Command command, int exitCode) { |
| 652 process = null; | 704 process = null; |
| 653 int totalSteps = testCase.commands.length; | 705 int totalSteps = testCase.commands.length; |
| 654 String suffix =' (step $currentStep of $totalSteps)'; | 706 String suffix =' (step $currentStep of $totalSteps)'; |
| 655 if (timedOut) { | 707 if (timedOut) { |
| 656 // Non-webdriver test timed out before it could complete. Webdriver tests | 708 // Non-webdriver test timed out before it could complete. Webdriver tests |
| 657 // run their own timeouts by timing from the launch of the browser (which | 709 // run their own timeouts by timing from the launch of the browser (which |
| 658 // could be delayed). | 710 // could be delayed). |
| 659 testComplete(0, true); | 711 testComplete(createCommandOutput(command, 0, true)); |
| 660 } else if (currentStep == totalSteps) { | 712 } else if (currentStep == totalSteps) { |
| 661 // Done with all test commands. | 713 // Done with all test commands. |
| 662 testComplete(exitCode, false); | 714 testComplete(createCommandOutput(command, exitCode, false)); |
| 663 } else if (exitCode != 0) { | 715 } else if (exitCode != 0) { |
| 664 // One of the steps failed. | 716 // One of the steps failed. |
| 665 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n'); | 717 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n'); |
| 666 testComplete(exitCode, true); | 718 testComplete(createCommandOutput(command, exitCode, true)); |
| 667 } else { | 719 } else { |
| 720 createCommandOutput(command, exitCode, true); |
| 668 // One compilation step successfully completed, move on to the | 721 // One compilation step successfully completed, move on to the |
| 669 // next step. | 722 // next step. |
| 670 stderr.add('test.dart: Compilation finished $suffix\n'); | 723 stderr.add('test.dart: Compilation finished $suffix\n'); |
| 671 stdout.add('test.dart: Compilation finished $suffix\n'); | 724 stdout.add('test.dart: Compilation finished $suffix\n'); |
| 672 if (currentStep == totalSteps - 1 && testCase.usesWebDriver && | 725 if (currentStep == totalSteps - 1 && testCase.usesWebDriver && |
| 673 !testCase.configuration['noBatch']) { | 726 !testCase.configuration['noBatch']) { |
| 674 // Note: processQueue will always be non-null for runtime == ie9, ie10, | 727 // Note: processQueue will always be non-null for runtime == ie9, ie10, |
| 675 // ff, safari, chrome, opera. (It is only null for runtime == vm) | 728 // ff, safari, chrome, opera. (It is only null for runtime == vm) |
| 676 // This RunningProcess object is done, and hands over control to | 729 // This RunningProcess object is done, and hands over control to |
| 677 // BatchRunner.startTest(), which handles reporting, etc. | 730 // BatchRunner.startTest(), which handles reporting, etc. |
| 678 timeoutTimer.cancel(); | 731 timeoutTimer.cancel(); |
| 679 processQueue._getBatchRunner(testCase).startTest(testCase); | 732 processQueue._getBatchRunner(testCase).startTest(testCase); |
| 680 } else { | 733 } else { |
| 681 runCommand(testCase.commands[currentStep++], stepExitHandler); | 734 runCommand(testCase.commands[currentStep++], commandComplete); |
| 682 } | 735 } |
| 683 } | 736 } |
| 684 } | 737 } |
| 685 | 738 |
| 739 /** |
| 740 * Called for all executed commands. |
| 741 */ |
| 742 CommandOutput createCommandOutput(Command command, |
| 743 int exitCode, |
| 744 bool incomplete) { |
| 745 var commandOutput = new CommandOutput.fromCase( |
| 746 testCase, |
| 747 command, |
| 748 exitCode, |
| 749 incomplete, |
| 750 timedOut, |
| 751 stdout, |
| 752 stderr, |
| 753 new Date.now().difference(startTime)); |
| 754 resetLocalOutputInformation(); |
| 755 return commandOutput; |
| 756 } |
| 757 |
| 758 void resetLocalOutputInformation() { |
| 759 stdout = new List<String>(); |
| 760 stderr = new List<String>(); |
| 761 } |
| 762 |
| 686 VoidFunction makeReadHandler(StringInputStream source, | 763 VoidFunction makeReadHandler(StringInputStream source, |
| 687 List<String> destination) { | 764 List<String> destination) { |
| 688 void handler () { | 765 void handler () { |
| 689 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. | 766 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. |
| 690 var line = source.readLine(); | 767 var line = source.readLine(); |
| 691 while (null != line) { | 768 while (null != line) { |
| 692 destination.add(line); | 769 destination.add(line); |
| 693 line = source.readLine(); | 770 line = source.readLine(); |
| 694 } | 771 } |
| 695 } | 772 } |
| 696 return handler; | 773 return handler; |
| 697 } | 774 } |
| 698 | 775 |
| 699 void start() { | 776 void start() { |
| 700 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); | 777 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); |
| 701 stdout = new List<String>(); | 778 resetLocalOutputInformation(); |
| 702 stderr = new List<String>(); | |
| 703 currentStep = 0; | 779 currentStep = 0; |
| 704 startTime = new Date.now(); | 780 startTime = new Date.now(); |
| 705 runCommand(testCase.commands[currentStep++], stepExitHandler); | 781 runCommand(testCase.commands[currentStep++], commandComplete); |
| 706 } | 782 } |
| 707 | 783 |
| 708 void runCommand(Command command, void exitHandler(int exitCode)) { | 784 void runCommand(Command command, void commandCompleteHandler(Command, int)) { |
| 785 void processExitHandler(int returnCode) { |
| 786 commandCompleteHandler(command, returnCode); |
| 787 } |
| 788 |
| 709 Future processFuture = Process.start(command.executable, command.arguments); | 789 Future processFuture = Process.start(command.executable, command.arguments); |
| 710 processFuture.then((Process p) { | 790 processFuture.then((Process p) { |
| 711 process = p; | 791 process = p; |
| 712 process.onExit = exitHandler; | 792 process.onExit = processExitHandler; |
| 713 var stdoutStringStream = new StringInputStream(process.stdout); | 793 var stdoutStringStream = new StringInputStream(process.stdout); |
| 714 var stderrStringStream = new StringInputStream(process.stderr); | 794 var stderrStringStream = new StringInputStream(process.stderr); |
| 715 stdoutStringStream.onLine = | 795 stdoutStringStream.onLine = |
| 716 makeReadHandler(stdoutStringStream, stdout); | 796 makeReadHandler(stdoutStringStream, stdout); |
| 717 stderrStringStream.onLine = | 797 stderrStringStream.onLine = |
| 718 makeReadHandler(stderrStringStream, stderr); | 798 makeReadHandler(stderrStringStream, stderr); |
| 719 if (timeoutTimer == null) { | 799 if (timeoutTimer == null) { |
| 720 // Create one timeout timer when starting test case, remove it at end. | 800 // Create one timeout timer when starting test case, remove it at end. |
| 721 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); | 801 timeoutTimer = new Timer(1000 * testCase.timeout, timeoutHandler); |
| 722 } | 802 } |
| 723 // If the timeout fired in between two commands, kill the just | 803 // If the timeout fired in between two commands, kill the just |
| 724 // started process immediately. | 804 // started process immediately. |
| 725 if (timedOut) safeKill(process); | 805 if (timedOut) safeKill(process); |
| 726 }); | 806 }); |
| 727 processFuture.handleException((e) { | 807 processFuture.handleException((e) { |
| 728 print("Process error:"); | 808 print("Process error:"); |
| 729 print(" Command: $command"); | 809 print(" Command: $command"); |
| 730 print(" Error: $e"); | 810 print(" Error: $e"); |
| 731 testComplete(-1, false); | 811 testComplete(createCommandOutput(command, -1, false)); |
| 732 return true; | 812 return true; |
| 733 }); | 813 }); |
| 734 } | 814 } |
| 735 | 815 |
| 736 void timeoutHandler(Timer unusedTimer) { | 816 void timeoutHandler(Timer unusedTimer) { |
| 737 timedOut = true; | 817 timedOut = true; |
| 738 safeKill(process); | 818 safeKill(process); |
| 739 } | 819 } |
| 740 | 820 |
| 741 void safeKill(Process p) { | 821 void safeKill(Process p) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 752 /** | 832 /** |
| 753 * This class holds a value, that can be changed. It is used when | 833 * This class holds a value, that can be changed. It is used when |
| 754 * closures need a shared value, that they can all change and read. | 834 * closures need a shared value, that they can all change and read. |
| 755 */ | 835 */ |
| 756 class MutableValue<T> { | 836 class MutableValue<T> { |
| 757 MutableValue(T this.value); | 837 MutableValue(T this.value); |
| 758 T value; | 838 T value; |
| 759 } | 839 } |
| 760 | 840 |
| 761 class BatchRunnerProcess { | 841 class BatchRunnerProcess { |
| 842 Command _command; |
| 762 String _executable; | 843 String _executable; |
| 763 List<String> _batchArguments; | 844 List<String> _batchArguments; |
| 764 | 845 |
| 765 Process _process; | 846 Process _process; |
| 766 StringInputStream _stdoutStream; | 847 StringInputStream _stdoutStream; |
| 767 StringInputStream _stderrStream; | 848 StringInputStream _stderrStream; |
| 768 | 849 |
| 769 TestCase _currentTest; | 850 TestCase _currentTest; |
| 770 List<String> _testStdout; | 851 List<String> _testStdout; |
| 771 List<String> _testStderr; | 852 List<String> _testStderr; |
| 772 String _status; | 853 String _status; |
| 773 bool _stdoutDrained = false; | 854 bool _stdoutDrained = false; |
| 774 bool _stderrDrained = false; | 855 bool _stderrDrained = false; |
| 775 MutableValue<bool> _ignoreStreams; | 856 MutableValue<bool> _ignoreStreams; |
| 776 Date _startTime; | 857 Date _startTime; |
| 777 Timer _timer; | 858 Timer _timer; |
| 778 | 859 |
| 779 bool _isWebDriver; | 860 bool _isWebDriver; |
| 780 | 861 |
| 781 BatchRunnerProcess(TestCase testCase) { | 862 BatchRunnerProcess(TestCase testCase) { |
| 863 _command = testCase.commands.last; |
| 782 _executable = testCase.commands.last.executable; | 864 _executable = testCase.commands.last.executable; |
| 783 _batchArguments = testCase.batchRunnerArguments; | 865 _batchArguments = testCase.batchRunnerArguments; |
| 784 _isWebDriver = testCase.usesWebDriver; | 866 _isWebDriver = testCase.usesWebDriver; |
| 785 } | 867 } |
| 786 | 868 |
| 787 bool get active => _currentTest != null; | 869 bool get active => _currentTest != null; |
| 788 | 870 |
| 789 void startTest(TestCase testCase) { | 871 void startTest(TestCase testCase) { |
| 790 Expect.isNull(_currentTest); | 872 Expect.isNull(_currentTest); |
| 791 _currentTest = testCase; | 873 _currentTest = testCase; |
| 874 _command = testCase.commands.last; |
| 792 if (_process == null) { | 875 if (_process == null) { |
| 793 // Start process if not yet started. | 876 // Start process if not yet started. |
| 794 _executable = testCase.commands.last.executable; | 877 _executable = testCase.commands.last.executable; |
| 795 _startProcess(() { | 878 _startProcess(() { |
| 796 doStartTest(testCase); | 879 doStartTest(testCase); |
| 797 }); | 880 }); |
| 798 } else if (testCase.commands.last.executable != _executable) { | 881 } else if (testCase.commands.last.executable != _executable) { |
| 799 // Restart this runner with the right executable for this test | 882 // Restart this runner with the right executable for this test |
| 800 // if needed. | 883 // if needed. |
| 801 _executable = testCase.commands.last.executable; | 884 _executable = testCase.commands.last.executable; |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 862 } | 945 } |
| 863 | 946 |
| 864 void _reportResult() { | 947 void _reportResult() { |
| 865 if (!active) return; | 948 if (!active) return; |
| 866 // _status == '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' | 949 // _status == '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' |
| 867 | 950 |
| 868 var outcome = _status.split(" ")[2]; | 951 var outcome = _status.split(" ")[2]; |
| 869 var exitCode = 0; | 952 var exitCode = 0; |
| 870 if (outcome == "CRASH") exitCode = -10; | 953 if (outcome == "CRASH") exitCode = -10; |
| 871 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; | 954 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; |
| 872 new TestOutput.fromCase(_currentTest, exitCode, false, | 955 new CommandOutput.fromCase(_currentTest, |
| 873 (outcome == "TIMEOUT"), | 956 _command, |
| 874 _testStdout, _testStderr, | 957 exitCode, |
| 875 new Date.now().difference(_startTime)); | 958 false, |
| 959 (outcome == "TIMEOUT"), |
| 960 _testStdout, |
| 961 _testStderr, |
| 962 new Date.now().difference(_startTime)); |
| 876 var test = _currentTest; | 963 var test = _currentTest; |
| 877 _currentTest = null; | 964 _currentTest = null; |
| 878 test.completed(); | 965 test.completed(); |
| 879 } | 966 } |
| 880 | 967 |
| 881 void _stderrDone() { | 968 void _stderrDone() { |
| 882 _stderrDrained = true; | 969 _stderrDrained = true; |
| 883 // Move on when both stdout and stderr has been drained. | 970 // Move on when both stdout and stderr has been drained. |
| 884 if (_stdoutDrained) _reportResult(); | 971 if (_stdoutDrained) _reportResult(); |
| 885 } | 972 } |
| (...skipping 444 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1330 // the developer doesn't waste his or her time trying to fix a bunch of | 1417 // the developer doesn't waste his or her time trying to fix a bunch of |
| 1331 // tests that appear to be broken but were actually just flakes that | 1418 // tests that appear to be broken but were actually just flakes that |
| 1332 // didn't get retried because there had already been one failure. | 1419 // didn't get retried because there had already been one failure. |
| 1333 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; | 1420 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 1334 new RunningProcess(test, allowRetry, this).start(); | 1421 new RunningProcess(test, allowRetry, this).start(); |
| 1335 } | 1422 } |
| 1336 _numProcesses++; | 1423 _numProcesses++; |
| 1337 } | 1424 } |
| 1338 } | 1425 } |
| 1339 } | 1426 } |
| OLD | NEW |