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 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 92 | 92 |
| 93 int get timeout() => configuration['timeout']; | 93 int get timeout() => configuration['timeout']; |
| 94 | 94 |
| 95 String get configurationString() { | 95 String get configurationString() { |
| 96 final component = configuration['component']; | 96 final component = configuration['component']; |
| 97 final mode = configuration['mode']; | 97 final mode = configuration['mode']; |
| 98 final arch = configuration['arch']; | 98 final arch = configuration['arch']; |
| 99 return "$component ${mode}_$arch"; | 99 return "$component ${mode}_$arch"; |
| 100 } | 100 } |
| 101 | 101 |
| 102 List<String> get batchRunnerArguments() => ['-batch']; | |
| 103 List<String> get batchTestArguments() => arguments; | |
| 104 | |
| 102 void completed() { completedHandler(this); } | 105 void completed() { completedHandler(this); } |
| 103 } | 106 } |
| 104 | 107 |
| 105 | 108 |
| 106 /** | 109 /** |
| 107 * BrowserTestCase has an extra compilation command that is run in a separate | 110 * BrowserTestCase has an extra compilation command that is run in a separate |
| 108 * process, before the regular test is run as in the base class [TestCase]. | 111 * process, before the regular test is run as in the base class [TestCase]. |
| 109 * If the compilation command fails, then the rest of the test is not run. | 112 * If the compilation command fails, then the rest of the test is not run. |
| 110 */ | 113 */ |
| 111 class BrowserTestCase extends TestCase { | 114 class BrowserTestCase extends TestCase { |
| (...skipping 27 matching lines...) Expand all Loading... | |
| 139 expectedOutcomes, | 142 expectedOutcomes, |
| 140 isNegative) { | 143 isNegative) { |
| 141 if (compilerPath != null) { | 144 if (compilerPath != null) { |
| 142 commandLine = 'execution command: $commandLine'; | 145 commandLine = 'execution command: $commandLine'; |
| 143 String compilationCommand = | 146 String compilationCommand = |
| 144 '$compilerPath ${Strings.join(compilerArguments, " ")}'; | 147 '$compilerPath ${Strings.join(compilerArguments, " ")}'; |
| 145 commandLine = 'compilation command: $compilationCommand\n$commandLine'; | 148 commandLine = 'compilation command: $compilationCommand\n$commandLine'; |
| 146 } | 149 } |
| 147 numRetries = 2; // Allow two retries to compensate for flaky browser tests. | 150 numRetries = 2; // Allow two retries to compensate for flaky browser tests. |
| 148 } | 151 } |
| 152 | |
| 153 List<String> get batchRunnerArguments() => [arguments[0], '--batch']; | |
| 154 List<String> get batchTestArguments() => | |
| 155 arguments.getRange(1, arguments.length - 1); | |
| 149 } | 156 } |
| 150 | 157 |
| 151 | 158 |
| 152 /** | 159 /** |
| 153 * TestOutput records the output of a completed test: the process's exit code, | 160 * TestOutput records the output of a completed test: the process's exit code, |
| 154 * the standard output and standard error, whether the process timed out, and | 161 * the standard output and standard error, whether the process timed out, and |
| 155 * the time the process took to run. It also contains a pointer to the | 162 * the time the process took to run. It also contains a pointer to the |
| 156 * [TestCase] this is the output of. | 163 * [TestCase] this is the output of. |
| 157 */ | 164 */ |
| 158 class TestOutput { | 165 class TestOutput { |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 201 | 208 |
| 202 // Browser case: | 209 // Browser case: |
| 203 // If the browser test failed, it may have been because DumpRenderTree | 210 // If the browser test failed, it may have been because DumpRenderTree |
| 204 // and the virtual framebuffer X server didn't hook up, or DRT crashed with | 211 // and the virtual framebuffer X server didn't hook up, or DRT crashed with |
| 205 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, | 212 // a core dump. Sometimes DRT crashes after it has set the stdout to PASS, |
| 206 // so we have to do this check first. | 213 // so we have to do this check first. |
| 207 for (String line in stderr) { | 214 for (String line in stderr) { |
| 208 if (line.contains('Gtk-WARNING **: cannot open display: :99') || | 215 if (line.contains('Gtk-WARNING **: cannot open display: :99') || |
| 209 line.contains('Failed to run command. return code=1')) { | 216 line.contains('Failed to run command. return code=1')) { |
| 210 // If we get the X server error, or DRT crashes with a core dump, retry | 217 // If we get the X server error, or DRT crashes with a core dump, retry |
| 211 // the test. | 218 // the test. |
| 212 requestRetry = true; | 219 requestRetry = true; |
| 213 return true; | 220 return true; |
| 214 } | 221 } |
| 215 } | 222 } |
| 216 | 223 |
| 217 // Browser tests fail unless stdout contains | 224 // Browser tests fail unless stdout contains |
| 218 // 'Content-Type: text/plain\nPASS'. | 225 // 'Content-Type: text/plain\nPASS'. |
| 219 String previous_line = ''; | 226 String previous_line = ''; |
| 220 for (String line in stdout) { | 227 for (String line in stdout) { |
| 221 if (line == 'PASS' && previous_line == 'Content-Type: text/plain') { | 228 if (line == 'PASS' && previous_line == 'Content-Type: text/plain') { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 235 * A RunningProcess actually runs a test, getting the command lines from | 242 * A RunningProcess actually runs a test, getting the command lines from |
| 236 * its [TestCase], starting the test process (and first, a compilation | 243 * its [TestCase], starting the test process (and first, a compilation |
| 237 * process if the TestCase is a [BrowserTestCase]), creating a timeout | 244 * process if the TestCase is a [BrowserTestCase]), creating a timeout |
| 238 * timer, and recording the results in a new [TestOutput] object, which it | 245 * timer, and recording the results in a new [TestOutput] object, which it |
| 239 * attaches to the TestCase. The lifetime of the RunningProcess is limited | 246 * attaches to the TestCase. The lifetime of the RunningProcess is limited |
| 240 * to the time it takes to start the process, run the process, and record | 247 * to the time it takes to start the process, run the process, and record |
| 241 * the result; there are no pointers to it, so it should be available to | 248 * the result; there are no pointers to it, so it should be available to |
| 242 * be garbage collected as soon as it is done. | 249 * be garbage collected as soon as it is done. |
| 243 */ | 250 */ |
| 244 class RunningProcess { | 251 class RunningProcess { |
| 252 ProcessQueue processQueue; | |
| 245 Process process; | 253 Process process; |
| 246 TestCase testCase; | 254 TestCase testCase; |
| 247 bool timedOut = false; | 255 bool timedOut = false; |
| 248 Date startTime; | 256 Date startTime; |
| 249 Timer timeoutTimer; | 257 Timer timeoutTimer; |
| 250 List<String> stdout; | 258 List<String> stdout; |
| 251 List<String> stderr; | 259 List<String> stderr; |
| 252 List<Function> handlers; | 260 List<Function> handlers; |
| 253 bool allowRetries = false; | 261 bool allowRetries = false; |
| 254 | 262 |
| 255 RunningProcess(TestCase this.testCase, [this.allowRetries]); | 263 RunningProcess(TestCase this.testCase, |
| 264 [this.allowRetries, this.processQueue]); | |
| 256 | 265 |
| 257 void exitHandler(int exitCode) { | 266 void exitHandler(int exitCode) { |
| 258 new TestOutput(testCase, exitCode, timedOut, stdout, | 267 new TestOutput(testCase, exitCode, timedOut, stdout, |
| 259 stderr, new Date.now().difference(startTime)); | 268 stderr, new Date.now().difference(startTime)); |
| 260 process.close(); | 269 process.close(); |
| 261 timeoutTimer.cancel(); | 270 timeoutTimer.cancel(); |
| 262 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { | 271 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { |
| 263 print(testCase.displayName); | 272 print(testCase.displayName); |
| 264 for (var line in testCase.output.stderr) print(line); | 273 for (var line in testCase.output.stderr) print(line); |
| 265 for (var line in testCase.output.stdout) print(line); | 274 for (var line in testCase.output.stdout) print(line); |
| 266 } | 275 } |
| 267 if (allowRetries != null && allowRetries | 276 if (allowRetries != null && allowRetries |
| 268 && testCase.configuration['component'] == 'webdriver' && | 277 && testCase.configuration['component'] == 'webdriver' && |
| 269 testCase.output.unexpectedOutput && testCase.numRetries > 0) { | 278 testCase.output.unexpectedOutput && testCase.numRetries > 0) { |
| 270 // Selenium tests can be flaky. Try rerunning. | 279 // Selenium tests can be flaky. Try rerunning. |
| 271 testCase.output.requestRetry = true; | 280 testCase.output.requestRetry = true; |
| 272 } | 281 } |
| 273 if (testCase.output.requestRetry) { | 282 if (testCase.output.requestRetry) { |
| 274 testCase.output.requestRetry = false; | 283 testCase.output.requestRetry = false; |
| 275 this.timedOut = false; | 284 this.timedOut = false; |
| 276 testCase.dynamic.numRetries--; | 285 testCase.dynamic.numRetries--; |
| 277 print("Potential flake. Re-running " + testCase.displayName); | 286 print("Potential flake. Re-running " + testCase.displayName); |
| 278 this.start(); | 287 this.start(); |
| 279 } else { | 288 } else { |
| 280 testCase.completed(); | 289 testCase.completed(); |
| 281 } | 290 } |
| 282 } | 291 } |
| 283 | 292 |
| 284 void compilerExitHandler(int exitCode) { | 293 void compilerExitHandler(int exitCode) { |
| 285 if (exitCode != 0) { | 294 if (exitCode != 0) { |
| 286 stderr.add('test.dart: Compilation step failed (exit code $exitCode)\n'); | 295 stderr.add('test.dart: Compilation step failed (exit code $exitCode)\n'); |
| 287 exitHandler(exitCode); | 296 exitHandler(exitCode); |
| 288 } else { | 297 } else { |
| 289 process.close(); | 298 process.close(); |
| 290 stderr.add('test.dart: Compilation finished, starting execution\n'); | 299 stderr.add('test.dart: Compilation finished, starting execution\n'); |
| 291 stdout.add('test.dart: Compilation finished, starting execution\n'); | 300 stdout.add('test.dart: Compilation finished, starting execution\n'); |
| 292 runCommand(testCase.executablePath, testCase.arguments, exitHandler); | 301 if (testCase.configuration['component'] == 'webdriver') { |
| 302 // Note: processQueue will always be non-null for component == webdriver | |
| 303 // (It is only null for component == vm) | |
| 304 processQueue._getBatchRunner(testCase).startTest(testCase); | |
| 305 } else { | |
| 306 runCommand(testCase.executablePath, testCase.arguments, exitHandler); | |
| 307 } | |
| 293 } | 308 } |
| 294 } | 309 } |
| 295 | 310 |
| 296 Function makeReadHandler(StringInputStream source, List<String> destination) { | 311 Function makeReadHandler(StringInputStream source, List<String> destination) { |
| 297 return () { | 312 return () { |
| 298 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. | 313 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. |
| 299 var line = source.readLine(); | 314 var line = source.readLine(); |
| 300 while (null != line) { | 315 while (null != line) { |
| 301 destination.add(line); | 316 destination.add(line); |
| 302 line = source.readLine(); | 317 line = source.readLine(); |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 339 makeReadHandler(stderrStringStream, stderr); | 354 makeReadHandler(stderrStringStream, stderr); |
| 340 timeoutTimer = new Timer(timeoutHandler, 1000 * testCase.timeout); | 355 timeoutTimer = new Timer(timeoutHandler, 1000 * testCase.timeout); |
| 341 } | 356 } |
| 342 | 357 |
| 343 void timeoutHandler(Timer unusedTimer) { | 358 void timeoutHandler(Timer unusedTimer) { |
| 344 timedOut = true; | 359 timedOut = true; |
| 345 process.kill(); | 360 process.kill(); |
| 346 } | 361 } |
| 347 } | 362 } |
| 348 | 363 |
| 349 | 364 class BatchRunnerProcess { |
| 350 class DartcBatchRunnerProcess { | |
| 351 String _executable; | 365 String _executable; |
| 366 List<String> _batchArguments; | |
| 352 | 367 |
| 353 Process _process; | 368 Process _process; |
| 354 StringInputStream _stdoutStream; | 369 StringInputStream _stdoutStream; |
| 355 StringInputStream _stderrStream; | 370 StringInputStream _stderrStream; |
| 356 | 371 |
| 357 TestCase _currentTest; | 372 TestCase _currentTest; |
| 358 List<String> _testStdout; | 373 List<String> _testStdout; |
| 359 List<String> _testStderr; | 374 List<String> _testStderr; |
| 360 Date _startTime; | 375 Date _startTime; |
| 361 Timer _timer; | 376 Timer _timer; |
| 362 | 377 |
| 363 DartcBatchRunnerProcess(String this._executable); | 378 bool _isWebDriver; |
| 379 | |
| 380 BatchRunnerProcess(TestCase testCase) { | |
| 381 _executable = testCase.executablePath; | |
| 382 _batchArguments = testCase.batchRunnerArguments; | |
| 383 _isWebDriver = testCase.configuration['component'] == 'webdriver'; | |
| 384 } | |
| 364 | 385 |
| 365 bool get active() => _currentTest != null; | 386 bool get active() => _currentTest != null; |
| 366 | 387 |
| 367 void startTest(TestCase testCase) { | 388 void startTest(TestCase testCase) { |
| 368 _currentTest = testCase; | 389 _currentTest = testCase; |
| 369 if (_process === null) { | 390 if (_process === null) { |
| 370 // Start process if not yet started. | 391 // Start process if not yet started. |
| 371 _executable = testCase.executablePath; | 392 _executable = testCase.executablePath; |
|
Bill Hesse
2012/09/25 12:48:45
Why isn't there an assignment to _batchArguments h
| |
| 372 _startProcess(() { | 393 _startProcess(() { |
| 373 doStartTest(testCase); | 394 doStartTest(testCase); |
| 374 }); | 395 }); |
| 375 } else if (testCase.executablePath != _executable) { | 396 } else if (testCase.executablePath != _executable) { |
| 376 // Restart this runner with the right executable for this test | 397 // Restart this runner with the right executable for this test |
| 377 // if needed. | 398 // if needed. |
| 378 _executable = testCase.executablePath; | 399 _executable = testCase.executablePath; |
| 400 _batchArguments = testCase.batchRunnerArguments; | |
| 379 _process.exitHandler = (exitCode) { | 401 _process.exitHandler = (exitCode) { |
| 380 _process.close(); | 402 _process.close(); |
| 381 _startProcess(() { | 403 _startProcess(() { |
| 382 doStartTest(testCase); | 404 doStartTest(testCase); |
| 383 }); | 405 }); |
| 384 }; | 406 }; |
| 385 _process.kill(); | 407 _process.kill(); |
| 386 } else { | 408 } else { |
| 387 doStartTest(testCase); | 409 doStartTest(testCase); |
| 388 } | 410 } |
| 389 } | 411 } |
| 390 | 412 |
| 391 void terminate() { | 413 void terminate() { |
| 392 if (_process !== null) { | 414 if (_process !== null) { |
| 415 bool closed = false; | |
| 393 _process.exitHandler = (exitCode) { | 416 _process.exitHandler = (exitCode) { |
| 417 closed = true; | |
| 394 _process.close(); | 418 _process.close(); |
| 395 }; | 419 }; |
| 396 _process.kill(); | 420 if (_isWebDriver) { |
| 421 // Use a graceful shutdown so our Selenium script can close | |
| 422 // the open browser processes. TODO(jmesserly): Send a signal once | |
| 423 // that's supported, see dartbug.com/1756. | |
| 424 _process.stdin.write('--terminate\n'.charCodes()); | |
| 425 | |
| 426 // In case the run_selenium process didn't close, kill it after 30s | |
| 427 bool shutdownMillisecs = 30000; | |
| 428 new Timer((e) { if (!closed) _process.kill(); }, shutdownMillisecs); | |
| 429 } else { | |
| 430 _process.kill(); | |
| 431 } | |
| 397 } | 432 } |
| 398 } | 433 } |
| 399 | 434 |
| 400 void doStartTest(TestCase testCase) { | 435 void doStartTest(TestCase testCase) { |
| 401 _startTime = new Date.now(); | 436 _startTime = new Date.now(); |
| 402 _testStdout = new List<String>(); | 437 _testStdout = new List<String>(); |
| 403 _testStderr = new List<String>(); | 438 _testStderr = new List<String>(); |
| 404 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); | 439 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); |
| 405 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); | 440 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); |
| 406 _timer = new Timer(_timeoutHandler(testCase), testCase.timeout * 1000); | 441 _timer = new Timer(_timeoutHandler, testCase.timeout * 1000); |
| 407 _process.stdin.write(_createArgumentsLine(testCase.arguments).charCodes()); | 442 var line = _createArgumentsLine(testCase.batchTestArguments); |
| 443 _process.stdin.write(line.charCodes()); | |
| 408 } | 444 } |
| 409 | 445 |
| 410 String _createArgumentsLine(List<String> arguments) { | 446 String _createArgumentsLine(List<String> arguments) { |
| 411 return Strings.join(arguments, ' ') + '\n'; | 447 return Strings.join(arguments, ' ') + '\n'; |
| 412 } | 448 } |
| 413 | 449 |
| 414 int _reportResult(String output) { | 450 int _reportResult(String output) { |
| 415 var test = _currentTest; | 451 var test = _currentTest; |
| 416 _currentTest = null; | 452 _currentTest = null; |
| 417 | 453 |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 453 } | 489 } |
| 454 | 490 |
| 455 void _exitHandler(exitCode) { | 491 void _exitHandler(exitCode) { |
| 456 if (_timer != null) _timer.cancel(); | 492 if (_timer != null) _timer.cancel(); |
| 457 _process.close(); | 493 _process.close(); |
| 458 _startProcess(() { | 494 _startProcess(() { |
| 459 _reportResult(">>> TEST CRASH"); | 495 _reportResult(">>> TEST CRASH"); |
| 460 }); | 496 }); |
| 461 } | 497 } |
| 462 | 498 |
| 463 Function _timeoutHandler(TestCase test) { | 499 void _timeoutHandler(ignore) { |
| 464 return (ignore) { | 500 _process.exitHandler = (exitCode) { |
| 465 _process.exitHandler = (exitCode) { | 501 _process.close(); |
| 466 _process.close(); | 502 _startProcess(() { |
| 467 _startProcess(() { | 503 _reportResult(">>> TEST TIMEOUT"); |
| 468 _reportResult(">>> TEST TIMEOUT"); | 504 }); |
| 469 }); | |
| 470 }; | |
| 471 _process.kill(); | |
| 472 }; | 505 }; |
| 506 _process.kill(); | |
| 473 } | 507 } |
| 474 | 508 |
| 475 void _startProcess(then) { | 509 void _startProcess(then) { |
| 476 _process = new Process.start(_executable, ['-batch']); | 510 _process = new Process.start(_executable, _batchArguments); |
| 477 _stdoutStream = new StringInputStream(_process.stdout); | 511 _stdoutStream = new StringInputStream(_process.stdout); |
| 478 _stderrStream = new StringInputStream(_process.stderr); | 512 _stderrStream = new StringInputStream(_process.stderr); |
| 479 _testStdout = new List<String>(); | 513 _testStdout = new List<String>(); |
| 480 _testStderr = new List<String>(); | 514 _testStderr = new List<String>(); |
| 481 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); | 515 _stdoutStream.lineHandler = _readOutput(_stdoutStream, _testStdout); |
| 482 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); | 516 _stderrStream.lineHandler = _readOutput(_stderrStream, _testStderr); |
| 483 _process.exitHandler = _exitHandler; | 517 _process.exitHandler = _exitHandler; |
| 484 _process.startHandler = then; | 518 _process.startHandler = then; |
| 485 } | 519 } |
| 486 } | 520 } |
| 487 | 521 |
| 488 | |
| 489 /** | 522 /** |
| 490 * ProcessQueue is the master control class, responsible for running all | 523 * ProcessQueue is the master control class, responsible for running all |
| 491 * the tests in all the TestSuites that have been registered. It includes | 524 * the tests in all the TestSuites that have been registered. It includes |
| 492 * a rate-limited queue to run a limited number of tests in parallel, | 525 * a rate-limited queue to run a limited number of tests in parallel, |
| 493 * a ProgressIndicator which prints output when tests are started and | 526 * a ProgressIndicator which prints output when tests are started and |
| 494 * and completed, and a summary report when all tests are completed, | 527 * and completed, and a summary report when all tests are completed, |
| 495 * and counters to determine when all of the tests in all of the test suites | 528 * and counters to determine when all of the tests in all of the test suites |
| 496 * have completed. | 529 * have completed. |
| 497 * | 530 * |
| 498 * Because multiple configurations may be run on each test suite, the | 531 * Because multiple configurations may be run on each test suite, the |
| 499 * ProcessQueue contains a cache in which a test suite may record information | 532 * ProcessQueue contains a cache in which a test suite may record information |
| 500 * about its list of tests, and may retrieve that information when it is called | 533 * about its list of tests, and may retrieve that information when it is called |
| 501 * upon to enqueue its tests again. | 534 * upon to enqueue its tests again. |
| 502 */ | 535 */ |
| 503 class ProcessQueue { | 536 class ProcessQueue { |
| 504 int _numProcesses = 0; | 537 int _numProcesses = 0; |
| 505 int _activeTestListers = 0; | 538 int _activeTestListers = 0; |
| 506 int _maxProcesses; | 539 int _maxProcesses; |
| 507 /** The number of tests we allow to actually fail before we stop retrying. */ | 540 /** The number of tests we allow to actually fail before we stop retrying. */ |
| 508 int _MAX_FAILED_NO_RETRY = 4; | 541 int _MAX_FAILED_NO_RETRY = 4; |
| 509 bool _verbose; | 542 bool _verbose; |
| 510 bool _listTests; | 543 bool _listTests; |
| 511 bool _keepGeneratedTests; | 544 bool _keepGeneratedTests; |
| 512 Function _enqueueMoreWork; | 545 Function _enqueueMoreWork; |
| 513 Queue<TestCase> _tests; | 546 Queue<TestCase> _tests; |
| 514 ProgressIndicator _progress; | 547 ProgressIndicator _progress; |
| 515 String _temporaryDirectory; | 548 String _temporaryDirectory; |
| 516 // For dartc batch processing we keep a list of batch processes. | 549 // For dartc/selenium batch processing we keep a list of batch processes. |
| 517 List<DartcBatchRunnerProcess> _batchProcesses; | 550 Map<String, List<BatchRunnerProcess>> _batchProcesses; |
| 551 | |
| 518 // Cache information about test cases per test suite. For multiple | 552 // Cache information about test cases per test suite. For multiple |
| 519 // configurations there is no need to repeatedly search the file | 553 // configurations there is no need to repeatedly search the file |
| 520 // system, generate tests, and search test files for options. | 554 // system, generate tests, and search test files for options. |
| 521 Map<String, List<TestInformation>> _testCache; | 555 Map<String, List<TestInformation>> _testCache; |
| 522 /** | 556 /** |
| 523 * String indicating the browser used to run the tests. Empty if no browser | 557 * String indicating the browser used to run the tests. Empty if no browser |
| 524 * used. | 558 * used. |
| 525 */ | 559 */ |
| 526 String browserUsed; | 560 String browserUsed; |
| 527 | 561 |
| 528 ProcessQueue(int this._maxProcesses, | 562 ProcessQueue(int this._maxProcesses, |
| 529 String progress, | 563 String progress, |
| 530 Date startTime, | 564 Date startTime, |
| 531 bool printTiming, | 565 bool printTiming, |
| 532 Function this._enqueueMoreWork, | 566 Function this._enqueueMoreWork, |
| 533 [bool this._verbose = false, | 567 [bool this._verbose = false, |
| 534 bool this._listTests = false, | 568 bool this._listTests = false, |
| 535 bool this._keepGeneratedTests = false]) | 569 bool this._keepGeneratedTests = false]) |
| 536 : _tests = new Queue<TestCase>(), | 570 : _tests = new Queue<TestCase>(), |
| 537 _progress = new ProgressIndicator.fromName(progress, | 571 _progress = new ProgressIndicator.fromName(progress, |
| 538 startTime, | 572 startTime, |
| 539 printTiming), | 573 printTiming), |
| 540 _batchProcesses = new List<DartcBatchRunnerProcess>(), | 574 _batchProcesses = new Map<String, List<BatchRunnerProcess>>(), |
| 541 _testCache = new Map<String, List<TestInformation>>() { | 575 _testCache = new Map<String, List<TestInformation>>() { |
| 542 if (!_enqueueMoreWork(this)) _progress.allDone(); | 576 if (!_enqueueMoreWork(this)) _progress.allDone(); |
| 543 browserUsed = ''; | 577 browserUsed = ''; |
| 544 } | 578 } |
| 545 | 579 |
| 546 /** | 580 /** |
| 547 * Registers a TestSuite so that all of its tests will be run. | 581 * Registers a TestSuite so that all of its tests will be run. |
| 548 */ | 582 */ |
| 549 void addTestSuite(TestSuite testSuite) { | 583 void addTestSuite(TestSuite testSuite) { |
| 550 _activeTestListers++; | 584 _activeTestListers++; |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 573 /** | 607 /** |
| 574 * Sometimes Webdriver doesn't close every browser window when it's done | 608 * Sometimes Webdriver doesn't close every browser window when it's done |
| 575 * with a test. At the end of all tests we clear out any neglected processes | 609 * with a test. At the end of all tests we clear out any neglected processes |
| 576 * that are still running. | 610 * that are still running. |
| 577 */ | 611 */ |
| 578 void killZombieBrowsers() { | 612 void killZombieBrowsers() { |
| 579 String chromeName = 'chrome'; | 613 String chromeName = 'chrome'; |
| 580 if (new Platform().operatingSystem() == 'macos') { | 614 if (new Platform().operatingSystem() == 'macos') { |
| 581 chromeName = 'Google\ Chrome'; | 615 chromeName = 'Google\ Chrome'; |
| 582 } | 616 } |
| 583 Map<String, List<String>> processNames = {'ie': ['iexplore'], 'safari': | 617 Map<String, List<String>> processNames = {'ie': ['iexplore'], |
| 584 ['Safari'], 'ff': ['firefox'], 'chrome': ['chromedriver', chromeName]}; | 618 'safari': ['Safari'], 'ff': ['firefox', 'firefox-bin'], |
| 619 'chrome': ['chromedriver', chromeName]}; | |
| 585 for (String name in processNames[browserUsed]) { | 620 for (String name in processNames[browserUsed]) { |
| 586 Process process = null; | 621 Process process = null; |
| 587 if (new Platform().operatingSystem() == 'windows') { | 622 if (new Platform().operatingSystem() == 'windows') { |
| 588 process = new Process.start( | 623 process = new Process.start( |
| 589 'C:\\Windows\\System32\\taskkill.exe', ['/F', '/IM', name + '.exe', | 624 'C:\\Windows\\System32\\taskkill.exe', ['/F', '/IM', name + '.exe', |
| 590 '/T']); | 625 '/T']); |
| 591 } else { | 626 } else { |
| 592 process = new Process.start('killall', ['-9', name]); | 627 process = new Process.start('killall', ['-9', name]); |
| 593 } | 628 } |
| 594 | 629 |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 619 _progress.allDone(); | 654 _progress.allDone(); |
| 620 } | 655 } |
| 621 } | 656 } |
| 622 | 657 |
| 623 void _checkDone() { | 658 void _checkDone() { |
| 624 // When there are no more active test listers ask for more work | 659 // When there are no more active test listers ask for more work |
| 625 // from process queue users. | 660 // from process queue users. |
| 626 if (_activeTestListers == 0 && !_enqueueMoreWork(this)) { | 661 if (_activeTestListers == 0 && !_enqueueMoreWork(this)) { |
| 627 _progress.allTestsKnown(); | 662 _progress.allTestsKnown(); |
| 628 if (_tests.isEmpty() && _numProcesses == 0) { | 663 if (_tests.isEmpty() && _numProcesses == 0) { |
| 629 _terminateDartcBatchRunners(); | 664 _terminateBatchRunners(); |
| 630 if (_keepGeneratedTests || _temporaryDirectory == null) { | 665 if (_keepGeneratedTests || _temporaryDirectory == null) { |
| 631 _cleanupAndMarkDone(); | 666 _cleanupAndMarkDone(); |
| 632 } else if (!_temporaryDirectory.startsWith('/tmp/') || | 667 } else if (!_temporaryDirectory.startsWith('/tmp/') || |
| 633 _temporaryDirectory.contains('/../')) { | 668 _temporaryDirectory.contains('/../')) { |
| 634 // Let's be extra careful, since rm -rf is so dangerous. | 669 // Let's be extra careful, since rm -rf is so dangerous. |
| 635 print('Temporary directory $_temporaryDirectory unsafe to delete!'); | 670 print('Temporary directory $_temporaryDirectory unsafe to delete!'); |
| 636 _cleanupAndMarkDone(); | 671 _cleanupAndMarkDone(); |
| 637 } else { | 672 } else { |
| 638 // TODO(dart:1211): Use delete(recursive=true) in Dart when it is | 673 // TODO(dart:1211): Use delete(recursive=true) in Dart when it is |
| 639 // implemented, and add Windows support. | 674 // implemented, and add Windows support. |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 656 | 691 |
| 657 void _runTest(TestCase test) { | 692 void _runTest(TestCase test) { |
| 658 if (test.configuration['component'] == 'webdriver') { | 693 if (test.configuration['component'] == 'webdriver') { |
| 659 browserUsed = test.configuration['browser']; | 694 browserUsed = test.configuration['browser']; |
| 660 } | 695 } |
| 661 _progress.testAdded(); | 696 _progress.testAdded(); |
| 662 _tests.add(test); | 697 _tests.add(test); |
| 663 _tryRunTest(); | 698 _tryRunTest(); |
| 664 } | 699 } |
| 665 | 700 |
| 666 void _terminateDartcBatchRunners() { | 701 void _terminateBatchRunners() { |
| 667 _batchProcesses.forEach((runner) => runner.terminate()); | 702 for (var runners in _batchProcesses.getValues()) { |
| 668 } | 703 for (var runner in runners) { |
| 669 | 704 runner.terminate(); |
| 670 void _ensureDartcBatchRunnersStarted(String executable) { | |
| 671 if (_batchProcesses.length == 0) { | |
| 672 for (int i = 0; i < _maxProcesses; i++) { | |
| 673 _batchProcesses.add(new DartcBatchRunnerProcess(executable)); | |
| 674 } | 705 } |
| 675 } | 706 } |
| 676 } | 707 } |
| 677 | 708 |
| 678 DartcBatchRunnerProcess _getDartcBatchRunnerProcess() { | 709 BatchRunnerProcess _getBatchRunner(TestCase test) { |
| 679 for (int i = 0; i < _batchProcesses.length; i++) { | 710 // Start batch processes if needed |
| 680 var runner = _batchProcesses[i]; | 711 var component = test.configuration['component']; |
| 712 var runners = _batchProcesses[component]; | |
| 713 if (runners == null) { | |
| 714 runners = new List<BatchRunnerProcess>(_maxProcesses); | |
| 715 for (int i = 0; i < _maxProcesses; i++) { | |
| 716 runners[i] = new BatchRunnerProcess(test); | |
| 717 } | |
| 718 _batchProcesses[component] = runners; | |
| 719 } | |
| 720 | |
| 721 for (var runner in runners) { | |
| 681 if (!runner.active) return runner; | 722 if (!runner.active) return runner; |
| 682 } | 723 } |
| 683 throw new Exception('Unable to find inactive batch runner.'); | 724 throw new Exception('Unable to find inactive batch runner.'); |
| 684 } | 725 } |
| 685 | 726 |
| 686 void _tryRunTest() { | 727 void _tryRunTest() { |
| 687 _checkDone(); | 728 _checkDone(); |
| 688 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { | 729 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { |
| 689 TestCase test = _tests.removeFirst(); | 730 TestCase test = _tests.removeFirst(); |
| 690 if (_verbose) print(test.commandLine); | 731 if (_verbose) print(test.commandLine); |
| 691 if (_listTests) { | 732 if (_listTests) { |
| 692 final String tab = '\t'; | 733 final String tab = '\t'; |
| 693 String outcomes = | 734 String outcomes = |
| 694 Strings.join(new List.from(test.expectedOutcomes), ','); | 735 Strings.join(new List.from(test.expectedOutcomes), ','); |
| 695 print(test.displayName + tab + outcomes + tab + test.isNegative + | 736 print(test.displayName + tab + outcomes + tab + test.isNegative + |
| 696 tab + Strings.join(test.arguments, tab)); | 737 tab + Strings.join(test.arguments, tab)); |
| 697 return; | 738 return; |
| 698 } | 739 } |
| 699 _progress.start(test); | 740 _progress.start(test); |
| 700 Function oldCallback = test.completedHandler; | 741 Function oldCallback = test.completedHandler; |
| 701 Function wrapper = (TestCase test_arg) { | 742 Function wrapper = (TestCase test_arg) { |
| 702 _numProcesses--; | 743 _numProcesses--; |
| 703 _progress.done(test_arg); | 744 _progress.done(test_arg); |
| 704 _tryRunTest(); | 745 _tryRunTest(); |
| 705 oldCallback(test_arg); | 746 oldCallback(test_arg); |
| 706 }; | 747 }; |
| 707 test.completedHandler = wrapper; | 748 test.completedHandler = wrapper; |
| 708 if (test.configuration['component'] == 'dartc' && | 749 if (test.configuration['component'] == 'dartc' && |
| 709 test.displayName != 'dartc/junit_tests') { | 750 test.displayName != 'dartc/junit_tests') { |
| 710 _ensureDartcBatchRunnersStarted(test.executablePath); | 751 _getBatchRunner(test).startTest(test); |
| 711 _getDartcBatchRunnerProcess().startTest(test); | |
| 712 } else { | 752 } else { |
| 713 // Once we've actually failed a test, technically, we wouldn't need to | 753 // Once we've actually failed a test, technically, we wouldn't need to |
| 714 // bother retrying any subsequent tests since the bot is already red. | 754 // bother retrying any subsequent tests since the bot is already red. |
| 715 // However, we continue to retry tests until we have actually failed | 755 // However, we continue to retry tests until we have actually failed |
| 716 // four tests (arbitrarily chosen) for more debugable output, so that | 756 // four tests (arbitrarily chosen) for more debugable output, so that |
| 717 // the developer doesn't waste his or her time trying to fix a bunch of | 757 // the developer doesn't waste his or her time trying to fix a bunch of |
| 718 // tests that appear to be broken but were actually just flakes that | 758 // tests that appear to be broken but were actually just flakes that |
| 719 // didn't get retried because there had already been one failure. | 759 // didn't get retried because there had already been one failure. |
| 720 new RunningProcess(test, | 760 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 721 _MAX_FAILED_NO_RETRY > _progress.numFailedTests).start(); | 761 new RunningProcess(test, allowRetry, this).start(); |
| 722 } | 762 } |
| 723 _numProcesses++; | 763 _numProcesses++; |
| 724 } | 764 } |
| 725 } | 765 } |
| 726 } | 766 } |
| OLD | NEW |