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 import "dart:convert" show LineSplitter, UTF8, JSON; | 16 import "dart:convert" show LineSplitter, UTF8, JSON; |
17 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow | 17 // We need to use the 'io' prefix here, otherwise io.exitCode will shadow |
18 // CommandOutput.exitCode in subclasses of CommandOutput. | 18 // CommandOutput.exitCode in subclasses of CommandOutput. |
19 import "dart:io" as io; | 19 import "dart:io" as io; |
20 import "dart:math" as math; | 20 import "dart:math" as math; |
| 21 import 'android.dart'; |
21 import 'dependency_graph.dart' as dgraph; | 22 import 'dependency_graph.dart' as dgraph; |
22 import "browser_controller.dart"; | 23 import "browser_controller.dart"; |
23 import "path.dart"; | 24 import "path.dart"; |
24 import "status_file_parser.dart"; | 25 import "status_file_parser.dart"; |
25 import "test_progress.dart"; | 26 import "test_progress.dart"; |
26 import "test_suite.dart"; | 27 import "test_suite.dart"; |
27 import "utils.dart"; | 28 import "utils.dart"; |
28 import 'record_and_replay.dart'; | 29 import 'record_and_replay.dart'; |
29 | 30 |
30 const int CRASHING_BROWSER_EXITCODE = -10; | 31 const int CRASHING_BROWSER_EXITCODE = -10; |
(...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 bool _equal(AnalysisCommand other) => | 338 bool _equal(AnalysisCommand other) => |
338 super._equal(other) && flavor == other.flavor; | 339 super._equal(other) && flavor == other.flavor; |
339 } | 340 } |
340 | 341 |
341 class VmCommand extends ProcessCommand { | 342 class VmCommand extends ProcessCommand { |
342 VmCommand._(String executable, List<String> arguments, | 343 VmCommand._(String executable, List<String> arguments, |
343 Map<String, String> environmentOverrides) | 344 Map<String, String> environmentOverrides) |
344 : super._("vm", executable, arguments, environmentOverrides); | 345 : super._("vm", executable, arguments, environmentOverrides); |
345 } | 346 } |
346 | 347 |
| 348 class AdbPrecompilationCommand extends Command { |
| 349 final String precompiledRunnerFilename; |
| 350 final String precompiledTestDirectory; |
| 351 final bool useBlobs; |
| 352 |
| 353 AdbPrecompilationCommand._(this.precompiledRunnerFilename, |
| 354 this.precompiledTestDirectory, |
| 355 this.useBlobs) |
| 356 : super._("adb_precompilation"); |
| 357 |
| 358 void _buildHashCode(HashCodeBuilder builder) { |
| 359 super._buildHashCode(builder); |
| 360 builder.add(precompiledRunnerFilename); |
| 361 builder.add(precompiledTestDirectory); |
| 362 builder.add(useBlobs); |
| 363 } |
| 364 |
| 365 bool _equal(AdbPrecompilationCommand other) => |
| 366 super._equal(other) && |
| 367 precompiledRunnerFilename == other.precompiledRunnerFilename && |
| 368 useBlobs == other.useBlobs && |
| 369 precompiledTestDirectory == other.precompiledTestDirectory; |
| 370 |
| 371 String toString() => 'Steps to push precompiled runner and precompiled code ' |
| 372 'to an attached device. Uses (and requires) adb.'; |
| 373 } |
| 374 |
347 class JSCommandlineCommand extends ProcessCommand { | 375 class JSCommandlineCommand extends ProcessCommand { |
348 JSCommandlineCommand._( | 376 JSCommandlineCommand._( |
349 String displayName, String executable, List<String> arguments, | 377 String displayName, String executable, List<String> arguments, |
350 [Map<String, String> environmentOverrides = null]) | 378 [Map<String, String> environmentOverrides = null]) |
351 : super._(displayName, executable, arguments, environmentOverrides); | 379 : super._(displayName, executable, arguments, environmentOverrides); |
352 } | 380 } |
353 | 381 |
354 class PubCommand extends ProcessCommand { | 382 class PubCommand extends ProcessCommand { |
355 final String command; | 383 final String command; |
356 | 384 |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
603 flavor, displayName, executable, arguments, environmentOverrides); | 631 flavor, displayName, executable, arguments, environmentOverrides); |
604 return _getUniqueCommand(command); | 632 return _getUniqueCommand(command); |
605 } | 633 } |
606 | 634 |
607 VmCommand getVmCommand(String executable, List<String> arguments, | 635 VmCommand getVmCommand(String executable, List<String> arguments, |
608 Map<String, String> environmentOverrides) { | 636 Map<String, String> environmentOverrides) { |
609 var command = new VmCommand._(executable, arguments, environmentOverrides); | 637 var command = new VmCommand._(executable, arguments, environmentOverrides); |
610 return _getUniqueCommand(command); | 638 return _getUniqueCommand(command); |
611 } | 639 } |
612 | 640 |
| 641 AdbPrecompilationCommand getAdbPrecompiledCommand(String precompiledRunner, |
| 642 String testDirectory, |
| 643 bool useBlobs) { |
| 644 var command = new AdbPrecompilationCommand._( |
| 645 precompiledRunner, testDirectory, useBlobs); |
| 646 return _getUniqueCommand(command); |
| 647 } |
| 648 |
613 Command getJSCommandlineCommand(String displayName, executable, arguments, | 649 Command getJSCommandlineCommand(String displayName, executable, arguments, |
614 [environment = null]) { | 650 [environment = null]) { |
615 var command = new JSCommandlineCommand._( | 651 var command = new JSCommandlineCommand._( |
616 displayName, executable, arguments, environment); | 652 displayName, executable, arguments, environment); |
617 return _getUniqueCommand(command); | 653 return _getUniqueCommand(command); |
618 } | 654 } |
619 | 655 |
620 Command getProcessCommand(String displayName, executable, arguments, | 656 Command getProcessCommand(String displayName, executable, arguments, |
621 [environment = null, workingDirectory = null]) { | 657 [environment = null, workingDirectory = null]) { |
622 var command = new ProcessCommand._( | 658 var command = new ProcessCommand._( |
(...skipping 995 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1618 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); | 1654 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |
1619 } else if (command is BrowserTestCommand) { | 1655 } else if (command is BrowserTestCommand) { |
1620 return new HTMLBrowserCommandOutputImpl( | 1656 return new HTMLBrowserCommandOutputImpl( |
1621 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); | 1657 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |
1622 } else if (command is AnalysisCommand) { | 1658 } else if (command is AnalysisCommand) { |
1623 return new AnalysisCommandOutputImpl( | 1659 return new AnalysisCommandOutputImpl( |
1624 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); | 1660 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |
1625 } else if (command is VmCommand) { | 1661 } else if (command is VmCommand) { |
1626 return new VmCommandOutputImpl( | 1662 return new VmCommandOutputImpl( |
1627 command, exitCode, timedOut, stdout, stderr, time, pid); | 1663 command, exitCode, timedOut, stdout, stderr, time, pid); |
| 1664 } else if (command is AdbPrecompilationCommand) { |
| 1665 return new VmCommandOutputImpl( |
| 1666 command, exitCode, timedOut, stdout, stderr, time, pid); |
1628 } else if (command is CompilationCommand) { | 1667 } else if (command is CompilationCommand) { |
1629 if (command.displayName == 'precompiler' || | 1668 if (command.displayName == 'precompiler' || |
1630 command.displayName == 'dart2snapshot') { | 1669 command.displayName == 'dart2snapshot') { |
1631 return new VmCommandOutputImpl( | 1670 return new VmCommandOutputImpl( |
1632 command, exitCode, timedOut, stdout, stderr, time, pid); | 1671 command, exitCode, timedOut, stdout, stderr, time, pid); |
1633 } | 1672 } |
1634 return new CompilationCommandOutputImpl( | 1673 return new CompilationCommandOutputImpl( |
1635 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); | 1674 command, exitCode, timedOut, stdout, stderr, time, compilationSkipped); |
1636 } else if (command is JSCommandlineCommand) { | 1675 } else if (command is JSCommandlineCommand) { |
1637 return new JsCommandlineOutputImpl( | 1676 return new JsCommandlineOutputImpl( |
(...skipping 751 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2389 Future cleanup(); | 2428 Future cleanup(); |
2390 // TODO(kustermann): The [timeout] parameter should be a property of Command | 2429 // TODO(kustermann): The [timeout] parameter should be a property of Command |
2391 Future<CommandOutput> runCommand( | 2430 Future<CommandOutput> runCommand( |
2392 dgraph.Node node, Command command, int timeout); | 2431 dgraph.Node node, Command command, int timeout); |
2393 } | 2432 } |
2394 | 2433 |
2395 class CommandExecutorImpl implements CommandExecutor { | 2434 class CommandExecutorImpl implements CommandExecutor { |
2396 final Map globalConfiguration; | 2435 final Map globalConfiguration; |
2397 final int maxProcesses; | 2436 final int maxProcesses; |
2398 final int maxBrowserProcesses; | 2437 final int maxBrowserProcesses; |
| 2438 AdbDevicePool adbDevicePool; |
2399 | 2439 |
2400 // For dart2js and analyzer batch processing, | 2440 // For dart2js and analyzer batch processing, |
2401 // we keep a list of batch processes. | 2441 // we keep a list of batch processes. |
2402 final _batchProcesses = new Map<String, List<BatchRunnerProcess>>(); | 2442 final _batchProcesses = new Map<String, List<BatchRunnerProcess>>(); |
2403 // We keep a BrowserTestRunner for every configuration. | 2443 // We keep a BrowserTestRunner for every configuration. |
2404 final _browserTestRunners = new Map<Map, BrowserTestRunner>(); | 2444 final _browserTestRunners = new Map<Map, BrowserTestRunner>(); |
2405 | 2445 |
2406 bool _finishing = false; | 2446 bool _finishing = false; |
2407 | 2447 |
2408 CommandExecutorImpl( | 2448 CommandExecutorImpl( |
2409 this.globalConfiguration, this.maxProcesses, this.maxBrowserProcesses); | 2449 this.globalConfiguration, this.maxProcesses, this.maxBrowserProcesses, |
| 2450 {this.adbDevicePool}); |
2410 | 2451 |
2411 Future cleanup() { | 2452 Future cleanup() { |
2412 assert(!_finishing); | 2453 assert(!_finishing); |
2413 _finishing = true; | 2454 _finishing = true; |
2414 | 2455 |
2415 Future _terminateBatchRunners() { | 2456 Future _terminateBatchRunners() { |
2416 var futures = []; | 2457 var futures = []; |
2417 for (var runners in _batchProcesses.values) { | 2458 for (var runners in _batchProcesses.values) { |
2418 futures.addAll(runners.map((runner) => runner.terminate())); | 2459 futures.addAll(runners.map((runner) => runner.terminate())); |
2419 } | 2460 } |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2453 if (command is BrowserTestCommand) { | 2494 if (command is BrowserTestCommand) { |
2454 return _startBrowserControllerTest(command, timeout); | 2495 return _startBrowserControllerTest(command, timeout); |
2455 } else if (command is CompilationCommand && dart2jsBatchMode) { | 2496 } else if (command is CompilationCommand && dart2jsBatchMode) { |
2456 return _getBatchRunner("dart2js") | 2497 return _getBatchRunner("dart2js") |
2457 .runCommand("dart2js", command, timeout, command.arguments); | 2498 .runCommand("dart2js", command, timeout, command.arguments); |
2458 } else if (command is AnalysisCommand && batchMode) { | 2499 } else if (command is AnalysisCommand && batchMode) { |
2459 return _getBatchRunner(command.flavor) | 2500 return _getBatchRunner(command.flavor) |
2460 .runCommand(command.flavor, command, timeout, command.arguments); | 2501 .runCommand(command.flavor, command, timeout, command.arguments); |
2461 } else if (command is ScriptCommand) { | 2502 } else if (command is ScriptCommand) { |
2462 return command.run(); | 2503 return command.run(); |
| 2504 } else if (command is AdbPrecompilationCommand) { |
| 2505 assert(adbDevicePool != null); |
| 2506 return adbDevicePool.aquireDevice().then((AdbDevice device) { |
| 2507 return _runAdbPrecompilationCommand(device, command).whenComplete(() { |
| 2508 adbDevicePool.releaseDevice(device); |
| 2509 }); |
| 2510 }); |
2463 } else { | 2511 } else { |
2464 return new RunningProcess(command, timeout).run(); | 2512 return new RunningProcess(command, timeout).run(); |
2465 } | 2513 } |
2466 } | 2514 } |
2467 | 2515 |
| 2516 Future<CommandOutput> _runAdbPrecompilationCommand( |
| 2517 AdbDevice device, AdbPrecompilationCommand command) async { |
| 2518 var runner = command.precompiledRunnerFilename; |
| 2519 var testdir = command.precompiledTestDirectory; |
| 2520 var devicedir = '/data/local/tmp/precompilation-testing'; |
| 2521 |
| 2522 // We copy all the files which the vm precompiler puts into the test |
| 2523 // directory. |
| 2524 List<String> files = new io.Directory(testdir) |
| 2525 .listSync() |
| 2526 .where((fse) => fse is io.File) |
| 2527 .map((file) => file.path) |
| 2528 .map((path) => path.substring(path.lastIndexOf('/') + 1)) |
| 2529 .toList(); |
| 2530 |
| 2531 // All closures are of type "Future<AdbCommandResult> run()" |
| 2532 List<Function> steps = []; |
| 2533 |
| 2534 steps.add(() => device.runAdbShellCommand( |
| 2535 ['rm', '-Rf', devicedir])); |
| 2536 steps.add(() => device.runAdbShellCommand( |
| 2537 ['mkdir', devicedir])); |
| 2538 steps.add(() => device.runAdbCommand( |
| 2539 ['push', runner, '$devicedir/runner'])); |
| 2540 steps.add(() => device.runAdbShellCommand( |
| 2541 ['chmod', '777', '$devicedir/runner'])); |
| 2542 |
| 2543 for (var file in files) { |
| 2544 steps.add(() => device.runAdbCommand( |
| 2545 ['push', '$testdir/$file', '$devicedir/$file'])); |
| 2546 } |
| 2547 |
| 2548 if (command.useBlobs) { |
| 2549 steps.add(() => device.runAdbShellCommand( |
| 2550 ['$devicedir/runner', '--run-precompiled-snapshot=$devicedir', |
| 2551 '--use_blobs', 'ignored.dart'])); |
| 2552 } else { |
| 2553 steps.add(() => device.runAdbShellCommand( |
| 2554 ['$devicedir/runner', '--run-precompiled-snapshot=$devicedir', |
| 2555 'ignored.dart'])); |
| 2556 } |
| 2557 |
| 2558 var stopwatch = new Stopwatch()..start(); |
| 2559 var writer = new StringBuffer(); |
| 2560 |
| 2561 await device.waitForBootCompleted(); |
| 2562 await device.waitForDevice(); |
| 2563 |
| 2564 AdbCommandResult result; |
| 2565 for (var i = 0; i < steps.length; i++) { |
| 2566 var fun = steps[i]; |
| 2567 var commandStopwatch = new Stopwatch()..start(); |
| 2568 result = await fun(); |
| 2569 |
| 2570 writer.writeln("Executing ${result.command}"); |
| 2571 if (result.stdout.length > 0) { |
| 2572 writer.writeln("Stdout:\n${result.stdout.trim()}"); |
| 2573 } |
| 2574 if (result.stderr.length > 0) { |
| 2575 writer.writeln("Stderr:\n${result.stderr.trim()}"); |
| 2576 } |
| 2577 writer.writeln("ExitCode: ${result.exitCode}"); |
| 2578 writer.writeln("Time: ${commandStopwatch.elapsed}"); |
| 2579 writer.writeln(""); |
| 2580 |
| 2581 // If one command fails, we stop processing the others and return |
| 2582 // immediately. |
| 2583 if (result.exitCode != 0) break; |
| 2584 } |
| 2585 return createCommandOutput( |
| 2586 command, result.exitCode, false, UTF8.encode('$writer'), |
| 2587 [], stopwatch.elapsed, false); |
| 2588 } |
| 2589 |
2468 BatchRunnerProcess _getBatchRunner(String identifier) { | 2590 BatchRunnerProcess _getBatchRunner(String identifier) { |
2469 // Start batch processes if needed | 2591 // Start batch processes if needed |
2470 var runners = _batchProcesses[identifier]; | 2592 var runners = _batchProcesses[identifier]; |
2471 if (runners == null) { | 2593 if (runners == null) { |
2472 runners = new List<BatchRunnerProcess>(maxProcesses); | 2594 runners = new List<BatchRunnerProcess>(maxProcesses); |
2473 for (int i = 0; i < maxProcesses; i++) { | 2595 for (int i = 0; i < maxProcesses; i++) { |
2474 runners[i] = new BatchRunnerProcess(); | 2596 runners[i] = new BatchRunnerProcess(); |
2475 } | 2597 } |
2476 _batchProcesses[identifier] = runners; | 2598 _batchProcesses[identifier] = runners; |
2477 } | 2599 } |
(...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2709 Map _globalConfiguration; | 2831 Map _globalConfiguration; |
2710 | 2832 |
2711 Function _allDone; | 2833 Function _allDone; |
2712 final dgraph.Graph _graph = new dgraph.Graph(); | 2834 final dgraph.Graph _graph = new dgraph.Graph(); |
2713 List<EventListener> _eventListener; | 2835 List<EventListener> _eventListener; |
2714 | 2836 |
2715 ProcessQueue(this._globalConfiguration, maxProcesses, maxBrowserProcesses, | 2837 ProcessQueue(this._globalConfiguration, maxProcesses, maxBrowserProcesses, |
2716 DateTime startTime, testSuites, this._eventListener, this._allDone, | 2838 DateTime startTime, testSuites, this._eventListener, this._allDone, |
2717 [bool verbose = false, | 2839 [bool verbose = false, |
2718 String recordingOutputFile, | 2840 String recordingOutputFile, |
2719 String recordedInputFile]) { | 2841 String recordedInputFile, |
| 2842 AdbDevicePool adbDevicePool]) { |
2720 void setupForListing(TestCaseEnqueuer testCaseEnqueuer) { | 2843 void setupForListing(TestCaseEnqueuer testCaseEnqueuer) { |
2721 _graph.events | 2844 _graph.events |
2722 .where((event) => event is dgraph.GraphSealedEvent) | 2845 .where((event) => event is dgraph.GraphSealedEvent) |
2723 .listen((dgraph.GraphSealedEvent event) { | 2846 .listen((dgraph.GraphSealedEvent event) { |
2724 var testCases = new List.from(testCaseEnqueuer.remainingTestCases); | 2847 var testCases = new List.from(testCaseEnqueuer.remainingTestCases); |
2725 testCases.sort((a, b) => a.displayName.compareTo(b.displayName)); | 2848 testCases.sort((a, b) => a.displayName.compareTo(b.displayName)); |
2726 | 2849 |
2727 print("\nGenerating all matching test cases ....\n"); | 2850 print("\nGenerating all matching test cases ....\n"); |
2728 | 2851 |
2729 for (TestCase testCase in testCases) { | 2852 for (TestCase testCase in testCases) { |
(...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2810 var commandEnqueuer = new CommandEnqueuer(_graph); | 2933 var commandEnqueuer = new CommandEnqueuer(_graph); |
2811 | 2934 |
2812 // CommandExecutor will execute commands | 2935 // CommandExecutor will execute commands |
2813 var executor; | 2936 var executor; |
2814 if (recording) { | 2937 if (recording) { |
2815 executor = new RecordingCommandExecutor(new Path(recordingOutputFile)); | 2938 executor = new RecordingCommandExecutor(new Path(recordingOutputFile)); |
2816 } else if (replaying) { | 2939 } else if (replaying) { |
2817 executor = new ReplayingCommandExecutor(new Path(recordedInputFile)); | 2940 executor = new ReplayingCommandExecutor(new Path(recordedInputFile)); |
2818 } else { | 2941 } else { |
2819 executor = new CommandExecutorImpl( | 2942 executor = new CommandExecutorImpl( |
2820 _globalConfiguration, maxProcesses, maxBrowserProcesses); | 2943 _globalConfiguration, maxProcesses, |
| 2944 maxBrowserProcesses, adbDevicePool: adbDevicePool); |
2821 } | 2945 } |
2822 | 2946 |
2823 // Run "runnable commands" using [executor] subject to | 2947 // Run "runnable commands" using [executor] subject to |
2824 // maxProcesses/maxBrowserProcesses constraint | 2948 // maxProcesses/maxBrowserProcesses constraint |
2825 commandQueue = new CommandQueue(_graph, testCaseEnqueuer, executor, | 2949 commandQueue = new CommandQueue(_graph, testCaseEnqueuer, executor, |
2826 maxProcesses, maxBrowserProcesses, verbose); | 2950 maxProcesses, maxBrowserProcesses, verbose); |
2827 | 2951 |
2828 // Finish test cases when all commands were run (or some failed) | 2952 // Finish test cases when all commands were run (or some failed) |
2829 var testCaseCompleter = | 2953 var testCaseCompleter = |
2830 new TestCaseCompleter(_graph, testCaseEnqueuer, commandQueue); | 2954 new TestCaseCompleter(_graph, testCaseEnqueuer, commandQueue); |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2886 } | 3010 } |
2887 } | 3011 } |
2888 | 3012 |
2889 void eventAllTestsDone() { | 3013 void eventAllTestsDone() { |
2890 for (var listener in _eventListener) { | 3014 for (var listener in _eventListener) { |
2891 listener.allDone(); | 3015 listener.allDone(); |
2892 } | 3016 } |
2893 _allDone(); | 3017 _allDone(); |
2894 } | 3018 } |
2895 } | 3019 } |
OLD | NEW |