Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(10)

Side by Side Diff: tools/testing/dart/test_runner.dart

Issue 9369051: Force close of browser Windows when complete. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: '' Created 8 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 class BrowserTestCase extends TestCase { 111 class BrowserTestCase extends TestCase {
112 /** 112 /**
113 * The executable that is run in a new process in the compilation phase. 113 * The executable that is run in a new process in the compilation phase.
114 */ 114 */
115 String compilerPath; 115 String compilerPath;
116 /** 116 /**
117 * The arguments for the compilation command. 117 * The arguments for the compilation command.
118 */ 118 */
119 List<String> compilerArguments; 119 List<String> compilerArguments;
120 /** 120 /**
121 * Indicates if this test is a rerun, to compensate for flaky browser tests. 121 * Indicates the number of potential retries remaining, to compensate for
122 * flaky browser tests.
122 */ 123 */
123 bool isRerun; 124 bool numRetries;
124 125
125 BrowserTestCase(displayName, 126 BrowserTestCase(displayName,
126 this.compilerPath, 127 this.compilerPath,
127 this.compilerArguments, 128 this.compilerArguments,
128 executablePath, 129 executablePath,
129 arguments, 130 arguments,
130 configuration, 131 configuration,
131 completedHandler, 132 completedHandler,
132 expectedOutcomes, 133 expectedOutcomes,
133 [isNegative = false]) : super(displayName, 134 [isNegative = false]) : super(displayName,
134 executablePath, 135 executablePath,
135 arguments, 136 arguments,
136 configuration, 137 configuration,
137 completedHandler, 138 completedHandler,
138 expectedOutcomes, 139 expectedOutcomes,
139 isNegative) { 140 isNegative) {
140 if (compilerPath != null) { 141 if (compilerPath != null) {
141 commandLine = 'execution command: $commandLine'; 142 commandLine = 'execution command: $commandLine';
142 String compilationCommand = 143 String compilationCommand =
143 '$compilerPath ${Strings.join(compilerArguments, " ")}'; 144 '$compilerPath ${Strings.join(compilerArguments, " ")}';
144 commandLine = 'compilation command: $compilationCommand\n$commandLine'; 145 commandLine = 'compilation command: $compilationCommand\n$commandLine';
145 } 146 }
146 isRerun = false; 147 numRetries = 2; // Allow two retries to compensate for flaky browser tests.
147 } 148 }
148 } 149 }
149 150
150 151
151 /** 152 /**
152 * TestOutput records the output of a completed test: the process's exit code, 153 * TestOutput records the output of a completed test: the process's exit code,
153 * the standard output and standard error, whether the process timed out, and 154 * the standard output and standard error, whether the process timed out, and
154 * the time the process took to run. It also contains a pointer to the 155 * the time the process took to run. It also contains a pointer to the
155 * [TestCase] this is the output of. 156 * [TestCase] this is the output of.
156 */ 157 */
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
252 new TestOutput(testCase, exitCode, timedOut, stdout, 253 new TestOutput(testCase, exitCode, timedOut, stdout,
253 stderr, new Date.now().difference(startTime)); 254 stderr, new Date.now().difference(startTime));
254 process.close(); 255 process.close();
255 timeoutTimer.cancel(); 256 timeoutTimer.cancel();
256 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { 257 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) {
257 print(testCase.displayName); 258 print(testCase.displayName);
258 for (var line in testCase.output.stderr) print(line); 259 for (var line in testCase.output.stderr) print(line);
259 for (var line in testCase.output.stdout) print(line); 260 for (var line in testCase.output.stdout) print(line);
260 } 261 }
261 if (testCase is BrowserTestCase && testCase.output.unexpectedOutput && 262 if (testCase is BrowserTestCase && testCase.output.unexpectedOutput &&
262 !testCase.isRerun) { 263 testCase.numRetries > 0) {
263 // Selenium tests can be flaky. Try rerunning. 264 // Selenium tests can be flaky. Try rerunning.
264 testCase.output.requestRetry = true; 265 testCase.output.requestRetry = true;
265 } 266 }
266 if (testCase.output.requestRetry) { 267 if (testCase.output.requestRetry) {
267 testCase.output.requestRetry = false; 268 testCase.output.requestRetry = false;
268 this.timedOut = false; 269 this.timedOut = false;
269 testCase.isRerun = true; 270 testCase.dynamic.numRetries--;
270 print("Potential flake. Re-running " + testCase.displayName); 271 print("Potential flake. Re-running " + testCase.displayName);
271 this.start(); 272 this.start();
272 } else { 273 } else {
273 testCase.completed(); 274 testCase.completed();
274 } 275 }
275 } 276 }
276 277
277 void compilerExitHandler(int exitCode) { 278 void compilerExitHandler(int exitCode) {
278 if (exitCode != 0) { 279 if (exitCode != 0) {
279 stderr.add('test.dart: Compilation step failed (exit code $exitCode)\n'); 280 stderr.add('test.dart: Compilation step failed (exit code $exitCode)\n');
(...skipping 223 matching lines...) Expand 10 before | Expand all | Expand 10 after
503 Function _enqueueMoreWork; 504 Function _enqueueMoreWork;
504 Queue<TestCase> _tests; 505 Queue<TestCase> _tests;
505 ProgressIndicator _progress; 506 ProgressIndicator _progress;
506 String _temporaryDirectory; 507 String _temporaryDirectory;
507 // For dartc batch processing we keep a list of batch processes. 508 // For dartc batch processing we keep a list of batch processes.
508 List<DartcBatchRunnerProcess> _batchProcesses; 509 List<DartcBatchRunnerProcess> _batchProcesses;
509 // Cache information about test cases per test suite. For multiple 510 // Cache information about test cases per test suite. For multiple
510 // configurations there is no need to repeatedly search the file 511 // configurations there is no need to repeatedly search the file
511 // system, generate tests, and search test files for options. 512 // system, generate tests, and search test files for options.
512 Map<String, List<TestInformation>> _testCache; 513 Map<String, List<TestInformation>> _testCache;
514 /**
515 * String indicating the browser used to run the tests. Empty if no browser
516 * used.
517 */
518 String browserUsed;
513 519
514 ProcessQueue(int this._maxProcesses, 520 ProcessQueue(int this._maxProcesses,
515 String progress, 521 String progress,
516 Date startTime, 522 Date startTime,
517 bool printTiming, 523 bool printTiming,
518 Function this._enqueueMoreWork, 524 Function this._enqueueMoreWork,
519 [bool this._verbose = false, 525 [bool this._verbose = false,
520 bool this._listTests = false, 526 bool this._listTests = false,
521 bool this._keepGeneratedTests = false]) 527 bool this._keepGeneratedTests = false])
522 : _tests = new Queue<TestCase>(), 528 : _tests = new Queue<TestCase>(),
523 _progress = new ProgressIndicator.fromName(progress, 529 _progress = new ProgressIndicator.fromName(progress,
524 startTime, 530 startTime,
525 printTiming), 531 printTiming),
526 _batchProcesses = new List<DartcBatchRunnerProcess>(), 532 _batchProcesses = new List<DartcBatchRunnerProcess>(),
527 _testCache = new Map<String, List<TestInformation>>() { 533 _testCache = new Map<String, List<TestInformation>>() {
528 if (!_enqueueMoreWork(this)) _progress.allDone(); 534 if (!_enqueueMoreWork(this)) _progress.allDone();
535 browserUsed = '';
529 } 536 }
530 537
531 /** 538 /**
532 * Registers a TestSuite so that all of its tests will be run. 539 * Registers a TestSuite so that all of its tests will be run.
533 */ 540 */
534 void addTestSuite(TestSuite testSuite) { 541 void addTestSuite(TestSuite testSuite) {
535 _activeTestListers++; 542 _activeTestListers++;
536 testSuite.forEachTest(_runTest, _testCache, globalTemporaryDirectory, 543 testSuite.forEachTest(_runTest, _testCache, globalTemporaryDirectory,
537 _testListerDone); 544 _testListerDone);
538 } 545 }
539 546
540 void _testListerDone() { 547 void _testListerDone() {
541 _activeTestListers--; 548 _activeTestListers--;
542 _checkDone(); 549 _checkDone();
543 } 550 }
544 551
545 String globalTemporaryDirectory() { 552 String globalTemporaryDirectory() {
546 if (_temporaryDirectory != null) return _temporaryDirectory; 553 if (_temporaryDirectory != null) return _temporaryDirectory;
547 554
548 if (new Platform().operatingSystem() == 'windows') { 555 if (new Platform().operatingSystem() == 'windows') {
549 throw new Exception( 556 throw new Exception(
550 'Test suite requires temporary directory. Not supported on Windows.'); 557 'Test suite requires temporary directory. Not supported on Windows.');
551 } 558 }
552 var tempDir = new Directory(''); 559 var tempDir = new Directory('');
553 tempDir.createTempSync(); 560 tempDir.createTempSync();
554 _temporaryDirectory = tempDir.path; 561 _temporaryDirectory = tempDir.path;
555 return _temporaryDirectory; 562 return _temporaryDirectory;
556 } 563 }
557 564
565 /**
566 * Sometimes Webdriver doesn't close every browser window when it's done
567 * with a test. At the end of all tests we clear out any neglected processes
568 * that are still running.
569 */
570 void killZombieBrowsers() {
571 String chromeName = 'chrome';
572 if (new Platform().operatingSystem() == 'macos') {
573 chromeName = 'Google\ Chrome';
574 }
575 Map<String, List<String>> processNames = {'ie': ['iexplore'], 'safari':
576 ['Safari'], 'ff': ['firefox'], 'chrome': ['chromedriver', chromeName]};
577 for (String name in processNames[browserUsed]) {
578 Process process = null;
579 if (new Platform().operatingSystem() == 'windows') {
580 process = new Process.start(
581 'C:\\Windows\\System32\\taskkill.exe', ['/F', '/IM', name + '.exe',
582 '/T']);
583 } else {
584 process = new Process.start('killall', ['-9', name]);
585 }
586
587 if (name == processNames[browserUsed].last()) {
588 process.exitHandler = (exitCode) {
589 process.close();
590 _progress.allDone();
591 };
592 process.errorHandler = (error) {
593 _progress.allDone();
594 };
595 } else {
596 process.exitHandler = (exitCode) {
597 process.close();
598 };
599 }
600 }
601 }
602
603 /**
604 * Perform any cleanup needed once all tests in a TestSuite have completed
605 * and notify our progress indicator that we are done.
606 */
607 void _cleanupAndMarkDone() {
608 if (browserUsed != '') {
609 killZombieBrowsers();
610 } else {
611 _progress.allDone();
612 }
613 }
558 614
559 void _checkDone() { 615 void _checkDone() {
560 // When there are no more active test listers ask for more work 616 // When there are no more active test listers ask for more work
561 // from process queue users. 617 // from process queue users.
562 if (_activeTestListers == 0 && !_enqueueMoreWork(this)) { 618 if (_activeTestListers == 0 && !_enqueueMoreWork(this)) {
563 _progress.allTestsKnown(); 619 _progress.allTestsKnown();
564 if (_tests.isEmpty() && _numProcesses == 0) { 620 if (_tests.isEmpty() && _numProcesses == 0) {
565 _terminateDartcBatchRunners(); 621 _terminateDartcBatchRunners();
566 if (_keepGeneratedTests || _temporaryDirectory == null) { 622 if (_keepGeneratedTests || _temporaryDirectory == null) {
567 _progress.allDone(); 623 _cleanupAndMarkDone();
568 } else if (!_temporaryDirectory.startsWith('/tmp/') || 624 } else if (!_temporaryDirectory.startsWith('/tmp/') ||
569 _temporaryDirectory.contains('/../')) { 625 _temporaryDirectory.contains('/../')) {
570 // Let's be extra careful, since rm -rf is so dangerous. 626 // Let's be extra careful, since rm -rf is so dangerous.
571 print('Temporary directory $_temporaryDirectory unsafe to delete!'); 627 print('Temporary directory $_temporaryDirectory unsafe to delete!');
572 _progress.allDone(); 628 _cleanupAndMarkDone();
573 } else { 629 } else {
574 // TODO(dart:1211): Use delete(recursive=true) in Dart when it is 630 // TODO(dart:1211): Use delete(recursive=true) in Dart when it is
575 // implemented, and add Windows support. 631 // implemented, and add Windows support.
576 var deletion = 632 var deletion =
577 new Process.start('/bin/rm', ['-rf', _temporaryDirectory]); 633 new Process.start('/bin/rm', ['-rf', _temporaryDirectory]);
578 deletion.exitHandler = (int exitCode) { 634 deletion.exitHandler = (int exitCode) {
579 if (exitCode == 0) { 635 if (exitCode == 0) {
580 if (!_listTests) { // Output of --list option is used by scripts. 636 if (!_listTests) { // Output of --list option is used by scripts.
581 print('\nTemporary directory $_temporaryDirectory deleted.'); 637 print('\nTemporary directory $_temporaryDirectory deleted.');
582 } 638 }
583 } else { 639 } else {
584 print('\nDeletion of temp dir $_temporaryDirectory failed.'); 640 print('\nDeletion of temp dir $_temporaryDirectory failed.');
585 } 641 }
586 _progress.allDone(); 642 _cleanupAndMarkDone();
587 }; 643 };
588 } 644 }
589 } 645 }
590 } 646 }
591 } 647 }
592 648
593 void _runTest(TestCase test) { 649 void _runTest(TestCase test) {
650 if (test.configuration['component'] == 'webdriver') {
651 browserUsed = test.configuration['browser'];
652 }
594 _progress.testAdded(); 653 _progress.testAdded();
595 _tests.add(test); 654 _tests.add(test);
596 _tryRunTest(); 655 _tryRunTest();
597 } 656 }
598 657
599 void _terminateDartcBatchRunners() { 658 void _terminateDartcBatchRunners() {
600 _batchProcesses.forEach((runner) => runner.terminate()); 659 _batchProcesses.forEach((runner) => runner.terminate());
601 } 660 }
602 661
603 void _ensureDartcBatchRunnersStarted(String executable) { 662 void _ensureDartcBatchRunnersStarted(String executable) {
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
642 test.displayName != 'dartc/junit_tests') { 701 test.displayName != 'dartc/junit_tests') {
643 _ensureDartcBatchRunnersStarted(test.executablePath); 702 _ensureDartcBatchRunnersStarted(test.executablePath);
644 _getDartcBatchRunnerProcess().startTest(test); 703 _getDartcBatchRunnerProcess().startTest(test);
645 } else { 704 } else {
646 new RunningProcess(test).start(); 705 new RunningProcess(test).start();
647 } 706 }
648 _numProcesses++; 707 _numProcesses++;
649 } 708 }
650 } 709 }
651 } 710 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698