Chromium Code Reviews| 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:io"); | 14 #import("dart:io"); |
| 15 #import("status_file_parser.dart"); | 15 #import("status_file_parser.dart"); |
| 16 #import("test_progress.dart"); | 16 #import("test_progress.dart"); |
| 17 #import("test_suite.dart"); | 17 #import("test_suite.dart"); |
| 18 | 18 |
| 19 final int NO_TIMEOUT = 0; | 19 final int NO_TIMEOUT = 0; |
| 20 | 20 |
| 21 /** A command executed as a step in a test case. */ | |
| 22 class Command { | |
| 23 /** Path to the executable of this command. */ | |
| 24 String executable; | |
| 25 | |
| 26 /** Command line arguments to the executable. */ | |
| 27 List<String> arguments; | |
| 28 | |
| 29 /** The actual command line that will be executed. */ | |
| 30 String commandLine; | |
| 31 | |
| 32 Command(this.executable, this.arguments) { | |
| 33 commandLine = "$executable ${Strings.join(arguments, ' ')}"; | |
| 34 } | |
| 35 } | |
| 21 | 36 |
| 22 /** | 37 /** |
| 23 * TestCase contains all the information needed to run a test and evaluate | 38 * TestCase contains all the information needed to run a test and evaluate |
| 24 * its output. Running a test involves starting a separate process, with | 39 * its output. Running a test involves starting a separate process, with |
| 25 * the executable and arguments given by the TestCase, and recording its | 40 * the executable and arguments given by the TestCase, and recording its |
| 26 * stdout and stderr output streams, and its exit code. TestCase only | 41 * stdout and stderr output streams, and its exit code. TestCase only |
| 27 * contains static information about the test; actually running the test is | 42 * contains static information about the test; actually running the test is |
| 28 * performed by [ProcessQueue] using a [RunningProcess] object. | 43 * performed by [ProcessQueue] using a [RunningProcess] object. |
| 29 * | 44 * |
| 30 * The output information is stored in a [TestOutput] instance contained | 45 * The output information is stored in a [TestOutput] instance contained |
| 31 * in the TestCase. The TestOutput instance is responsible for evaluating | 46 * in the TestCase. The TestOutput instance is responsible for evaluating |
| 32 * if the test has passed, failed, crashed, or timed out, and the TestCase | 47 * if the test has passed, failed, crashed, or timed out, and the TestCase |
| 33 * has information about what the expected result of the test should be. | 48 * has information about what the expected result of the test should be. |
| 34 * | 49 * |
| 35 * The TestCase has a callback function, [completedHandler], that is run when | 50 * The TestCase has a callback function, [completedHandler], that is run when |
| 36 * the test is completed. | 51 * the test is completed. |
| 37 */ | 52 */ |
| 38 class TestCase { | 53 class TestCase { |
| 39 String executablePath; | 54 /** |
| 40 List<String> arguments; | 55 * A list of commands to execute. Most test cases have a single command. Frog |
| 56 * tests have two commands, one to compilate the source and another to execute | |
| 57 * it. Some isolate tests might even have three, if they require compiling | |
| 58 * multiple sources that are run in isolation. | |
| 59 */ | |
| 60 final List<Command> commands; | |
| 61 | |
| 41 Map configuration; | 62 Map configuration; |
| 42 String commandLine; | |
| 43 String displayName; | 63 String displayName; |
| 44 TestOutput output; | 64 TestOutput output; |
| 45 bool isNegative; | 65 bool isNegative; |
| 46 Set<String> expectedOutcomes; | 66 Set<String> expectedOutcomes; |
| 47 Function completedHandler; | 67 Function completedHandler; |
| 48 | 68 |
| 49 TestCase(this.displayName, | 69 TestCase(this.displayName, |
| 50 this.executablePath, | 70 this.commands, |
| 51 this.arguments, | |
| 52 this.configuration, | 71 this.configuration, |
| 53 this.completedHandler, | 72 this.completedHandler, |
| 54 this.expectedOutcomes, | 73 this.expectedOutcomes, |
| 55 [this.isNegative = false]) { | 74 [this.isNegative = false]) { |
| 56 if (!isNegative) { | 75 if (!isNegative) { |
| 57 this.isNegative = displayName.contains("NegativeTest"); | 76 this.isNegative = displayName.contains("NegativeTest"); |
| 58 } | 77 } |
| 59 commandLine = "$executablePath ${Strings.join(arguments, ' ')}"; | |
| 60 | 78 |
| 61 // Special command handling. If a special command is specified | 79 // Special command handling. If a special command is specified |
| 62 // we have to completely rewrite the command that we are using. | 80 // we have to completely rewrite the command that we are using. |
| 63 // We generate a new command-line that is the special command | 81 // We generate a new command-line that is the special command |
| 64 // where we replace '@' with the original command. | 82 // where we replace '@' with the original command. |
| 65 var specialCommand = configuration['special-command']; | 83 var specialCommand = configuration['special-command']; |
| 66 if (!specialCommand.isEmpty()) { | 84 if (!specialCommand.isEmpty()) { |
| 67 Expect.isTrue(specialCommand.contains('@'), | 85 Expect.isTrue(specialCommand.contains('@'), |
| 68 "special-command must contain a '@' char"); | 86 "special-command must contain a '@' char"); |
| 69 var specialCommandSplit = specialCommand.split('@'); | 87 var specialCommandSplit = specialCommand.split('@'); |
| 70 var prefix = specialCommandSplit[0]; | 88 var prefix = specialCommandSplit[0]; |
| 71 var suffix = specialCommandSplit[1]; | 89 var suffix = specialCommandSplit[1]; |
| 72 commandLine = '$prefix $commandLine $suffix'; | 90 List<Command> newCommands = []; |
| 73 var newArguments = []; | 91 for (Command c in commands) { |
| 74 if (prefix.length > 0) { | 92 var newExecutablePath; |
| 75 var prefixSplit = prefix.split(' '); | 93 var newArguments = []; |
| 76 var newExecutablePath = prefixSplit[0]; | 94 |
| 77 for (int i = 1; i < prefixSplit.length; i++) { | 95 if (prefix.length > 0) { |
| 78 var current = prefixSplit[i]; | 96 var prefixSplit = prefix.split(' '); |
| 79 if (!current.isEmpty()) newArguments.add(current); | 97 newExecutablePath = prefixSplit[0]; |
| 98 for (int i = 1; i < prefixSplit.length; i++) { | |
| 99 var current = prefixSplit[i]; | |
| 100 if (!current.isEmpty()) newArguments.add(current); | |
| 101 } | |
| 102 newArguments.add(c.executable); | |
| 80 } | 103 } |
| 81 newArguments.add(executablePath); | 104 newArguments.addAll(arguments); |
| 82 executablePath = newExecutablePath; | 105 var suffixSplit = suffix.split(' '); |
| 106 suffixSplit.forEach((e) { | |
| 107 if (!e.isEmpty()) newArguments.add(e); | |
| 108 }); | |
| 109 final newCommand = new Command(newExecutablePath, newArguments); | |
| 110 newCommands.add(newCommand); | |
| 111 Expect.stringEquals('$prefix ${c.commandLine} $suffix', | |
| 112 newCommand.commandLine); | |
| 83 } | 113 } |
| 84 newArguments.addAll(arguments); | 114 commands = newCommand; |
| 85 var suffixSplit = suffix.split(' '); | |
| 86 suffixSplit.forEach((e) { | |
| 87 if (!e.isEmpty()) newArguments.add(e); | |
| 88 }); | |
| 89 arguments = newArguments; | |
| 90 } | 115 } |
| 91 } | 116 } |
| 92 | 117 |
| 93 int get timeout() => configuration['timeout']; | 118 int get timeout() => configuration['timeout']; |
| 94 | 119 |
| 95 String get configurationString() { | 120 String get configurationString() { |
| 96 final component = configuration['component']; | 121 final component = configuration['component']; |
| 97 final mode = configuration['mode']; | 122 final mode = configuration['mode']; |
| 98 final arch = configuration['arch']; | 123 final arch = configuration['arch']; |
| 99 return "$component ${mode}_$arch"; | 124 return "$component ${mode}_$arch"; |
| 100 } | 125 } |
| 101 | 126 |
| 102 List<String> get batchRunnerArguments() => ['-batch']; | 127 List<String> get batchRunnerArguments() => ['-batch']; |
| 103 List<String> get batchTestArguments() => arguments; | 128 List<String> get batchTestArguments() => commands.last().arguments; |
| 104 | 129 |
| 105 void completed() { completedHandler(this); } | 130 void completed() { completedHandler(this); } |
| 106 } | 131 } |
| 107 | 132 |
| 108 | 133 |
| 109 /** | 134 /** |
| 110 * BrowserTestCase has an extra compilation command that is run in a separate | 135 * BrowserTestCase has an extra compilation command that is run in a separate |
| 111 * process, before the regular test is run as in the base class [TestCase]. | 136 * process, before the regular test is run as in the base class [TestCase]. |
| 112 * If the compilation command fails, then the rest of the test is not run. | 137 * If the compilation command fails, then the rest of the test is not run. |
| 113 */ | 138 */ |
| 114 class BrowserTestCase extends TestCase { | 139 class BrowserTestCase extends TestCase { |
| 115 /** | 140 /** |
| 116 * The executable that is run in a new process in the compilation phase. | |
| 117 */ | |
| 118 String compilerPath; | |
| 119 /** | |
| 120 * The arguments for the compilation command. | |
| 121 */ | |
| 122 List<String> compilerArguments; | |
| 123 /** | |
| 124 * Indicates the number of potential retries remaining, to compensate for | 141 * Indicates the number of potential retries remaining, to compensate for |
| 125 * flaky browser tests. | 142 * flaky browser tests. |
| 126 */ | 143 */ |
| 127 bool numRetries; | 144 bool numRetries; |
| 128 | 145 |
| 129 BrowserTestCase(displayName, | 146 BrowserTestCase(displayName, commands, configuration, completedHandler, |
| 130 this.compilerPath, | 147 expectedOutcomes, [isNegative = false]) |
| 131 this.compilerArguments, | 148 : super(displayName, commands, configuration, completedHandler, |
| 132 executablePath, | 149 expectedOutcomes, isNegative) { |
| 133 arguments, | |
| 134 configuration, | |
| 135 completedHandler, | |
| 136 expectedOutcomes, | |
| 137 [isNegative = false]) : super(displayName, | |
| 138 executablePath, | |
| 139 arguments, | |
| 140 configuration, | |
| 141 completedHandler, | |
| 142 expectedOutcomes, | |
| 143 isNegative) { | |
| 144 if (compilerPath != null) { | |
| 145 commandLine = 'execution command: $commandLine'; | |
| 146 String compilationCommand = | |
| 147 '$compilerPath ${Strings.join(compilerArguments, " ")}'; | |
| 148 commandLine = 'compilation command: $compilationCommand\n$commandLine'; | |
| 149 } | |
| 150 numRetries = 2; // Allow two retries to compensate for flaky browser tests. | 150 numRetries = 2; // Allow two retries to compensate for flaky browser tests. |
| 151 } | 151 } |
| 152 | 152 |
| 153 List<String> get batchRunnerArguments() => [arguments[0], '--batch']; | 153 List<String> get _lastArguments() => command.last().arguments; |
| 154 | |
| 155 List<String> get batchRunnerArguments() => [_lastArguments[0], '--batch']; | |
| 156 | |
| 154 List<String> get batchTestArguments() => | 157 List<String> get batchTestArguments() => |
| 155 arguments.getRange(1, arguments.length - 1); | 158 _lastArguments.getRange(1, _lastArguments.length - 1); |
| 156 } | 159 } |
| 157 | 160 |
| 158 | 161 |
| 159 /** | 162 /** |
| 160 * TestOutput records the output of a completed test: the process's exit code, | 163 * TestOutput records the output of a completed test: the process's exit code, |
| 161 * the standard output and standard error, whether the process timed out, and | 164 * the standard output and standard error, whether the process timed out, and |
| 162 * the time the process took to run. It also contains a pointer to the | 165 * the time the process took to run. It also contains a pointer to the |
| 163 * [TestCase] this is the output of. | 166 * [TestCase] this is the output of. |
| 164 */ | 167 */ |
| 165 class TestOutput { | 168 class TestOutput { |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 253 Process process; | 256 Process process; |
| 254 TestCase testCase; | 257 TestCase testCase; |
| 255 bool timedOut = false; | 258 bool timedOut = false; |
| 256 Date startTime; | 259 Date startTime; |
| 257 Timer timeoutTimer; | 260 Timer timeoutTimer; |
| 258 List<String> stdout; | 261 List<String> stdout; |
| 259 List<String> stderr; | 262 List<String> stderr; |
| 260 List<Function> handlers; | 263 List<Function> handlers; |
| 261 bool allowRetries = false; | 264 bool allowRetries = false; |
| 262 | 265 |
| 266 /** Which command of [testCase.commands] is currently being executed. */ | |
| 267 int currentStep; | |
| 268 | |
| 263 RunningProcess(TestCase this.testCase, | 269 RunningProcess(TestCase this.testCase, |
| 264 [this.allowRetries, this.processQueue]); | 270 [this.allowRetries, this.processQueue]); |
| 265 | 271 |
| 266 void exitHandler(int exitCode) { | 272 /** |
| 273 * Called when all commands are executed. [exitCode] is 0 if all command | |
| 274 * succeded, otherwise it will have the exit code of the first failing | |
| 275 * command. | |
| 276 */ | |
| 277 void testComplete(int exitCode) { | |
| 267 new TestOutput(testCase, exitCode, timedOut, stdout, | 278 new TestOutput(testCase, exitCode, timedOut, stdout, |
| 268 stderr, new Date.now().difference(startTime)); | 279 stderr, new Date.now().difference(startTime)); |
| 269 process.close(); | |
| 270 timeoutTimer.cancel(); | 280 timeoutTimer.cancel(); |
| 271 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { | 281 if (testCase.output.unexpectedOutput && testCase.configuration['verbose']) { |
| 272 print(testCase.displayName); | 282 print(testCase.displayName); |
| 273 for (var line in testCase.output.stderr) print(line); | 283 for (var line in testCase.output.stderr) print(line); |
| 274 for (var line in testCase.output.stdout) print(line); | 284 for (var line in testCase.output.stdout) print(line); |
| 275 } | 285 } |
| 276 if (allowRetries != null && allowRetries | 286 if (allowRetries != null && allowRetries |
| 277 && testCase.configuration['component'] == 'webdriver' && | 287 && testCase.configuration['component'] == 'webdriver' && |
| 278 testCase.output.unexpectedOutput && testCase.numRetries > 0) { | 288 testCase.output.unexpectedOutput && testCase.numRetries > 0) { |
| 279 // Selenium tests can be flaky. Try rerunning. | 289 // Selenium tests can be flaky. Try rerunning. |
| 280 testCase.output.requestRetry = true; | 290 testCase.output.requestRetry = true; |
| 281 } | 291 } |
| 282 if (testCase.output.requestRetry) { | 292 if (testCase.output.requestRetry) { |
| 283 testCase.output.requestRetry = false; | 293 testCase.output.requestRetry = false; |
| 284 this.timedOut = false; | 294 this.timedOut = false; |
| 285 testCase.dynamic.numRetries--; | 295 testCase.dynamic.numRetries--; |
| 286 print("Potential flake. Re-running " + testCase.displayName); | 296 print("Potential flake. Re-running ${testCase.displayName}"); |
| 287 this.start(); | 297 this.start(); |
| 288 } else { | 298 } else { |
| 289 testCase.completed(); | 299 testCase.completed(); |
| 290 } | 300 } |
| 291 } | 301 } |
| 292 | 302 |
| 293 void compilerExitHandler(int exitCode) { | 303 /** |
| 294 if (exitCode != 0) { | 304 * Process exit handler called at the end of every command. It internally |
| 305 * decides what is the most appropriate handler. In particular, all but the | |
| 306 * last command are compilation steps analyzed in [compilerExitHandler], the | |
| 307 * last command actually runs the test, which is analyzed in | |
| 308 * [testExitHandler]. | |
| 309 */ | |
| 310 void stepExitHandler(int exitCode) { | |
| 311 process.close(); | |
| 312 if (currentStep == testCase.commands.length) { // done with test command | |
| 313 testComplete(exitCode); | |
| 314 } else if (exitCode != 0) { | |
| 295 stderr.add('test.dart: Compilation step failed (exit code $exitCode)\n'); | 315 stderr.add('test.dart: Compilation step failed (exit code $exitCode)\n'); |
| 296 exitHandler(exitCode); | 316 testComplete(exitCode); |
| 297 } else { | 317 } else { |
| 298 process.close(); | 318 stderr.add('test.dart: Compilation step finished\n'); |
|
Bill Hesse
2012/02/28 16:41:56
Could we add currentStep to the messages in stderr
Siggi Cherem (dart-lang)
2012/02/28 17:42:37
Done
| |
| 299 stderr.add('test.dart: Compilation finished, starting execution\n'); | 319 stdout.add('test.dart: Compilation step finished\n'); |
| 300 stdout.add('test.dart: Compilation finished, starting execution\n'); | 320 if (currentStep == testCase.commands.length - 1 |
| 301 if (testCase.configuration['component'] == 'webdriver') { | 321 && testCase.configuration['component'] == 'webdriver') { |
| 302 // Note: processQueue will always be non-null for component == webdriver | 322 // Note: processQueue will always be non-null for component == webdriver |
| 303 // (It is only null for component == vm) | 323 // (It is only null for component == vm) |
| 304 processQueue._getBatchRunner(testCase).startTest(testCase); | 324 processQueue._getBatchRunner(testCase).startTest(testCase); |
| 305 } else { | 325 } else { |
| 306 runCommand(testCase.executablePath, testCase.arguments, exitHandler); | 326 runCommand(testCase.commands[currentStep++], stepExitHandler); |
| 307 } | 327 } |
| 308 } | 328 } |
| 309 } | 329 } |
| 310 | 330 |
| 311 Function makeReadHandler(StringInputStream source, List<String> destination) { | 331 Function makeReadHandler(StringInputStream source, List<String> destination) { |
| 312 return () { | 332 return () { |
| 313 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. | 333 if (source.closed) return; // TODO(whesse): Remove when bug is fixed. |
| 314 var line = source.readLine(); | 334 var line = source.readLine(); |
| 315 while (null != line) { | 335 while (null != line) { |
| 316 destination.add(line); | 336 destination.add(line); |
| 317 line = source.readLine(); | 337 line = source.readLine(); |
| 318 } | 338 } |
| 319 }; | 339 }; |
| 320 } | 340 } |
| 321 | 341 |
| 322 void start() { | 342 void start() { |
| 323 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); | 343 Expect.isFalse(testCase.expectedOutcomes.contains(SKIP)); |
| 324 stdout = new List<String>(); | 344 stdout = new List<String>(); |
| 325 stderr = new List<String>(); | 345 stderr = new List<String>(); |
| 326 if (testCase is BrowserTestCase && testCase.dynamic.compilerPath != null) { | 346 currentStep = 0; |
| 327 runCommand(testCase.dynamic.compilerPath, | 347 runCommand(testCase.commands[currentStep++], stepExitHandler); |
| 328 testCase.dynamic.compilerArguments, | |
| 329 compilerExitHandler); | |
| 330 } else { | |
| 331 runCommand(testCase.executablePath, testCase.arguments, exitHandler); | |
| 332 } | |
| 333 } | 348 } |
| 334 | 349 |
| 335 void runCommand(String executable, | 350 void runCommand(Command command, |
| 336 List<String> arguments, | |
| 337 void exitHandler(int exitCode)) { | 351 void exitHandler(int exitCode)) { |
| 338 if (new Platform().operatingSystem() == 'windows') { | 352 if (new Platform().operatingSystem() == 'windows') { |
| 339 // Windows can't handle the first command if it is a .bat file or the like | 353 // Windows can't handle the first command if it is a .bat file or the like |
| 340 // with the slashes going the other direction. | 354 // with the slashes going the other direction. |
| 341 // TODO(efortuna): Remove this when fixed (Issue 1306). | 355 // TODO(efortuna): Remove this when fixed (Issue 1306). |
| 342 executable = executable.replaceAll('/', '\\'); | 356 command.executable = command.executable.replaceAll('/', '\\'); |
| 343 } | 357 } |
| 344 process = new Process.start(executable, arguments); | 358 process = new Process.start(command.executable, command.arguments); |
| 345 process.exitHandler = exitHandler; | 359 process.exitHandler = exitHandler; |
| 346 startTime = new Date.now(); | 360 startTime = new Date.now(); |
| 347 InputStream stdoutStream = process.stdout; | 361 InputStream stdoutStream = process.stdout; |
| 348 InputStream stderrStream = process.stderr; | 362 InputStream stderrStream = process.stderr; |
| 349 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream); | 363 StringInputStream stdoutStringStream = new StringInputStream(stdoutStream); |
| 350 StringInputStream stderrStringStream = new StringInputStream(stderrStream); | 364 StringInputStream stderrStringStream = new StringInputStream(stderrStream); |
| 351 stdoutStringStream.lineHandler = | 365 stdoutStringStream.lineHandler = |
| 352 makeReadHandler(stdoutStringStream, stdout); | 366 makeReadHandler(stdoutStringStream, stdout); |
| 353 stderrStringStream.lineHandler = | 367 stderrStringStream.lineHandler = |
| 354 makeReadHandler(stderrStringStream, stderr); | 368 makeReadHandler(stderrStringStream, stderr); |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 371 | 385 |
| 372 TestCase _currentTest; | 386 TestCase _currentTest; |
| 373 List<String> _testStdout; | 387 List<String> _testStdout; |
| 374 List<String> _testStderr; | 388 List<String> _testStderr; |
| 375 Date _startTime; | 389 Date _startTime; |
| 376 Timer _timer; | 390 Timer _timer; |
| 377 | 391 |
| 378 bool _isWebDriver; | 392 bool _isWebDriver; |
| 379 | 393 |
| 380 BatchRunnerProcess(TestCase testCase) { | 394 BatchRunnerProcess(TestCase testCase) { |
| 381 _executable = testCase.executablePath; | 395 _executable = testCase.commands.last().executable; |
| 382 _batchArguments = testCase.batchRunnerArguments; | 396 _batchArguments = testCase.batchRunnerArguments; |
| 383 _isWebDriver = testCase.configuration['component'] == 'webdriver'; | 397 _isWebDriver = testCase.configuration['component'] == 'webdriver'; |
| 384 } | 398 } |
| 385 | 399 |
| 386 bool get active() => _currentTest != null; | 400 bool get active() => _currentTest != null; |
| 387 | 401 |
| 388 void startTest(TestCase testCase) { | 402 void startTest(TestCase testCase) { |
| 389 _currentTest = testCase; | 403 _currentTest = testCase; |
| 390 if (_process === null) { | 404 if (_process === null) { |
| 391 // Start process if not yet started. | 405 // Start process if not yet started. |
| 392 _executable = testCase.executablePath; | 406 _executable = testCase.commands.last().executable; |
| 393 _startProcess(() { | 407 _startProcess(() { |
| 394 doStartTest(testCase); | 408 doStartTest(testCase); |
| 395 }); | 409 }); |
| 396 } else if (testCase.executablePath != _executable) { | 410 } else if (testCase.commands.last().executable != _executable) { |
| 397 // Restart this runner with the right executable for this test | 411 // Restart this runner with the right executable for this test |
| 398 // if needed. | 412 // if needed. |
| 399 _executable = testCase.executablePath; | 413 _executable = testCase.commands.last().executable; |
| 400 _batchArguments = testCase.batchRunnerArguments; | 414 _batchArguments = testCase.batchRunnerArguments; |
| 401 _process.exitHandler = (exitCode) { | 415 _process.exitHandler = (exitCode) { |
| 402 _process.close(); | 416 _process.close(); |
| 403 _startProcess(() { | 417 _startProcess(() { |
| 404 doStartTest(testCase); | 418 doStartTest(testCase); |
| 405 }); | 419 }); |
| 406 }; | 420 }; |
| 407 _process.kill(); | 421 _process.kill(); |
| 408 } else { | 422 } else { |
| 409 doStartTest(testCase); | 423 doStartTest(testCase); |
| (...skipping 311 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 721 for (var runner in runners) { | 735 for (var runner in runners) { |
| 722 if (!runner.active) return runner; | 736 if (!runner.active) return runner; |
| 723 } | 737 } |
| 724 throw new Exception('Unable to find inactive batch runner.'); | 738 throw new Exception('Unable to find inactive batch runner.'); |
| 725 } | 739 } |
| 726 | 740 |
| 727 void _tryRunTest() { | 741 void _tryRunTest() { |
| 728 _checkDone(); | 742 _checkDone(); |
| 729 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { | 743 if (_numProcesses < _maxProcesses && !_tests.isEmpty()) { |
| 730 TestCase test = _tests.removeFirst(); | 744 TestCase test = _tests.removeFirst(); |
| 731 if (_verbose) print(test.commandLine); | 745 if (_verbose) print(test.commands.last().commandLine); |
| 732 if (_listTests) { | 746 if (_listTests) { |
| 733 final String tab = '\t'; | 747 final String tab = '\t'; |
| 734 String outcomes = | 748 String outcomes = |
| 735 Strings.join(new List.from(test.expectedOutcomes), ','); | 749 Strings.join(new List.from(test.expectedOutcomes), ','); |
| 736 print(test.displayName + tab + outcomes + tab + test.isNegative + | 750 print(test.displayName + tab + outcomes + tab + test.isNegative + |
| 737 tab + Strings.join(test.arguments, tab)); | 751 tab + Strings.join(test.commands.last().arguments, tab)); |
| 738 return; | 752 return; |
| 739 } | 753 } |
| 740 _progress.start(test); | 754 _progress.start(test); |
| 741 Function oldCallback = test.completedHandler; | 755 Function oldCallback = test.completedHandler; |
| 742 Function wrapper = (TestCase test_arg) { | 756 Function wrapper = (TestCase test_arg) { |
| 743 _numProcesses--; | 757 _numProcesses--; |
| 744 _progress.done(test_arg); | 758 _progress.done(test_arg); |
| 745 _tryRunTest(); | 759 _tryRunTest(); |
| 746 oldCallback(test_arg); | 760 oldCallback(test_arg); |
| 747 }; | 761 }; |
| 748 test.completedHandler = wrapper; | 762 test.completedHandler = wrapper; |
| 749 if (test.configuration['component'] == 'dartc' && | 763 if (test.configuration['component'] == 'dartc' && |
| 750 test.displayName != 'dartc/junit_tests') { | 764 test.displayName != 'dartc/junit_tests') { |
| 751 _getBatchRunner(test).startTest(test); | 765 _getBatchRunner(test).startTest(test); |
| 752 } else { | 766 } else { |
| 753 // Once we've actually failed a test, technically, we wouldn't need to | 767 // Once we've actually failed a test, technically, we wouldn't need to |
| 754 // bother retrying any subsequent tests since the bot is already red. | 768 // bother retrying any subsequent tests since the bot is already red. |
| 755 // However, we continue to retry tests until we have actually failed | 769 // However, we continue to retry tests until we have actually failed |
| 756 // four tests (arbitrarily chosen) for more debugable output, so that | 770 // four tests (arbitrarily chosen) for more debugable output, so that |
| 757 // the developer doesn't waste his or her time trying to fix a bunch of | 771 // the developer doesn't waste his or her time trying to fix a bunch of |
| 758 // tests that appear to be broken but were actually just flakes that | 772 // tests that appear to be broken but were actually just flakes that |
| 759 // didn't get retried because there had already been one failure. | 773 // didn't get retried because there had already been one failure. |
| 760 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; | 774 bool allowRetry = _MAX_FAILED_NO_RETRY > _progress.numFailedTests; |
| 761 new RunningProcess(test, allowRetry, this).start(); | 775 new RunningProcess(test, allowRetry, this).start(); |
| 762 } | 776 } |
| 763 _numProcesses++; | 777 _numProcesses++; |
| 764 } | 778 } |
| 765 } | 779 } |
| 766 } | 780 } |
| OLD | NEW |