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