| OLD | NEW |
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, 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 #library("test_runner"); | 5 #library("test_runner"); |
| 6 | 6 |
| 7 #import("status_file_parser.dart"); | 7 #import("status_file_parser.dart"); |
| 8 #import("test_progress.dart"); | 8 #import("test_progress.dart"); |
| 9 #import("test_suite.dart"); | 9 #import("test_suite.dart"); |
| 10 | 10 |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 94 bool get didFail() => exitCode != 0 && !hasCrashed; | 94 bool get didFail() => exitCode != 0 && !hasCrashed; |
| 95 | 95 |
| 96 // Reverse result of a negative test. | 96 // Reverse result of a negative test. |
| 97 bool get hasFailed() => (testCase.isNegative ? !didFail : didFail); | 97 bool get hasFailed() => (testCase.isNegative ? !didFail : didFail); |
| 98 } | 98 } |
| 99 | 99 |
| 100 | 100 |
| 101 class RunningProcess { | 101 class RunningProcess { |
| 102 Process process; | 102 Process process; |
| 103 TestCase testCase; | 103 TestCase testCase; |
| 104 int timeout; | |
| 105 bool timedOut = false; | 104 bool timedOut = false; |
| 106 Date startTime; | 105 Date startTime; |
| 107 Timer timeoutTimer; | 106 Timer timeoutTimer; |
| 108 List<String> stdout; | 107 List<String> stdout; |
| 109 List<String> stderr; | 108 List<String> stderr; |
| 110 List<Function> handlers; | 109 List<Function> handlers; |
| 111 | 110 |
| 112 RunningProcess(this.testCase, [this.timeout = NO_TIMEOUT]); | 111 RunningProcess(this.testCase); |
| 113 | 112 |
| 114 void exitHandler(int exitCode) { | 113 void exitHandler(int exitCode) { |
| 115 new TestOutput(testCase, exitCode, timedOut, stdout, | 114 new TestOutput(testCase, exitCode, timedOut, stdout, |
| 116 stderr, new Date.now().difference(startTime)); | 115 stderr, new Date.now().difference(startTime)); |
| 117 process.close(); | 116 process.close(); |
| 118 timeoutTimer.cancel(); | 117 timeoutTimer.cancel(); |
| 119 testCase.completed(); | 118 testCase.completed(); |
| 120 } | 119 } |
| 121 | 120 |
| 122 void makeReadHandler(StringInputStream source, List<String> destination) { | 121 void makeReadHandler(StringInputStream source, List<String> destination) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 140 InputStream stdoutStream = process.stdout; | 139 InputStream stdoutStream = process.stdout; |
| 141 InputStream stderrStream = process.stderr; | 140 InputStream stderrStream = process.stderr; |
| 142 stdout = new List<String>(); | 141 stdout = new List<String>(); |
| 143 stderr = new List<String>(); | 142 stderr = new List<String>(); |
| 144 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream); | 143 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream); |
| 145 StringInputStream stderrStringStream = new StringInputStream(stderrStream); | 144 StringInputStream stderrStringStream = new StringInputStream(stderrStream); |
| 146 stdoutStringStream.dataHandler = | 145 stdoutStringStream.dataHandler = |
| 147 makeReadHandler(stdoutStringStream, stdout); | 146 makeReadHandler(stdoutStringStream, stdout); |
| 148 stderrStringStream.dataHandler = | 147 stderrStringStream.dataHandler = |
| 149 makeReadHandler(stderrStringStream, stderr); | 148 makeReadHandler(stderrStringStream, stderr); |
| 150 if (timeout != NO_TIMEOUT) { | 149 timeoutTimer = new Timer(timeoutHandler, 1000 * testCase.timeout, false); |
| 151 timeoutTimer = new Timer(timeoutHandler, 1000 * timeout, false); | |
| 152 } | |
| 153 } | 150 } |
| 154 | 151 |
| 155 void timeoutHandler(Timer unusedTimer) { | 152 void timeoutHandler(Timer unusedTimer) { |
| 156 timedOut = true; | 153 timedOut = true; |
| 157 process.kill(); | 154 process.kill(); |
| 158 } | 155 } |
| 159 } | 156 } |
| 160 | 157 |
| 161 | 158 |
| 159 class DartcBatchRunnerProcess { |
| 160 String _executable; |
| 161 |
| 162 Process _process; |
| 163 StringInputStream _stdoutStream; |
| 164 StringInputStream _stderrStream; |
| 165 |
| 166 TestCase _currentTest; |
| 167 StringBuffer _testStdout; |
| 168 StringBuffer _testStderr; |
| 169 Date _startTime; |
| 170 Timer _timer; |
| 171 |
| 172 DartcBatchRunnerProcess(String this._executable) { |
| 173 _startProcess(); |
| 174 } |
| 175 |
| 176 bool get active() => _currentTest != null; |
| 177 |
| 178 void startTest(TestCase testCase) { |
| 179 _startTime = new Date.now(); |
| 180 _currentTest = testCase; |
| 181 _testStdout = new List<String>(); |
| 182 _testStderr = new List<String>(); |
| 183 _stdoutStream.dataHandler = _readOutput(_stdoutStream, _testStdout); |
| 184 _stderrStream.dataHandler = _readOutput(_stderrStream, _testStderr); |
| 185 _timer = new Timer(_timeoutHandler(testCase), |
| 186 testCase.timeout * 1000, |
| 187 false); |
| 188 _process.stdin.write(_createArgumentsLine(testCase.arguments).charCodes()); |
| 189 } |
| 190 |
| 191 void terminate() { |
| 192 _process.exitHandler = (exitCode) { |
| 193 _process.close(); |
| 194 }; |
| 195 _process.kill(); |
| 196 } |
| 197 |
| 198 String _createArgumentsLine(List<String> arguments) { |
| 199 var buffer = new StringBuffer(); |
| 200 for (var i = 0; i < arguments.length; i++) { |
| 201 buffer.add("${arguments[i]} "); |
| 202 } |
| 203 buffer.add("\n"); |
| 204 return buffer.toString(); |
| 205 } |
| 206 |
| 207 int _reportResult(String output) { |
| 208 var test = _currentTest; |
| 209 _currentTest = null; |
| 210 |
| 211 // output = '>>> TEST {PASS, FAIL, OK, CRASH, FAIL, TIMEOUT}' |
| 212 var outcome = output.split(" ")[2]; |
| 213 var exitCode = 0; |
| 214 if (outcome == "CRASH") exitCode = -10; |
| 215 if (outcome == "FAIL" || outcome == "TIMEOUT") exitCode = 1; |
| 216 new TestOutput(test, exitCode, outcome == "TIMEOUT", _testStdout, |
| 217 _testStderr, new Date.now().difference(_startTime)); |
| 218 test.completed(); |
| 219 } |
| 220 |
| 221 void _readOutput(StringInputStream stream, List<String> buffer) { |
| 222 return () { |
| 223 var status; |
| 224 var line = stream.readLine(); |
| 225 // Drain the input stream to get the error output. |
| 226 while (line != null) { |
| 227 if (line.startsWith('>>> TEST')) { |
| 228 status = line; |
| 229 } else if (line.startsWith('>>> BATCH START')) { |
| 230 // ignore |
| 231 } else if (line.startsWith('>>> ')) { |
| 232 throw new Exception('Unexpected command from dartc batch runner.'); |
| 233 } else { |
| 234 buffer.add(line); |
| 235 } |
| 236 line = stream.readLine(); |
| 237 } |
| 238 if (status != null) { |
| 239 _timer.cancel(); |
| 240 // For crashing processes, let the exit handler deal with it. |
| 241 if (!status.contains("CRASH")) { |
| 242 _reportResult(status); |
| 243 } |
| 244 } |
| 245 }; |
| 246 } |
| 247 |
| 248 void _exitHandler(exitCode) { |
| 249 if (_timer != null) _timer.cancel(); |
| 250 _process.close(); |
| 251 _startProcess(); |
| 252 _reportResult(">>> TEST CRASH"); |
| 253 } |
| 254 |
| 255 void _timeoutHandler(TestCase test) { |
| 256 return (ignore) { |
| 257 _process.exitHandler = (exitCode) { |
| 258 _process.close(); |
| 259 _startProcess(); |
| 260 _reportResult(">>> TEST TIMEOUT"); |
| 261 }; |
| 262 _process.kill(); |
| 263 }; |
| 264 } |
| 265 |
| 266 void _startProcess() { |
| 267 _process = new Process(_executable, ['-batch']); |
| 268 _stdoutStream = new StringInputStream(_process.stdout); |
| 269 _stderrStream = new StringInputStream(_process.stderr); |
| 270 _testStdout = new List<String>(); |
| 271 _testStderr = new List<String>(); |
| 272 _stdoutStream.dataHandler = _readOutput(_stdoutStream, _testStdout); |
| 273 _stderrStream.dataHandler = _readOutput(_stderrStream, _testStderr); |
| 274 _process.exitHandler = _exitHandler; |
| 275 _process.start(); |
| 276 } |
| 277 } |
| 278 |
| 279 |
| 162 class ProcessQueue { | 280 class ProcessQueue { |
| 163 int _numProcesses = 0; | 281 int _numProcesses = 0; |
| 164 int _activeTestListers = 0; | 282 int _activeTestListers = 0; |
| 165 final int _maxProcesses; | 283 int _maxProcesses; |
| 166 Queue<TestCase> _tests; | 284 Queue<TestCase> _tests; |
| 167 ProgressIndicator _progress; | 285 ProgressIndicator _progress; |
| 168 | 286 |
| 287 // For dartc batch processing we keep a list of batch processes for |
| 288 // each of debug and release mode. If dartc tests are run in both |
| 289 // release and debug mode this will spawn many processes but only |
| 290 // half of them will be active at a time. |
| 291 Map<String, List<DartcBatchRunnerProcess>> _batchProcessesMap; |
| 292 |
| 169 ProcessQueue(int this._maxProcesses, | 293 ProcessQueue(int this._maxProcesses, |
| 170 String progress, | 294 String progress, |
| 171 Date start_time) | 295 Date start_time) |
| 172 : _tests = new Queue<TestCase>(), | 296 : _tests = new Queue<TestCase>(), |
| 173 _progress = new ProgressIndicator.fromName(progress, start_time); | 297 _progress = new ProgressIndicator.fromName(progress, start_time), |
| 174 | 298 _batchProcessesMap = new Map<String, List<DartcBatchRunnerProcess>>() { |
| 175 addTestSuite(TestSuite testSuite) { | 299 _maxProcesses = _maxProcesses; |
| 300 } |
| 301 |
| 302 void addTestSuite(TestSuite testSuite) { |
| 176 _activeTestListers++; | 303 _activeTestListers++; |
| 177 testSuite.forEachTest(_runTest, _testListerDone); | 304 testSuite.forEachTest(_runTest, _testListerDone); |
| 178 } | 305 } |
| 179 | 306 |
| 180 _testListerDone() { | 307 void _testListerDone() { |
| 181 _activeTestListers--; | 308 _activeTestListers--; |
| 182 _checkDone(); | 309 _checkDone(); |
| 183 } | 310 } |
| 184 | 311 |
| 185 _checkDone() { | 312 void _checkDone() { |
| 186 if (_activeTestListers == 0 && _tests.isEmpty() && _numProcesses == 0) { | 313 if (_activeTestListers == 0 && _tests.isEmpty() && _numProcesses == 0) { |
| 314 _terminateDartcBatchRunners(); |
| 187 _progress.allDone(); | 315 _progress.allDone(); |
| 188 } | 316 } |
| 189 } | 317 } |
| 190 | 318 |
| 191 _runTest(TestCase test) { | 319 void _runTest(TestCase test) { |
| 192 _progress.testAdded(); | 320 _progress.testAdded(); |
| 193 _tests.add(test); | 321 _tests.add(test); |
| 194 _tryRunTest(); | 322 _tryRunTest(); |
| 195 } | 323 } |
| 196 | 324 |
| 197 _tryRunTest() { | 325 void _terminateDartcBatchRunners() { |
| 326 _batchProcessesMap.forEach((key, value) { |
| 327 for (int i = 0; i < value.length; i++) { |
| 328 value[i].terminate(); |
| 329 } |
| 330 }); |
| 331 } |
| 332 |
| 333 DartcBatchRunnerProcess _getDartcBatchRunnerProcess(TestCase test) { |
| 334 var batchProcesses = _batchProcessesMap[test.executablePath]; |
| 335 if (batchProcesses == null) { |
| 336 // Dartc batch processing is heavy. Scale down the number of |
| 337 // concurrent tasks to be no more than the actual number of |
| 338 // processors even when running dartc benchmarks in both debug |
| 339 // and release mode. |
| 340 var processors = new Platform().numberOfProcessors(); |
| 341 if (_maxProcesses >= (processors / 2)) { |
| 342 _maxProcesses = (processors / 2).toInt(); |
| 343 } |
| 344 batchProcesses = new List<DartcBatchRunnerProcess>(_maxProcesses); |
| 345 _batchProcessesMap[test.executablePath] = batchProcesses; |
| 346 for (int i = 0; i < _maxProcesses; i++) { |
| 347 batchProcesses[i] = new DartcBatchRunnerProcess(test.executablePath); |
| 348 } |
| 349 } |
| 350 for (int i = 0; i < batchProcesses.length; i++) { |
| 351 var runner = batchProcesses[i]; |
| 352 if (!runner.active) return runner; |
| 353 } |
| 354 throw new Exception('Unable to find inactive batch runner.'); |
| 355 } |
| 356 |
| 357 void _tryRunTest() { |
| 198 _checkDone(); | 358 _checkDone(); |
| 199 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { | 359 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { |
| 200 TestCase test = _tests.removeFirst(); | 360 TestCase test = _tests.removeFirst(); |
| 201 _progress.start(test); | 361 _progress.start(test); |
| 202 Function oldCallback = test.completedHandler; | 362 Function oldCallback = test.completedHandler; |
| 203 Function wrapper = (TestCase test_arg) { | 363 Function wrapper = (TestCase test_arg) { |
| 204 _numProcesses--; | 364 _numProcesses--; |
| 205 _progress.done(test_arg); | 365 _progress.done(test_arg); |
| 206 _tryRunTest(); | 366 _tryRunTest(); |
| 207 oldCallback(test_arg); | 367 oldCallback(test_arg); |
| 208 }; | 368 }; |
| 209 test.completedHandler = wrapper; | 369 test.completedHandler = wrapper; |
| 210 new RunningProcess(test, test.timeout).start(); | 370 if (test.executablePath.contains('dartc_test')) { |
| 371 _getDartcBatchRunnerProcess(test).startTest(test); |
| 372 } else { |
| 373 new RunningProcess(test).start(); |
| 374 } |
| 211 _numProcesses++; | 375 _numProcesses++; |
| 212 } | 376 } |
| 213 } | 377 } |
| 214 } | 378 } |
| OLD | NEW |