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