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

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

Issue 9581025: Webdriver install script, take 2. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 8 years, 9 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
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 114 matching lines...) Expand 10 before | Expand all | Expand 10 after
125 final component = configuration['component']; 125 final component = configuration['component'];
126 final mode = configuration['mode']; 126 final mode = configuration['mode'];
127 final arch = configuration['arch']; 127 final arch = configuration['arch'];
128 return "$component ${mode}_$arch"; 128 return "$component ${mode}_$arch";
129 } 129 }
130 130
131 List<String> get batchRunnerArguments() => ['-batch']; 131 List<String> get batchRunnerArguments() => ['-batch'];
132 List<String> get batchTestArguments() => commands.last().arguments; 132 List<String> get batchTestArguments() => commands.last().arguments;
133 133
134 void completed() { completedHandler(this); } 134 void completed() { completedHandler(this); }
135
136 bool get usesWebDriver() => configuration['component'] == 'webdriver';
135 } 137 }
136 138
137 139
138 /** 140 /**
139 * BrowserTestCase has an extra compilation command that is run in a separate 141 * BrowserTestCase has an extra compilation command that is run in a separate
140 * process, before the regular test is run as in the base class [TestCase]. 142 * process, before the regular test is run as in the base class [TestCase].
141 * If the compilation command fails, then the rest of the test is not run. 143 * If the compilation command fails, then the rest of the test is not run.
142 */ 144 */
143 class BrowserTestCase extends TestCase { 145 class BrowserTestCase extends TestCase {
144 /** 146 /**
(...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 void testComplete(int exitCode) { 475 void testComplete(int exitCode) {
474 new TestOutput.fromCase(testCase, exitCode, timedOut, stdout, 476 new TestOutput.fromCase(testCase, exitCode, timedOut, stdout,
475 stderr, new Date.now().difference(startTime)); 477 stderr, new Date.now().difference(startTime));
476 timeoutTimer.cancel(); 478 timeoutTimer.cancel();
477 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { 479 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) {
478 print(testCase.displayName); 480 print(testCase.displayName);
479 for (var line in testCase.output.stderr) print(line); 481 for (var line in testCase.output.stderr) print(line);
480 for (var line in testCase.output.stdout) print(line); 482 for (var line in testCase.output.stdout) print(line);
481 } 483 }
482 if (allowRetries != null && allowRetries 484 if (allowRetries != null && allowRetries
483 && testCase.configuration['component'] == 'webdriver' && 485 && testCase.usesWebDriver && testCase.output.unexpectedOutput
484 testCase.output.unexpectedOutput && testCase.numRetries > 0) { 486 && testCase.numRetries > 0) {
485 // Selenium tests can be flaky. Try rerunning. 487 // Selenium tests can be flaky. Try rerunning.
486 testCase.output.requestRetry = true; 488 testCase.output.requestRetry = true;
487 } 489 }
488 if (testCase.output.requestRetry) { 490 if (testCase.output.requestRetry) {
489 testCase.output.requestRetry = false; 491 testCase.output.requestRetry = false;
490 this.timedOut = false; 492 this.timedOut = false;
491 testCase.dynamic.numRetries--; 493 testCase.dynamic.numRetries--;
492 print("Potential flake. Re-running ${testCase.displayName} " + 494 print("Potential flake. Re-running ${testCase.displayName} " +
493 "(${testCase.dynamic.numRetries} attempt(s) remains)"); 495 "(${testCase.dynamic.numRetries} attempt(s) remains)");
494 this.start(); 496 this.start();
(...skipping 12 matching lines...) Expand all
507 int totalSteps = testCase.commands.length; 509 int totalSteps = testCase.commands.length;
508 String suffix =' (step $currentStep of $totalSteps)'; 510 String suffix =' (step $currentStep of $totalSteps)';
509 if (currentStep == totalSteps) { // done with test command 511 if (currentStep == totalSteps) { // done with test command
510 testComplete(exitCode); 512 testComplete(exitCode);
511 } else if (exitCode != 0) { 513 } else if (exitCode != 0) {
512 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n'); 514 stderr.add('test.dart: Compilation failed$suffix, exit code $exitCode\n');
513 testComplete(exitCode); 515 testComplete(exitCode);
514 } else { 516 } else {
515 stderr.add('test.dart: Compilion finished $suffix\n'); 517 stderr.add('test.dart: Compilion finished $suffix\n');
516 stdout.add('test.dart: Compilion finished $suffix\n'); 518 stdout.add('test.dart: Compilion finished $suffix\n');
517 if (currentStep == totalSteps - 1 519 if (currentStep == totalSteps - 1 && testCase.usesWebDriver &&
518 && testCase.configuration['component'] == 'webdriver') { 520 !testCase.configuration['noBatch']) {
519 // Note: processQueue will always be non-null for component == webdriver 521 // Note: processQueue will always be non-null for component == webdriver
520 // (It is only null for component == vm) 522 // (It is only null for component == vm)
521 processQueue._getBatchRunner(testCase).startTest(testCase); 523 processQueue._getBatchRunner(testCase).startTest(testCase);
522 } else { 524 } else {
523 runCommand(testCase.commands[currentStep++], stepExitHandler); 525 runCommand(testCase.commands[currentStep++], stepExitHandler);
524 } 526 }
525 } 527 }
526 } 528 }
527 529
528 Function makeReadHandler(StringInputStream source, List<String> destination) { 530 Function makeReadHandler(StringInputStream source, List<String> destination) {
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
586 bool _stdoutDrained = false; 588 bool _stdoutDrained = false;
587 bool _stderrDrained = false; 589 bool _stderrDrained = false;
588 Date _startTime; 590 Date _startTime;
589 Timer _timer; 591 Timer _timer;
590 592
591 bool _isWebDriver; 593 bool _isWebDriver;
592 594
593 BatchRunnerProcess(TestCase testCase) { 595 BatchRunnerProcess(TestCase testCase) {
594 _executable = testCase.commands.last().executable; 596 _executable = testCase.commands.last().executable;
595 _batchArguments = testCase.batchRunnerArguments; 597 _batchArguments = testCase.batchRunnerArguments;
596 _isWebDriver = testCase.configuration['component'] == 'webdriver'; 598 _isWebDriver = testCase.usesWebDriver;
597 } 599 }
598 600
599 bool get active() => _currentTest != null; 601 bool get active() => _currentTest != null;
600 602
601 void startTest(TestCase testCase) { 603 void startTest(TestCase testCase) {
602 _currentTest = testCase; 604 _currentTest = testCase;
603 if (_process === null) { 605 if (_process === null) {
604 // Start process if not yet started. 606 // Start process if not yet started.
605 _executable = testCase.commands.last().executable; 607 _executable = testCase.commands.last().executable;
606 _startProcess(() { 608 _startProcess(() {
(...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after
771 * 773 *
772 * Because multiple configurations may be run on each test suite, the 774 * Because multiple configurations may be run on each test suite, the
773 * ProcessQueue contains a cache in which a test suite may record information 775 * ProcessQueue contains a cache in which a test suite may record information
774 * about its list of tests, and may retrieve that information when it is called 776 * about its list of tests, and may retrieve that information when it is called
775 * upon to enqueue its tests again. 777 * upon to enqueue its tests again.
776 */ 778 */
777 class ProcessQueue { 779 class ProcessQueue {
778 int _numProcesses = 0; 780 int _numProcesses = 0;
779 int _activeTestListers = 0; 781 int _activeTestListers = 0;
780 int _maxProcesses; 782 int _maxProcesses;
783
781 /** The number of tests we allow to actually fail before we stop retrying. */ 784 /** The number of tests we allow to actually fail before we stop retrying. */
782 int _MAX_FAILED_NO_RETRY = 4; 785 int _MAX_FAILED_NO_RETRY = 4;
783 bool _verbose; 786 bool _verbose;
784 bool _listTests; 787 bool _listTests;
785 bool _keepGeneratedTests; 788 bool _keepGeneratedTests;
786 Function _enqueueMoreWork; 789 Function _enqueueMoreWork;
787 Queue<TestCase> _tests; 790 Queue<TestCase> _tests;
788 ProgressIndicator _progress; 791 ProgressIndicator _progress;
789 String _temporaryDirectory; 792 String _temporaryDirectory;
793
790 // For dartc/selenium batch processing we keep a list of batch processes. 794 // For dartc/selenium batch processing we keep a list of batch processes.
791 Map<String, List<BatchRunnerProcess>> _batchProcesses; 795 Map<String, List<BatchRunnerProcess>> _batchProcesses;
792 796
793 // Cache information about test cases per test suite. For multiple 797 // Cache information about test cases per test suite. For multiple
794 // configurations there is no need to repeatedly search the file 798 // configurations there is no need to repeatedly search the file
795 // system, generate tests, and search test files for options. 799 // system, generate tests, and search test files for options.
796 Map<String, List<TestInformation>> _testCache; 800 Map<String, List<TestInformation>> _testCache;
801
797 /** 802 /**
798 * String indicating the browser used to run the tests. Empty if no browser 803 * String indicating the browser used to run the tests. Empty if no browser
799 * used. 804 * used.
800 */ 805 */
801 String browserUsed; 806 String browserUsed = '';
807
808 /**
809 * Process running the selenium server .jar (only used for Safari and Opera
810 * tests.)
811 */
812 Process _seleniumServer = null;
813
814 /** True if we are in the process of starting the server. */
815 bool _startingServer = false;
816
817 /** True if we find that there is already a selenium jar running. */
818 bool _seleniumAlreadyRunning = false;
802 819
803 ProcessQueue(int this._maxProcesses, 820 ProcessQueue(int this._maxProcesses,
804 String progress, 821 String progress,
805 Date startTime, 822 Date startTime,
806 bool printTiming, 823 bool printTiming,
807 Function this._enqueueMoreWork, 824 Function this._enqueueMoreWork,
808 [bool this._verbose = false, 825 [bool this._verbose = false,
809 bool this._listTests = false, 826 bool this._listTests = false,
810 bool this._keepGeneratedTests = false]) 827 bool this._keepGeneratedTests = false])
811 : _tests = new Queue<TestCase>(), 828 : _tests = new Queue<TestCase>(),
812 _progress = new ProgressIndicator.fromName(progress, 829 _progress = new ProgressIndicator.fromName(progress,
813 startTime, 830 startTime,
814 printTiming), 831 printTiming),
815 _batchProcesses = new Map<String, List<BatchRunnerProcess>>(), 832 _batchProcesses = new Map<String, List<BatchRunnerProcess>>(),
816 _testCache = new Map<String, List<TestInformation>>() { 833 _testCache = new Map<String, List<TestInformation>>() {
817 if (!_enqueueMoreWork(this)) _progress.allDone(); 834 if (!_enqueueMoreWork(this)) _progress.allDone();
818 browserUsed = '';
819 } 835 }
820 836
821 /** 837 /**
822 * Registers a TestSuite so that all of its tests will be run. 838 * Registers a TestSuite so that all of its tests will be run.
823 */ 839 */
824 void addTestSuite(TestSuite testSuite) { 840 void addTestSuite(TestSuite testSuite) {
825 _activeTestListers++; 841 _activeTestListers++;
826 testSuite.forEachTest(_runTest, _testCache, globalTemporaryDirectory, 842 testSuite.forEachTest(_runTest, _testCache, globalTemporaryDirectory,
827 _testListerDone); 843 _testListerDone);
828 } 844 }
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
884 } 900 }
885 } 901 }
886 902
887 /** 903 /**
888 * Perform any cleanup needed once all tests in a TestSuite have completed 904 * Perform any cleanup needed once all tests in a TestSuite have completed
889 * and notify our progress indicator that we are done. 905 * and notify our progress indicator that we are done.
890 */ 906 */
891 void _cleanupAndMarkDone() { 907 void _cleanupAndMarkDone() {
892 if (browserUsed != '') { 908 if (browserUsed != '') {
893 killZombieBrowsers(); 909 killZombieBrowsers();
910 if (_seleniumServer != null) {
911 _seleniumServer.kill();
912 }
894 } else { 913 } else {
895 _progress.allDone(); 914 _progress.allDone();
896 } 915 }
897 } 916 }
898 917
899 void _checkDone() { 918 void _checkDone() {
900 // When there are no more active test listers ask for more work 919 // When there are no more active test listers ask for more work
901 // from process queue users. 920 // from process queue users.
902 if (_activeTestListers == 0 && !_enqueueMoreWork(this)) { 921 if (_activeTestListers == 0 && !_enqueueMoreWork(this)) {
903 _progress.allTestsKnown(); 922 _progress.allTestsKnown();
(...skipping 18 matching lines...) Expand all
922 } 941 }
923 } else { 942 } else {
924 print('\nDeletion of temp dir $_temporaryDirectory failed.'); 943 print('\nDeletion of temp dir $_temporaryDirectory failed.');
925 } 944 }
926 _cleanupAndMarkDone(); 945 _cleanupAndMarkDone();
927 }; 946 };
928 } 947 }
929 } 948 }
930 } 949 }
931 } 950 }
951
952 /**
953 * True if we are using a browser + platform combination that needs the
954 * Selenium server jar.
955 */
956 bool get _needsSelenium() => new Platform().operatingSystem() == 'macos' &&
957 browserUsed == 'safari';
958
959 /** True if the Selenium Server is ready to be used. */
960 bool get _isSeleniumAvailable() => _seleniumServer != null ||
961 _seleniumAlreadyRunning;
962
963 /**
964 * Restart all the processes that have been waiting/stopped for the server to
965 * start up. If we just call this once we end up with a single-"threaded" run.
966 */
967 void resumeTesting() {
968 for (int i = 0; i < _maxProcesses; i++) _tryRunTest();
969 }
970
971 /** Start the Selenium Server jar, if appropriate for this platform. */
972 void _ensureSeleniumServerRunning() {
973 if (!_isSeleniumAvailable && !_startingServer) {
974 _startingServer = true;
975
976 // Check to see if the jar was already running before the program started.
977 String cmd = 'ps';
978 var arg = ['aux'];
979 if (new Platform().operatingSystem() == 'windows') {
980 cmd = 'tasklist';
981 arg.add('/v');
982 }
983 Process p = new Process.start(cmd, arg);
984 final StringInputStream stdoutStringStream =
985 new StringInputStream(p.stdout);
986 stdoutStringStream.onLine = () {
987 var line = stdoutStringStream.readLine();
988 while (null != line) {
989 if (const RegExp(@".*selenium-server-standalone.*").hasMatch(line)) {
990 _seleniumAlreadyRunning = true;
991 resumeTesting();
992 }
993 line = stdoutStringStream.readLine();
994 }
995 if (!_isSeleniumAvailable) {
996 _startSeleniumServer();
997 }
998 };
999 }
1000 }
932 1001
933 void _runTest(TestCase test) { 1002 void _runTest(TestCase test) {
934 if (test.configuration['component'] == 'webdriver') { 1003 if (test.usesWebDriver) {
935 browserUsed = test.configuration['browser']; 1004 browserUsed = test.configuration['browser'];
1005 if (_needsSelenium) _ensureSeleniumServerRunning();
936 } 1006 }
937 _progress.testAdded(); 1007 _progress.testAdded();
938 _tests.add(test); 1008 _tests.add(test);
939 _tryRunTest(); 1009 _tryRunTest();
940 } 1010 }
941 1011
1012 /**
1013 * Monitor the output of the Selenium server, to know when we are ready to
1014 * begin running tests.
1015 * source: Output(Stream) from the Java server.
1016 */
1017 Function makeSeleniumServerHandler(StringInputStream source) {
1018 return () {
1019 if (source.closed) return; // TODO(whesse): Remove when bug is fixed.
1020 var line = source.readLine();
1021 while (null != line) {
1022 if (const RegExp(@".*Started.*Server.*").hasMatch(line) ||
1023 const RegExp(@"Exception.*Selenium is already running.*").hasMatch(
1024 line)) {
1025 resumeTesting();
1026 }
1027 line = source.readLine();
1028 }
1029 };
1030 }
1031
1032 /**
1033 * For browser tests using Safari or Opera, we need to use the Selenium 1.0
1034 * Java server.
1035 */
1036 void _startSeleniumServer() {
1037 // Get the absolute path to the Selenium jar.
1038 String filePath = new Options().script;
1039 String pathSep = new Platform().pathSeparator();
1040 int index = filePath.lastIndexOf(pathSep);
1041 filePath = filePath.substring(0, index) + '${pathSep}testing${pathSep}';
1042 var dir = new Directory(filePath);
1043 dir.onFile = (String file) {
1044 if (const RegExp(@"selenium-server-standalone-.*\.jar").hasMatch(file)
1045 && _seleniumServer == null) {
1046 _seleniumServer = new Process.start('java', ['-jar', file]);
1047 // Heads up: there seems to an obscure data race of some form in
1048 // the VM between launching the server process and launching the test
1049 // tasks that disappears when you read IO (which is convenient, since
1050 // that is our condition for knowing that the server is ready).
1051 StringInputStream stdoutStringStream =
1052 new StringInputStream(_seleniumServer.stdout);
1053 StringInputStream stderrStringStream =
1054 new StringInputStream(_seleniumServer.stderr);
1055 stdoutStringStream.onLine =
1056 makeSeleniumServerHandler(stdoutStringStream);
1057 stderrStringStream.onLine =
1058 makeSeleniumServerHandler(stderrStringStream);
1059 }
1060 };
1061 dir.list();
1062 }
1063
942 void _terminateBatchRunners() { 1064 void _terminateBatchRunners() {
943 for (var runners in _batchProcesses.getValues()) { 1065 for (var runners in _batchProcesses.getValues()) {
944 for (var runner in runners) { 1066 for (var runner in runners) {
945 runner.terminate(); 1067 runner.terminate();
946 } 1068 }
947 } 1069 }
948 } 1070 }
949 1071
950 BatchRunnerProcess _getBatchRunner(TestCase test) { 1072 BatchRunnerProcess _getBatchRunner(TestCase test) {
951 // Start batch processes if needed 1073 // Start batch processes if needed
(...skipping 10 matching lines...) Expand all
962 for (var runner in runners) { 1084 for (var runner in runners) {
963 if (!runner.active) return runner; 1085 if (!runner.active) return runner;
964 } 1086 }
965 throw new Exception('Unable to find inactive batch runner.'); 1087 throw new Exception('Unable to find inactive batch runner.');
966 } 1088 }
967 1089
968 void _tryRunTest() { 1090 void _tryRunTest() {
969 _checkDone(); 1091 _checkDone();
970 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { 1092 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) {
971 TestCase test = _tests.removeFirst(); 1093 TestCase test = _tests.removeFirst();
972 if (_verbose) print(test.commands.last().commandLine);
973 if (_listTests) { 1094 if (_listTests) {
974 final String tab = '\t'; 1095 final String tab = '\t';
975 String outcomes = 1096 String outcomes =
976 Strings.join(new List.from(test.expectedOutcomes), ','); 1097 Strings.join(new List.from(test.expectedOutcomes), ',');
977 print(test.displayName + tab + outcomes + tab + test.isNegative + 1098 print(test.displayName + tab + outcomes + tab + test.isNegative +
978 tab + Strings.join(test.commands.last().arguments, tab)); 1099 tab + Strings.join(test.commands.last().arguments, tab));
979 return; 1100 return;
980 } 1101 }
1102 if (test.usesWebDriver && _needsSelenium && !_isSeleniumAvailable) {
1103 // The server is not ready to run Selenium tests. Put the test back in
1104 // the queue.
1105 _tests.addFirst(test);
1106 return;
1107 }
1108 if (_verbose) print(test.commands.last().commandLine);
981 _progress.start(test); 1109 _progress.start(test);
982 Function oldCallback = test.completedHandler; 1110 Function oldCallback = test.completedHandler;
983 Function wrapper = (TestCase test_arg) { 1111 Function wrapper = (TestCase test_arg) {
984 _numProcesses--; 1112 _numProcesses--;
985 _progress.done(test_arg); 1113 _progress.done(test_arg);
986 _tryRunTest(); 1114 _tryRunTest();
987 oldCallback(test_arg); 1115 oldCallback(test_arg);
988 }; 1116 };
989 test.completedHandler = wrapper; 1117 test.completedHandler = wrapper;
990 if (test.configuration['component'] == 'dartc' && 1118 if (test.configuration['component'] == 'dartc' &&
991 test.displayName != 'dartc/junit_tests') { 1119 test.displayName != 'dartc/junit_tests') {
992 _getBatchRunner(test).startTest(test); 1120 _getBatchRunner(test).startTest(test);
993 } else { 1121 } else {
994 // Once we've actually failed a test, technically, we wouldn't need to 1122 // Once we've actually failed a test, technically, we wouldn't need to
995 // bother retrying any subsequent tests since the bot is already red. 1123 // bother retrying any subsequent tests since the bot is already red.
996 // However, we continue to retry tests until we have actually failed 1124 // However, we continue to retry tests until we have actually failed
997 // four tests (arbitrarily chosen) for more debugable output, so that 1125 // four tests (arbitrarily chosen) for more debugable output, so that
998 // the developer doesn't waste his or her time trying to fix a bunch of 1126 // the developer doesn't waste his or her time trying to fix a bunch of
999 // tests that appear to be broken but were actually just flakes that 1127 // tests that appear to be broken but were actually just flakes that
1000 // didn't get retried because there had already been one failure. 1128 // didn't get retried because there had already been one failure.
1001 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; 1129 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests;
1002 new RunningProcess(test, allowRetry, this).start(); 1130 new RunningProcess(test, allowRetry, this).start();
1003 } 1131 }
1004 _numProcesses++; 1132 _numProcesses++;
1005 } 1133 }
1006 } 1134 }
1007 } 1135 }
OLDNEW
« no previous file with comments | « tools/testing/dart/test_options.dart ('k') | tools/testing/perf_testing/buildbot_browser_test_setup.sh » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698