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