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 14 matching lines...) Expand all Loading... | |
| 25 | 25 |
| 26 /** Command line arguments to the executable. */ | 26 /** Command line arguments to the executable. */ |
| 27 List<String> arguments; | 27 List<String> arguments; |
| 28 | 28 |
| 29 /** The actual command line that will be executed. */ | 29 /** The actual command line that will be executed. */ |
| 30 String commandLine; | 30 String commandLine; |
| 31 | 31 |
| 32 Command(this.executable, this.arguments) { | 32 Command(this.executable, this.arguments) { |
| 33 commandLine = "$executable ${Strings.join(arguments, ' ')}"; | 33 commandLine = "$executable ${Strings.join(arguments, ' ')}"; |
| 34 } | 34 } |
| 35 | |
| 36 String toString() => commandLine; | |
| 35 } | 37 } |
| 36 | 38 |
| 37 /** | 39 /** |
| 38 * TestCase contains all the information needed to run a test and evaluate | 40 * TestCase contains all the information needed to run a test and evaluate |
| 39 * its output. Running a test involves starting a separate process, with | 41 * its output. Running a test involves starting a separate process, with |
| 40 * the executable and arguments given by the TestCase, and recording its | 42 * the executable and arguments given by the TestCase, and recording its |
| 41 * stdout and stderr output streams, and its exit code. TestCase only | 43 * stdout and stderr output streams, and its exit code. TestCase only |
| 42 * contains static information about the test; actually running the test is | 44 * contains static information about the test; actually running the test is |
| 43 * performed by [ProcessQueue] using a [RunningProcess] object. | 45 * performed by [ProcessQueue] using a [RunningProcess] object. |
| 44 * | 46 * |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 58 * multiple sources that are run in isolation. | 60 * multiple sources that are run in isolation. |
| 59 */ | 61 */ |
| 60 final List<Command> commands; | 62 final List<Command> commands; |
| 61 | 63 |
| 62 Map configuration; | 64 Map configuration; |
| 63 String displayName; | 65 String displayName; |
| 64 TestOutput output; | 66 TestOutput output; |
| 65 bool isNegative; | 67 bool isNegative; |
| 66 Set<String> expectedOutcomes; | 68 Set<String> expectedOutcomes; |
| 67 Function completedHandler; | 69 Function completedHandler; |
| 70 TestInformation info; | |
| 68 | 71 |
| 69 TestCase(this.displayName, | 72 TestCase(this.displayName, |
| 70 this.commands, | 73 this.commands, |
| 71 this.configuration, | 74 this.configuration, |
| 72 this.completedHandler, | 75 this.completedHandler, |
| 73 this.expectedOutcomes, | 76 this.expectedOutcomes, |
| 74 [this.isNegative = false]) { | 77 [this.isNegative = false, |
| 78 this.info = null]) { | |
| 75 if (!isNegative) { | 79 if (!isNegative) { |
| 76 this.isNegative = displayName.contains("NegativeTest"); | 80 this.isNegative = displayName.contains("NegativeTest"); |
| 77 } | 81 } |
| 78 | 82 |
| 79 // Special command handling. If a special command is specified | 83 // Special command handling. If a special command is specified |
| 80 // we have to completely rewrite the command that we are using. | 84 // we have to completely rewrite the command that we are using. |
| 81 // We generate a new command-line that is the special command | 85 // We generate a new command-line that is the special command |
| 82 // where we replace '@' with the original command. | 86 // where we replace '@' with the original command. |
| 83 var specialCommand = configuration['special-command']; | 87 var specialCommand = configuration['special-command']; |
| 84 if (!specialCommand.isEmpty()) { | 88 if (!specialCommand.isEmpty()) { |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 158 _lastArguments.getRange(1, _lastArguments.length - 1); | 162 _lastArguments.getRange(1, _lastArguments.length - 1); |
| 159 } | 163 } |
| 160 | 164 |
| 161 | 165 |
| 162 /** | 166 /** |
| 163 * TestOutput records the output of a completed test: the process's exit code, | 167 * TestOutput records the output of a completed test: the process's exit code, |
| 164 * the standard output and standard error, whether the process timed out, and | 168 * the standard output and standard error, whether the process timed out, and |
| 165 * the time the process took to run. It also contains a pointer to the | 169 * the time the process took to run. It also contains a pointer to the |
| 166 * [TestCase] this is the output of. | 170 * [TestCase] this is the output of. |
| 167 */ | 171 */ |
| 168 class TestOutput { | 172 interface TestOutput default TestOutputImpl { |
|
zundel
2012/02/29 08:11:40
I didn't come up with much better than what was he
| |
| 173 TestOutput.fromCase(TestCase testCase, int exitCode, bool timedOut, | |
| 174 List<String> stdout, List<String> stderr, Duration time); | |
| 175 | |
| 176 String get result(); | |
| 177 | |
| 178 bool get unexpectedOutput(); | |
| 179 | |
| 180 bool get hasCrashed(); | |
| 181 | |
| 182 bool get hasTimedOut(); | |
| 183 | |
| 184 bool get didFail(); | |
| 185 } | |
| 186 | |
| 187 class TestOutputImpl implements TestOutput { | |
| 169 TestCase testCase; | 188 TestCase testCase; |
| 170 int exitCode; | 189 int exitCode; |
| 171 bool timedOut; | 190 bool timedOut; |
| 172 bool failed = false; | 191 bool failed = false; |
| 173 List<String> stdout; | 192 List<String> stdout; |
| 174 List<String> stderr; | 193 List<String> stderr; |
| 175 Duration time; | 194 Duration time; |
| 195 | |
| 176 /** | 196 /** |
| 177 * Set to true if we encounter a condition in the output that indicates we | 197 * Set to true if we encounter a condition in the output that indicates we |
| 178 * need to rerun this test. | 198 * need to rerun this test. |
| 179 */ | 199 */ |
| 180 bool requestRetry; | 200 bool requestRetry; |
| 181 | 201 |
| 182 TestOutput(this.testCase, this.exitCode, this.timedOut, this.stdout, | 202 // Don't call this constructor, call TestOutput.fromCase() to |
| 203 // get anew TestOutput instance. | |
| 204 TestOutputImpl(this.testCase, this.exitCode, this.timedOut, this.stdout, | |
| 183 this.stderr, this.time) { | 205 this.stderr, this.time) { |
| 184 testCase.output = this; | 206 testCase.output = this; |
| 185 requestRetry = false; | 207 requestRetry = false; |
| 186 } | 208 } |
| 187 | 209 |
| 210 factory TestOutputImpl.fromCase (testCase, exitCode, timedOut, stdout, stderr, | |
| 211 time) { | |
| 212 if (testCase is BrowserTestCase) { | |
| 213 return new BrowserTestOutputImpl(testCase, exitCode, timedOut, | |
| 214 stdout, stderr, time); | |
| 215 } else if (testCase.configuration['component'] == 'dartc') { | |
| 216 return new AnalysisTestOutputImpl(testCase, exitCode, timedOut, | |
| 217 stdout, stderr, time); | |
| 218 } | |
| 219 return new TestOutputImpl(testCase, exitCode, timedOut, | |
| 220 stdout, stderr, time); | |
| 221 } | |
| 222 | |
| 188 String get result() => | 223 String get result() => |
| 189 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); | 224 hasCrashed ? CRASH : (hasTimedOut ? TIMEOUT : (hasFailed ? FAIL : PASS)); |
| 190 | 225 |
| 191 bool get unexpectedOutput() => !testCase.expectedOutcomes.contains(result); | 226 bool get unexpectedOutput() => !testCase.expectedOutcomes.contains(result); |
| 192 | 227 |
| 193 bool get hasCrashed() { | 228 bool get hasCrashed() { |
| 194 if (new Platform().operatingSystem() == 'windows') { | 229 if (new Platform().operatingSystem() == 'windows') { |
| 195 // The VM uses std::abort to terminate on asserts. | 230 // The VM uses std::abort to terminate on asserts. |
| 196 // std::abort terminates with exit code 3 on Windows. | 231 // std::abort terminates with exit code 3 on Windows. |
| 197 if (exitCode == 3) { | 232 if (exitCode == 3) { |
| 198 return !timedOut; | 233 return !timedOut; |
| 199 } | 234 } |
| 200 return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0)); | 235 return (!timedOut && (exitCode < 0) && ((0x3FFFFF00 & exitCode) == 0)); |
| 201 } | 236 } |
| 202 // The Java dartc runner exits with code 253 in case of unhandled | 237 // The Java dartc runner exits with code 253 in case of unhandled |
| 203 // exceptions. | 238 // exceptions. |
| 204 return (!timedOut && ((exitCode < 0) || (exitCode == 253))); | 239 return (!timedOut && ((exitCode < 0) || (exitCode == 253))); |
| 205 } | 240 } |
| 206 | 241 |
| 207 bool get hasTimedOut() => timedOut; | 242 bool get hasTimedOut() => timedOut; |
| 208 | 243 |
| 209 bool get didFail() { | 244 bool get didFail() { |
| 210 if (testCase is !BrowserTestCase) return (exitCode != 0 && !hasCrashed); | 245 return (exitCode != 0 && !hasCrashed); |
| 246 } | |
| 247 | |
| 248 // Reverse result of a negative test. | |
| 249 bool get hasFailed() => (testCase.isNegative ? !didFail : didFail); | |
| 250 } | |
| 211 | 251 |
| 252 class BrowserTestOutputImpl extends TestOutputImpl { | |
| 253 BrowserTestOutputImpl(testCase, exitCode, timedOut, stdout, stderr, time) : | |
| 254 super(testCase, exitCode, timedOut, stdout, stderr, time); | |
| 255 | |
| 256 bool get didFail() { | |
| 212 // Browser case: | 257 // Browser case: |
| 213 // If the browser test failed, it may have been because DumpRenderTree | 258 // If the browser test failed, it may have been because DumpRenderTree |
| 214 // and the virtual framebuffer X server didn't hook up, or DRT crashed with | 259 // and the virtual framebuffer X server didn't hook up, or DRT crashed with |
| 215 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, | 260 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, |
| 216 // so we have to do this check first. | 261 // so we have to do this check first. |
| 217 for (String line in stderr) { | 262 for (String line in stderr) { |
| 218 if (line.contains('Gtk-WARNING **: cannot open display: :99') || | 263 if (line.contains('Gtk-WARNING **: cannot open display: :99') || |
| 219 line.contains('Failed to run command. return code=1')) { | 264 line.contains('Failed to run command. return code=1')) { |
| 220 // If we get the X server error, or DRT crashes with a core dump, retry | 265 // If we get the X server error, or DRT crashes with a core dump, retry |
| 221 // the test. | 266 // the test. |
| 222 if (testCase.dynamic.numRetries > 0) { | 267 if (testCase.dynamic.numRetries > 0) { |
| 223 requestRetry = true; | 268 requestRetry = true; |
| 224 } | 269 } |
| 225 return true; | 270 return true; |
| 226 } | 271 } |
| 227 } | 272 } |
| 228 | 273 |
| 229 // Browser tests fail unless stdout contains | 274 // Browser tests fail unless stdout contains |
| 230 // 'Content-Type: text/plain\nPASS'. | 275 // 'Content-Type: text/plain\nPASS'. |
| 231 String previous_line = ''; | 276 String previous_line = ''; |
| 232 for (String line in stdout) { | 277 for (String line in stdout) { |
| 233 if (line == 'PASS' && previous_line == 'Content-Type: text/plain') { | 278 if (line == 'PASS' && previous_line == 'Content-Type: text/plain') { |
| 234 return (exitCode != 0 && !hasCrashed); | 279 return (exitCode != 0 && !hasCrashed); |
| 235 } | 280 } |
| 236 previous_line = line; | 281 previous_line = line; |
| 237 } | 282 } |
| 238 | |
| 239 return true; | 283 return true; |
| 240 } | 284 } |
| 285 } | |
| 241 | 286 |
| 242 // Reverse result of a negative test. | 287 // The static analyzer does not actaully execute code, so |
| 243 bool get hasFailed() => (testCase.isNegative ? !didFail : didFail); | 288 // the criteria for success now depend on the text sent |
| 289 // to stderr. | |
| 290 class AnalysisTestOutputImpl extends TestOutputImpl { | |
| 291 AnalysisTestOutputImpl(testCase, exitCode, timedOut, stdout, stderr, time) : | |
| 292 super(testCase, exitCode, timedOut, stdout, stderr, time) { | |
| 293 } | |
| 294 | |
| 295 bool get didFail() { | |
| 296 if (hasCrashed) return false; | |
| 297 | |
| 298 List<String> errors = []; | |
| 299 List<String> staticWarnings = []; | |
| 300 | |
| 301 // Read the returned list of errors and stuff them away. | |
| 302 for (String line in stderr) { | |
| 303 if (line.length == 0) continue; | |
| 304 List<String> fields = splitMachineError(line); | |
| 305 switch(fields[0]) { | |
| 306 case 'ERROR': | |
| 307 errors.add(fields); | |
| 308 break; | |
| 309 case 'WARNING': | |
| 310 // We only care about testing Static type warnings | |
| 311 // ignore all others | |
| 312 if (fields[1] == 'STATIC_TYPE') { | |
| 313 staticWarnings.add(fields); | |
| 314 } | |
| 315 break; | |
| 316 default: | |
| 317 // Skip error output that doesn't match the machine format | |
| 318 } | |
| 319 } | |
| 320 if (testCase.info != null | |
| 321 && testCase.info.optionsFromFile['isMultitest']) { | |
| 322 return _didMultitestFail(errors, staticWarnings); | |
| 323 } | |
| 324 return _didStandardTestFail(errors, staticWarnings); | |
| 325 } | |
| 326 | |
| 327 bool _didMultitestFail(List errors, List staticWarnings) { | |
| 328 String outcome = testCase.info.multitestOutcome; | |
| 329 if ((outcome == '' || outcome == 'compile-time error') && errors.length > 0) { | |
| 330 return true; | |
| 331 } else if (outcome == 'static type error' && staticWarnings.length > 0) { | |
| 332 return true; | |
| 333 } | |
| 334 return false; | |
| 335 } | |
| 336 | |
| 337 bool _didStandardTestFail(List errors, List staticWarnings) { | |
| 338 bool hasFatalTypeErrors = false; | |
| 339 int numStaticTypeAnnotations = 0; | |
| 340 int numCompileTimeAnnotations = 0; | |
| 341 if (testCase.info != null) { | |
| 342 var optionsFromFile = testCase.info.optionsFromFile; | |
| 343 hasFatalTypeErrors = optionsFromFile['hasFatalTypeErrors']; | |
| 344 for (Command c in testCase.commands) { | |
| 345 for (String arg in c.arguments) { | |
| 346 if (arg == '--fatal-type-errors') { | |
| 347 hasFatalTypeErrors = true; | |
| 348 break; | |
| 349 } | |
| 350 } | |
| 351 } | |
| 352 numStaticTypeAnnotations = optionsFromFile['numStaticTypeAnnotations']; | |
| 353 numCompileTimeAnnotations = optionsFromFile['numCompileTimeAnnotations']; | |
| 354 } | |
| 355 | |
| 356 // TODO(zundel): These assertions are catching some sort of issue | |
| 357 // where the output between two test cases is getting crossed. | |
| 358 if (errors.length == 0) { | |
| 359 if (!hasFatalTypeErrors) { | |
| 360 Expect.isTrue(exitCode == 0, | |
| 361 "Expected error: exitCode:${exitCode} command[0]:${testCase.commands [0]}"); | |
| 362 } | |
| 363 } else { | |
| 364 Expect.isTrue(exitCode != 0, | |
| 365 "Unexpected error: exitCode:${exitCode} command[0]:${testCase.commands[0 ]} errors[0]:${errors[0]}"); | |
| 366 } | |
| 367 | |
| 368 if (numCompileTimeAnnotations > 0 | |
| 369 && numCompileTimeAnnotations < errors.length) { | |
| 370 // Expected compile-time errors were not returned. The test did not 'fail ' in the way | |
| 371 // intended so don't return failed. | |
| 372 // TODO(zundel): give a good diagnostic message here | |
| 373 return false; | |
| 374 } | |
| 375 | |
| 376 if (numStaticTypeAnnotations > 0 || hasFatalTypeErrors) { | |
| 377 // TODO(zundel): match up the annotation line numbers | |
| 378 // with the reported error line numbers | |
| 379 if (staticWarnings.length < numStaticTypeAnnotations) { | |
| 380 // TODO(zundel): How to give a good diagnostic message here? | |
| 381 return true; | |
| 382 } | |
| 383 return false; | |
| 384 } else if (errors.length != 0) { | |
| 385 return true; | |
| 386 } | |
| 387 return false; | |
| 388 } | |
| 389 | |
| 390 // Parse a line delimited by the | character using \ as an escape charager | |
| 391 // like: FOO|BAR|FOO\|BAR|FOO\\BAZ as 4 fields: FOO BAR FOO|BAR FOO\BAZ | |
| 392 List<String> splitMachineError(String line) { | |
| 393 StringBuffer field = new StringBuffer(); | |
| 394 List<String> result = []; | |
| 395 bool escaped = false; | |
| 396 for (var i = 0 ; i < line.length; i++) { | |
| 397 var c = line[i]; | |
| 398 if (!escaped && c == '\\') { | |
| 399 escaped = true; | |
| 400 continue; | |
| 401 } | |
| 402 escaped = false; | |
| 403 if (c == '|') { | |
| 404 result.add(field.toString()); | |
| 405 field.clear(); | |
| 406 continue; | |
| 407 } | |
| 408 field.add(c); | |
| 409 } | |
| 410 result.add(field.toString()); | |
| 411 return result; | |
| 412 } | |
| 244 } | 413 } |
| 245 | 414 |
| 246 /** | 415 /** |
| 247 * A RunningProcess actually runs a test, getting the command lines from | 416 * A RunningProcess actually runs a test, getting the command lines from |
| 248 * its [TestCase], starting the test process (and first, a compilation | 417 * its [TestCase], starting the test process (and first, a compilation |
| 249 * process if the TestCase is a [BrowserTestCase]), creating a timeout | 418 * process if the TestCase is a [BrowserTestCase]), creating a timeout |
| 250 * timer, and recording the results in a new [TestOutput] object, which it | 419 * timer, and recording the results in a new [TestOutput] object, which it |
| 251 * attaches to the TestCase. The lifetime of the RunningProcess is limited | 420 * attaches to the TestCase. The lifetime of the RunningProcess is limited |
| 252 * to the time it takes to start the process, run the process, and record | 421 * to the time it takes to start the process, run the process, and record |
| 253 * the result; there are no pointers to it, so it should be available to | 422 * the result; there are no pointers to it, so it should be available to |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 270 | 439 |
| 271 RunningProcess(TestCase this.testCase, | 440 RunningProcess(TestCase this.testCase, |
| 272 [this.allowRetries, this.processQueue]); | 441 [this.allowRetries, this.processQueue]); |
| 273 | 442 |
| 274 /** | 443 /** |
| 275 * Called when all commands are executed. [exitCode] is 0 if all command | 444 * Called when all commands are executed. [exitCode] is 0 if all command |
| 276 * succeded, otherwise it will have the exit code of the first failing | 445 * succeded, otherwise it will have the exit code of the first failing |
| 277 * command. | 446 * command. |
| 278 */ | 447 */ |
| 279 void testComplete(int exitCode) { | 448 void testComplete(int exitCode) { |
| 280 new TestOutput(testCase, exitCode, timedOut, stdout, | 449 new TestOutput.fromCase(testCase, exitCode, timedOut, stdout, |
| 281 stderr, new Date.now().difference(startTime)); | 450 stderr, new Date.now().difference(startTime)); |
| 282 timeoutTimer.cancel(); | 451 timeoutTimer.cancel(); |
| 283 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { | 452 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { |
| 284 print(testCase.displayName); | 453 print(testCase.displayName); |
| 285 for (var line in testCase.output.stderr) print(line); | 454 for (var line in testCase.output.stderr) print(line); |
| 286 for (var line in testCase.output.stdout) print(line); | 455 for (var line in testCase.output.stdout) print(line); |
| 287 } | 456 } |
| 288 if (allowRetries != null && allowRetries | 457 if (allowRetries != null && allowRetries |
| 289 && testCase.configuration['component'] == 'webdriver' && | 458 && testCase.configuration['component'] == 'webdriver' && |
| 290 testCase.output.unexpectedOutput && testCase.numRetries > 0) { | 459 testCase.output.unexpectedOutput && testCase.numRetries > 0) { |
| 291 // Selenium tests can be flaky. Try rerunning. | 460 // Selenium tests can be flaky. Try rerunning. |
| 292 testCase.output.requestRetry = true; | 461 testCase.output.requestRetry = true; |
| 293 } | 462 } |
| 294 if (testCase.output.requestRetry) { | 463 if (testCase.output.requestRetry) { |
| 295 testCase.output.requestRetry = false; | 464 testCase.output.requestRetry = false; |
| 296 this.timedOut = false; | 465 this.timedOut = false; |
| 297 testCase.dynamic.numRetries--; | 466 testCase.dynamic.numRetries--; |
| 298 print("Potential flake. " + | 467 print("Potential flake. Re-running ${testCase.displayName} " + |
| 299 "Re-running ${testCase.displayName} " + | |
| 300 "(${testCase.dynamic.numRetries} attempt(s) remains)"); | 468 "(${testCase.dynamic.numRetries} attempt(s) remains)"); |
| 301 this.start(); | 469 this.start(); |
| 302 } else { | 470 } else { |
| 303 testCase.completed(); | 471 testCase.completed(); |
| 304 } | 472 } |
| 305 } | 473 } |
| 306 | 474 |
| 307 /** | 475 /** |
| 308 * Process exit handler called at the end of every command. It internally | 476 * Process exit handler called at the end of every command. It internally |
| 309 * treats all but the last command as compilation steps. The last command is | 477 * treats all but the last command as compilation steps. The last command is |
| (...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 445 bool shutdownMillisecs = 30000; | 613 bool shutdownMillisecs = 30000; |
| 446 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs); | 614 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs); |
| 447 } else { | 615 } else { |
| 448 _process.kill(); | 616 _process.kill(); |
| 449 } | 617 } |
| 450 } | 618 } |
| 451 } | 619 } |
| 452 | 620 |
| 453 void doStartTest(TestCase testCase) { | 621 void doStartTest(TestCase testCase) { |
| 454 _startTime = new Date.now(); | 622 _startTime = new Date.now(); |
| 455 _testStdout = new List<String>(); | 623 _testStdout = []; |
| 456 _testStderr = new List<String>(); | 624 _testStderr = []; |
| 457 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); | 625 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); |
| 458 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); | 626 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); |
| 459 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000); | 627 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000); |
| 460 var line = _createArgumentsLine(testCase.batchTestArguments); | 628 var line = _createArgumentsLine(testCase.batchTestArguments); |
| 461 _process.stdin.write(line.charCodes()); | 629 _process.stdin.write(line.charCodes()); |
| 462 } | 630 } |
| 463 | 631 |
| 464 String _createArgumentsLine(List<String> arguments) { | 632 String _createArgumentsLine(List<String> arguments) { |
| 465 return Strings.join(arguments, ' ') + '\n'; | 633 return Strings.join(arguments, ' ') + '\n'; |
| 466 } | 634 } |
| 467 | 635 |
| 468 int _reportResult(String output) { | 636 int _reportResult(String output) { |
| 469 var test = _currentTest; | 637 var test = _currentTest; |
| 470 _currentTest = null; | 638 _currentTest = null; |
| 471 | 639 |
| 472 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' | 640 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' |
| 473 var outcome = output.split(" ")[2]; | 641 var outcome = output.split(" ")[2]; |
| 474 var exitCode = 0; | 642 var exitCode = 0; |
| 475 if (outcome == "CRASH") exitCode = -10; | 643 if (outcome == "CRASH") exitCode = -10; |
| 476 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; | 644 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; |
| 477 new TestOutput(test, exitCode, outcome == "TIMEOUT", _testStdout, | 645 new TestOutput.fromCase(test, exitCode, outcome == "TIMEOUT", _testStdout, |
| 478 _testStderr, new Date.now().difference(_startTime)); | 646 _testStderr, new Date.now().difference(_startTime)); |
| 647 _testStdout = []; | |
| 648 _testStderr = []; | |
| 479 test.completed(); | 649 test.completed(); |
| 480 } | 650 } |
| 481 | 651 |
| 482 Function _readOutput(StringInputStream stream, List<String> buffer) { | 652 Function _readOutput(StringInputStream stream, List<String> buffer) { |
| 483 return () { | 653 return () { |
| 484 var status; | 654 var status; |
| 485 var line = stream.readLine(); | 655 var line = stream.readLine(); |
| 486 // Drain the input stream to get the error output. | 656 // Drain the input stream to get the error output. |
| 487 while (line != null) { | 657 while (line != null) { |
| 488 if (line.startsWith('>>> TEST')) { | 658 if (line.startsWith('>>> TEST')) { |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 501 // For crashing processes, let the exit handler deal with it. | 671 // For crashing processes, let the exit handler deal with it. |
| 502 if (!status.contains("CRASH")) { | 672 if (!status.contains("CRASH")) { |
| 503 _reportResult(status); | 673 _reportResult(status); |
| 504 } | 674 } |
| 505 } | 675 } |
| 506 }; | 676 }; |
| 507 } | 677 } |
| 508 | 678 |
| 509 void _exitHandler(exitCode) { | 679 void _exitHandler(exitCode) { |
| 510 if (_timer != null) _timer.cancel(); | 680 if (_timer != null) _timer.cancel(); |
| 681 _reportResult(">>> TEST CRASH"); | |
|
zundel
2012/02/29 08:11:40
I moved this because _startProcess() clears the _t
| |
| 511 _process.close(); | 682 _process.close(); |
| 512 _startProcess(() { | 683 _startProcess(() {}); |
| 513 _reportResult(">>> TEST CRASH"); | |
| 514 }); | |
| 515 } | 684 } |
| 516 | 685 |
| 517 void _timeoutHandler(ignore) { | 686 void _timeoutHandler(ignore) { |
| 518 _process.exitHandler = (exitCode) { | 687 _process.exitHandler = (exitCode) { |
| 688 _reportResult(">>> TEST TIMEOUT"); | |
| 519 _process.close(); | 689 _process.close(); |
| 520 _startProcess(() { | 690 _startProcess(() {}); |
| 521 _reportResult(">>> TEST TIMEOUT"); | |
| 522 }); | |
| 523 }; | 691 }; |
| 524 _process.kill(); | 692 _process.kill(); |
| 525 } | 693 } |
| 526 | 694 |
| 527 void _startProcess(then) { | 695 void _startProcess(then) { |
| 528 _process = new Process.start(_executable, _batchArguments); | 696 _process = new Process.start(_executable, _batchArguments); |
| 529 _stdoutStream = new StringInputStream(_process.stdout); | 697 _stdoutStream = new StringInputStream(_process.stdout); |
| 530 _stderrStream = new StringInputStream(_process.stderr); | 698 _stderrStream = new StringInputStream(_process.stderr); |
| 531 _testStdout = new List<String>(); | 699 _testStdout = new List<String>(); |
| 532 _testStderr = new List<String>(); | 700 _testStderr = new List<String>(); |
| (...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 775 // the developer doesn't waste his or her time trying to fix a bunch of | 943 // the developer doesn't waste his or her time trying to fix a bunch of |
| 776 // tests that appear to be broken but were actually just flakes that | 944 // tests that appear to be broken but were actually just flakes that |
| 777 // didn't get retried because there had already been one failure. | 945 // didn't get retried because there had already been one failure. |
| 778 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; | 946 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 779 new RunningProcess(test, allowRetry, this).start(); | 947 new RunningProcess(test, allowRetry, this).start(); |
| 780 } | 948 } |
| 781 _numProcesses++; | 949 _numProcesses++; |
| 782 } | 950 } |
| 783 } | 951 } |
| 784 } | 952 } |
| OLD | NEW |