| 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. |
| 11 */ | 11 */ |
| 12 library test_runner; | 12 library test_runner; |
| 13 | 13 |
| 14 import "dart:async"; | 14 import "dart:async"; |
| 15 import "dart:collection" show Queue; | 15 import "dart:collection" show Queue; |
| 16 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow | 16 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow |
| 17 // CommandOutput.exitCode in subclasses of CommandOutput. | 17 // CommandOutput.exitCode in subclasses of CommandOutput. |
| 18 import "dart:io" as io; | 18 import "dart:io" as io; |
| 19 import "dart:isolate"; | 19 import "dart:isolate"; |
| 20 import "dart:uri"; |
| 20 import "browser_controller.dart"; | 21 import "browser_controller.dart"; |
| 21 import "http_server.dart" as http_server; | 22 import "http_server.dart" as http_server; |
| 22 import "status_file_parser.dart"; | 23 import "status_file_parser.dart"; |
| 23 import "test_progress.dart"; | 24 import "test_progress.dart"; |
| 24 import "test_suite.dart"; | 25 import "test_suite.dart"; |
| 25 import "utils.dart"; | 26 import "utils.dart"; |
| 26 import 'record_and_replay.dart'; | 27 import 'record_and_replay.dart'; |
| 27 | 28 |
| 28 const int NO_TIMEOUT = 0; | 29 const int NO_TIMEOUT = 0; |
| 29 const int SLOW_TIMEOUT_MULTIPLIER = 4; | 30 const int SLOW_TIMEOUT_MULTIPLIER = 4; |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 114 // TODO(efortuna): Remove this when fixed (Issue 1306). | 115 // TODO(efortuna): Remove this when fixed (Issue 1306). |
| 115 executable = executable.replaceAll('/', '\\'); | 116 executable = executable.replaceAll('/', '\\'); |
| 116 } | 117 } |
| 117 var quotedArguments = []; | 118 var quotedArguments = []; |
| 118 arguments.forEach((argument) => quotedArguments.add('"$argument"')); | 119 arguments.forEach((argument) => quotedArguments.add('"$argument"')); |
| 119 commandLine = "\"$executable\" ${quotedArguments.join(' ')}"; | 120 commandLine = "\"$executable\" ${quotedArguments.join(' ')}"; |
| 120 } | 121 } |
| 121 | 122 |
| 122 String toString() => commandLine; | 123 String toString() => commandLine; |
| 123 | 124 |
| 124 Future<bool> get outputIsUpToDate => new Future.value(false); | 125 Future<bool> get outputIsUpToDate => new Future.immediate(false); |
| 125 io.Path get expectedOutputFile => null; | 126 io.Path get expectedOutputFile => null; |
| 126 bool get isPixelTest => false; | 127 bool get isPixelTest => false; |
| 127 } | 128 } |
| 128 | 129 |
| 129 class CompilationCommand extends Command { | 130 class CompilationCommand extends Command { |
| 130 String _outputFile; | 131 String _outputFile; |
| 131 bool _neverSkipCompilation; | 132 bool _neverSkipCompilation; |
| 132 List<Uri> _bootstrapDependencies; | 133 List<Uri> _bootstrapDependencies; |
| 133 | 134 |
| 134 CompilationCommand(this._outputFile, | 135 CompilationCommand(this._outputFile, |
| 135 this._neverSkipCompilation, | 136 this._neverSkipCompilation, |
| 136 this._bootstrapDependencies, | 137 this._bootstrapDependencies, |
| 137 String executable, | 138 String executable, |
| 138 List<String> arguments) | 139 List<String> arguments) |
| 139 : super(executable, arguments); | 140 : super(executable, arguments); |
| 140 | 141 |
| 141 Future<bool> get outputIsUpToDate { | 142 Future<bool> get outputIsUpToDate { |
| 142 if (_neverSkipCompilation) return new Future.value(false); | 143 if (_neverSkipCompilation) return new Future.immediate(false); |
| 143 | 144 |
| 144 Future<List<Uri>> readDepsFile(String path) { | 145 Future<List<Uri>> readDepsFile(String path) { |
| 145 var file = new io.File(new io.Path(path).toNativePath()); | 146 var file = new io.File(new io.Path(path).toNativePath()); |
| 146 if (!file.existsSync()) { | 147 if (!file.existsSync()) { |
| 147 return new Future.value(null); | 148 return new Future.immediate(null); |
| 148 } | 149 } |
| 149 return file.readAsLines().then((List<String> lines) { | 150 return file.readAsLines().then((List<String> lines) { |
| 150 var dependencies = new List<Uri>(); | 151 var dependencies = new List<Uri>(); |
| 151 for (var line in lines) { | 152 for (var line in lines) { |
| 152 line = line.trim(); | 153 line = line.trim(); |
| 153 if (line.length > 0) { | 154 if (line.length > 0) { |
| 154 dependencies.add(Uri.parse(line)); | 155 dependencies.add(new Uri(line)); |
| 155 } | 156 } |
| 156 } | 157 } |
| 157 return dependencies; | 158 return dependencies; |
| 158 }); | 159 }); |
| 159 } | 160 } |
| 160 | 161 |
| 161 return readDepsFile("$_outputFile.deps").then((dependencies) { | 162 return readDepsFile("$_outputFile.deps").then((dependencies) { |
| 162 if (dependencies != null) { | 163 if (dependencies != null) { |
| 163 dependencies.addAll(_bootstrapDependencies); | 164 dependencies.addAll(_bootstrapDependencies); |
| 164 var jsOutputLastModified = TestUtils.lastModifiedCache.getLastModified( | 165 var jsOutputLastModified = TestUtils.lastModifiedCache.getLastModified( |
| 165 new Uri(scheme: 'file', path: _outputFile)); | 166 new Uri.fromComponents(scheme: 'file', path: _outputFile)); |
| 166 if (jsOutputLastModified != null) { | 167 if (jsOutputLastModified != null) { |
| 167 for (var dependency in dependencies) { | 168 for (var dependency in dependencies) { |
| 168 var dependencyLastModified = | 169 var dependencyLastModified = |
| 169 TestUtils.lastModifiedCache.getLastModified(dependency); | 170 TestUtils.lastModifiedCache.getLastModified(dependency); |
| 170 if (dependencyLastModified == null || | 171 if (dependencyLastModified == null || |
| 171 dependencyLastModified.isAfter(jsOutputLastModified)) { | 172 dependencyLastModified.isAfter(jsOutputLastModified)) { |
| 172 return false; | 173 return false; |
| 173 } | 174 } |
| 174 } | 175 } |
| 175 return true; | 176 return true; |
| (...skipping 844 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1020 _runCommand(); | 1021 _runCommand(); |
| 1021 return completer.future; | 1022 return completer.future; |
| 1022 } | 1023 } |
| 1023 | 1024 |
| 1024 void _runCommand() { | 1025 void _runCommand() { |
| 1025 command.outputIsUpToDate.then((bool isUpToDate) { | 1026 command.outputIsUpToDate.then((bool isUpToDate) { |
| 1026 if (isUpToDate) { | 1027 if (isUpToDate) { |
| 1027 compilationSkipped = true; | 1028 compilationSkipped = true; |
| 1028 _commandComplete(0); | 1029 _commandComplete(0); |
| 1029 } else { | 1030 } else { |
| 1030 var processEnvironment = _createProcessEnvironment(); | 1031 var processOptions = _createProcessOptions(); |
| 1031 var commandArguments = _modifySeleniumTimeout(command.arguments, | 1032 var commandArguments = _modifySeleniumTimeout(command.arguments, |
| 1032 testCase.timeout); | 1033 testCase.timeout); |
| 1033 Future processFuture = | 1034 Future processFuture = io.Process.start(command.executable, |
| 1034 io.Process.start(command.executable, | 1035 commandArguments, |
| 1035 commandArguments, | 1036 processOptions); |
| 1036 environment: processEnvironment); | |
| 1037 processFuture.then((io.Process process) { | 1037 processFuture.then((io.Process process) { |
| 1038 // Close stdin so that tests that try to block on input will fail. | 1038 // Close stdin so that tests that try to block on input will fail. |
| 1039 process.stdin.close(); | 1039 process.stdin.close(); |
| 1040 void timeoutHandler() { | 1040 void timeoutHandler() { |
| 1041 timedOut = true; | 1041 timedOut = true; |
| 1042 if (process != null) { | 1042 if (process != null) { |
| 1043 process.kill(); | 1043 process.kill(); |
| 1044 } | 1044 } |
| 1045 } | 1045 } |
| 1046 process.exitCode.then(_commandComplete); | 1046 process.exitCode.then(_commandComplete); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1080 stderr, | 1080 stderr, |
| 1081 new DateTime.now().difference(startTime), | 1081 new DateTime.now().difference(startTime), |
| 1082 compilationSkipped); | 1082 compilationSkipped); |
| 1083 return commandOutput; | 1083 return commandOutput; |
| 1084 } | 1084 } |
| 1085 | 1085 |
| 1086 void _drainStream(Stream<List<int>> source, List<int> destination) { | 1086 void _drainStream(Stream<List<int>> source, List<int> destination) { |
| 1087 source.listen(destination.addAll); | 1087 source.listen(destination.addAll); |
| 1088 } | 1088 } |
| 1089 | 1089 |
| 1090 Map<String, String> _createProcessEnvironment() { | 1090 io.ProcessOptions _createProcessOptions() { |
| 1091 var baseEnvironment = command.environment != null ? | 1091 var baseEnvironment = command.environment != null ? |
| 1092 command.environment : io.Platform.environment; | 1092 command.environment : io.Platform.environment; |
| 1093 var environment = new Map<String, String>.from(baseEnvironment); | 1093 io.ProcessOptions options = new io.ProcessOptions(); |
| 1094 environment['DART_CONFIGURATION'] = | 1094 options.environment = new Map<String, String>.from(baseEnvironment); |
| 1095 options.environment['DART_CONFIGURATION'] = |
| 1095 TestUtils.configurationDir(testCase.configuration); | 1096 TestUtils.configurationDir(testCase.configuration); |
| 1096 | 1097 |
| 1097 for (var excludedEnvironmentVariable in EXCLUDED_ENVIRONMENT_VARIABLES) { | 1098 for (var excludedEnvironmentVariable in EXCLUDED_ENVIRONMENT_VARIABLES) { |
| 1098 environment.remove(excludedEnvironmentVariable); | 1099 options.environment.remove(excludedEnvironmentVariable); |
| 1099 } | 1100 } |
| 1100 | 1101 |
| 1101 return environment; | 1102 return options; |
| 1102 } | 1103 } |
| 1103 } | 1104 } |
| 1104 | 1105 |
| 1105 class BatchRunnerProcess { | 1106 class BatchRunnerProcess { |
| 1106 Command _command; | 1107 Command _command; |
| 1107 String _executable; | 1108 String _executable; |
| 1108 List<String> _batchArguments; | 1109 List<String> _batchArguments; |
| 1109 | 1110 |
| 1110 io.Process _process; | 1111 io.Process _process; |
| 1111 Completer _stdoutCompleter; | 1112 Completer _stdoutCompleter; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1151 doStartTest(testCase); | 1152 doStartTest(testCase); |
| 1152 }); | 1153 }); |
| 1153 }; | 1154 }; |
| 1154 _process.kill(); | 1155 _process.kill(); |
| 1155 } else { | 1156 } else { |
| 1156 doStartTest(testCase); | 1157 doStartTest(testCase); |
| 1157 } | 1158 } |
| 1158 } | 1159 } |
| 1159 | 1160 |
| 1160 Future terminate() { | 1161 Future terminate() { |
| 1161 if (_process == null) return new Future.value(true); | 1162 if (_process == null) return new Future.immediate(true); |
| 1162 Completer completer = new Completer(); | 1163 Completer completer = new Completer(); |
| 1163 Timer killTimer; | 1164 Timer killTimer; |
| 1164 _processExitHandler = (_) { | 1165 _processExitHandler = (_) { |
| 1165 if (killTimer != null) killTimer.cancel(); | 1166 if (killTimer != null) killTimer.cancel(); |
| 1166 completer.complete(true); | 1167 completer.complete(true); |
| 1167 }; | 1168 }; |
| 1168 if (_isWebDriver) { | 1169 if (_isWebDriver) { |
| 1169 // Use a graceful shutdown so our Selenium script can close | 1170 // Use a graceful shutdown so our Selenium script can close |
| 1170 // the open browser processes. On Windows, signals do not exist | 1171 // the open browser processes. On Windows, signals do not exist |
| 1171 // and a kill is a hard kill. | 1172 // and a kill is a hard kill. |
| (...skipping 483 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1655 testRunner.logger = DebugLogger.info; | 1656 testRunner.logger = DebugLogger.info; |
| 1656 _browserTestRunners[runtime] = testRunner; | 1657 _browserTestRunners[runtime] = testRunner; |
| 1657 return testRunner.start().then((started) { | 1658 return testRunner.start().then((started) { |
| 1658 if (started) { | 1659 if (started) { |
| 1659 return testRunner; | 1660 return testRunner; |
| 1660 } | 1661 } |
| 1661 print("Issue starting browser test runner"); | 1662 print("Issue starting browser test runner"); |
| 1662 io.exit(1); | 1663 io.exit(1); |
| 1663 }); | 1664 }); |
| 1664 } | 1665 } |
| 1665 return new Future.value(_browserTestRunners[runtime]); | 1666 return new Future.immediate(_browserTestRunners[runtime]); |
| 1666 } | 1667 } |
| 1667 | 1668 |
| 1668 void _startBrowserControllerTest(var test) { | 1669 void _startBrowserControllerTest(var test) { |
| 1669 var callback = (var output, var duration) { | 1670 var callback = (var output, var duration) { |
| 1670 var nextCommandIndex = test.commandOutputs.keys.length; | 1671 var nextCommandIndex = test.commandOutputs.keys.length; |
| 1671 new CommandOutput.fromCase(test, | 1672 new CommandOutput.fromCase(test, |
| 1672 test.commands[nextCommandIndex], | 1673 test.commands[nextCommandIndex], |
| 1673 0, | 1674 0, |
| 1674 false, | 1675 false, |
| 1675 output == "TIMEOUT", | 1676 output == "TIMEOUT", |
| (...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1909 } | 1910 } |
| 1910 } | 1911 } |
| 1911 | 1912 |
| 1912 void eventAllTestsDone() { | 1913 void eventAllTestsDone() { |
| 1913 for (var listener in _eventListener) { | 1914 for (var listener in _eventListener) { |
| 1914 listener.allDone(); | 1915 listener.allDone(); |
| 1915 } | 1916 } |
| 1916 } | 1917 } |
| 1917 } | 1918 } |
| 1918 | 1919 |
| OLD | NEW |