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. |
(...skipping 346 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
357 void _buildHashCode(HashCodeBuilder builder) { | 357 void _buildHashCode(HashCodeBuilder builder) { |
358 super._buildHashCode(builder); | 358 super._buildHashCode(builder); |
359 builder.addJson(flavor); | 359 builder.addJson(flavor); |
360 } | 360 } |
361 | 361 |
362 bool _equal(AnalysisCommand other) => | 362 bool _equal(AnalysisCommand other) => |
363 super._equal(other) && flavor == other.flavor; | 363 super._equal(other) && flavor == other.flavor; |
364 } | 364 } |
365 | 365 |
366 class VmCommand extends ProcessCommand { | 366 class VmCommand extends ProcessCommand { |
367 final bool needsDFERunner; | |
368 VmCommand._(String executable, List<String> arguments, | 367 VmCommand._(String executable, List<String> arguments, |
369 Map<String, String> environmentOverrides, | 368 Map<String, String> environmentOverrides) |
370 bool this.needsDFERunner) | |
371 : super._("vm", executable, arguments, environmentOverrides); | 369 : super._("vm", executable, arguments, environmentOverrides); |
372 | |
373 void _buildHashCode(HashCodeBuilder builder) { | |
374 super._buildHashCode(builder); | |
375 builder.add(needsDFERunner); | |
376 } | |
377 | |
378 bool _equal(VmCommand other) => | |
379 super._equal(other) && needsDFERunner == other.needsDFERunner; | |
380 } | 370 } |
381 | 371 |
382 class VmBatchCommand extends ProcessCommand implements VmCommand { | 372 class VmBatchCommand extends ProcessCommand implements VmCommand { |
383 final String dartFile; | 373 final String dartFile; |
384 final bool checked; | 374 final bool checked; |
385 final needsDFERunner = false; | |
386 | 375 |
387 VmBatchCommand._(String executable, String dartFile, List<String> arguments, | 376 VmBatchCommand._(String executable, String dartFile, List<String> arguments, |
388 Map<String, String> environmentOverrides, {this.checked: true}) | 377 Map<String, String> environmentOverrides, {this.checked: true}) |
389 : this.dartFile = dartFile, | 378 : this.dartFile = dartFile, |
390 super._('vm-batch', executable, arguments, environmentOverrides); | 379 super._('vm-batch', executable, arguments, environmentOverrides); |
391 | 380 |
392 @override | 381 @override |
393 List<String> get batchArguments => checked | 382 List<String> get batchArguments => checked |
394 ? ['--checked', dartFile] | 383 ? ['--checked', dartFile] |
395 : [dartFile]; | 384 : [dartFile]; |
(...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
644 | 633 |
645 AnalysisCommand getAnalysisCommand( | 634 AnalysisCommand getAnalysisCommand( |
646 String displayName, executable, arguments, environmentOverrides, | 635 String displayName, executable, arguments, environmentOverrides, |
647 {String flavor: 'dart2analyzer'}) { | 636 {String flavor: 'dart2analyzer'}) { |
648 var command = new AnalysisCommand._( | 637 var command = new AnalysisCommand._( |
649 flavor, displayName, executable, arguments, environmentOverrides); | 638 flavor, displayName, executable, arguments, environmentOverrides); |
650 return _getUniqueCommand(command); | 639 return _getUniqueCommand(command); |
651 } | 640 } |
652 | 641 |
653 VmCommand getVmCommand(String executable, List<String> arguments, | 642 VmCommand getVmCommand(String executable, List<String> arguments, |
654 Map<String, String> environmentOverrides, {bool needsDFERunner: false}) { | 643 Map<String, String> environmentOverrides) { |
655 var command = new VmCommand._(executable, arguments, environmentOverrides, n
eedsDFERunner); | 644 var command = new VmCommand._(executable, arguments, environmentOverrides); |
656 return _getUniqueCommand(command); | 645 return _getUniqueCommand(command); |
657 } | 646 } |
658 | 647 |
659 VmBatchCommand getVmBatchCommand(String executable, String tester, | 648 VmBatchCommand getVmBatchCommand(String executable, String tester, |
660 List<String> arguments, Map<String, String> environmentOverrides, | 649 List<String> arguments, Map<String, String> environmentOverrides, |
661 {bool checked: true}) { | 650 {bool checked: true}) { |
662 var command = | 651 var command = |
663 new VmBatchCommand._(executable, tester, arguments, environmentOverrides
, | 652 new VmBatchCommand._(executable, tester, arguments, environmentOverrides
, |
664 checked: checked); | 653 checked: checked); |
665 return _getUniqueCommand(command); | 654 return _getUniqueCommand(command); |
(...skipping 1228 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1894 bool timedOut = false; | 1883 bool timedOut = false; |
1895 DateTime startTime; | 1884 DateTime startTime; |
1896 Timer timeoutTimer; | 1885 Timer timeoutTimer; |
1897 int pid; | 1886 int pid; |
1898 OutputLog stdout = new OutputLog(); | 1887 OutputLog stdout = new OutputLog(); |
1899 OutputLog stderr = new OutputLog(); | 1888 OutputLog stderr = new OutputLog(); |
1900 List<String> diagnostics = <String>[]; | 1889 List<String> diagnostics = <String>[]; |
1901 bool compilationSkipped = false; | 1890 bool compilationSkipped = false; |
1902 Completer<CommandOutput> completer; | 1891 Completer<CommandOutput> completer; |
1903 Map configuration; | 1892 Map configuration; |
1904 List<String> preArguments; | |
1905 | 1893 |
1906 RunningProcess(this.command, | 1894 RunningProcess(this.command, |
1907 this.timeout, | 1895 this.timeout, |
1908 {this.configuration, | 1896 {this.configuration}); |
1909 this.preArguments}); | |
1910 | 1897 |
1911 Future<CommandOutput> run() { | 1898 Future<CommandOutput> run() { |
1912 completer = new Completer<CommandOutput>(); | 1899 completer = new Completer<CommandOutput>(); |
1913 startTime = new DateTime.now(); | 1900 startTime = new DateTime.now(); |
1914 _runCommand(); | 1901 _runCommand(); |
1915 return completer.future; | 1902 return completer.future; |
1916 } | 1903 } |
1917 | 1904 |
1918 void _runCommand() { | 1905 void _runCommand() { |
1919 command.outputIsUpToDate.then((bool isUpToDate) { | 1906 command.outputIsUpToDate.then((bool isUpToDate) { |
1920 if (isUpToDate) { | 1907 if (isUpToDate) { |
1921 compilationSkipped = true; | 1908 compilationSkipped = true; |
1922 _commandComplete(0); | 1909 _commandComplete(0); |
1923 } else { | 1910 } else { |
1924 var processEnvironment = _createProcessEnvironment(); | 1911 var processEnvironment = _createProcessEnvironment(); |
1925 var args = command.arguments; | 1912 var args = command.arguments; |
1926 if (preArguments != null) { | |
1927 args = []..addAll(preArguments)..addAll(args); | |
1928 } | |
1929 Future processFuture = io.Process.start( | 1913 Future processFuture = io.Process.start( |
1930 command.executable, args, | 1914 command.executable, args, |
1931 environment: processEnvironment, | 1915 environment: processEnvironment, |
1932 workingDirectory: command.workingDirectory); | 1916 workingDirectory: command.workingDirectory); |
1933 processFuture.then((io.Process process) { | 1917 processFuture.then((io.Process process) { |
1934 StreamSubscription stdoutSubscription = | 1918 StreamSubscription stdoutSubscription = |
1935 _drainStream(process.stdout, stdout); | 1919 _drainStream(process.stdout, stdout); |
1936 StreamSubscription stderrSubscription = | 1920 StreamSubscription stderrSubscription = |
1937 _drainStream(process.stderr, stderr); | 1921 _drainStream(process.stderr, stderr); |
1938 | 1922 |
(...skipping 387 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2326 bool _dictEquals(Map a, Map b) { | 2310 bool _dictEquals(Map a, Map b) { |
2327 if (a == null) return b == null; | 2311 if (a == null) return b == null; |
2328 if (b == null) return false; | 2312 if (b == null) return false; |
2329 for (var key in a.keys) { | 2313 for (var key in a.keys) { |
2330 if (a[key] != b[key]) return false; | 2314 if (a[key] != b[key]) return false; |
2331 } | 2315 } |
2332 return true; | 2316 return true; |
2333 } | 2317 } |
2334 } | 2318 } |
2335 | 2319 |
2336 class BatchDFEProcess { | |
2337 io.Process _process; | |
2338 int _port = -1; | |
2339 Function _processExitHandler; | |
2340 | |
2341 Completer terminating = null; | |
2342 | |
2343 bool locked = false; | |
2344 | |
2345 Future<int> acquire() async { | |
2346 try { | |
2347 assert(!locked); | |
2348 locked = true; | |
2349 if (_process == null) { | |
2350 await _startProcess(); | |
2351 } | |
2352 return _port; | |
2353 } catch(e) { | |
2354 locked = false; | |
2355 rethrow; | |
2356 } | |
2357 } | |
2358 | |
2359 void release() { | |
2360 locked = false; | |
2361 } | |
2362 | |
2363 Future terminate() { | |
2364 locked = true; | |
2365 if (_process == null) { | |
2366 return new Future.value(true); | |
2367 } | |
2368 if (terminating == null) { | |
2369 terminating = new Completer(); | |
2370 _process.kill(); | |
2371 } | |
2372 return terminating.future; | |
2373 } | |
2374 | |
2375 _onExit(exitCode) { | |
2376 if (terminating != null) { | |
2377 terminating.complete(); | |
2378 return; | |
2379 } | |
2380 | |
2381 _process = null; | |
2382 locked = false; | |
2383 _port = -1; | |
2384 } | |
2385 | |
2386 static Future<String> _firstLine(stream) { | |
2387 var completer = new Completer<String>(); | |
2388 stream.transform(UTF8.decoder) | |
2389 .transform(new LineSplitter()) | |
2390 .listen((line) { | |
2391 if (!completer.isCompleted) { | |
2392 completer.complete(line); | |
2393 } | |
2394 // We need to drain a pipe continuously. | |
2395 }, onDone: () { | |
2396 if (!completer.isCompleted) { | |
2397 completer.completeError( | |
2398 "DFE kernel compiler server did not sucessfully start up"); | |
2399 } | |
2400 }); | |
2401 return completer.future; | |
2402 } | |
2403 | |
2404 Future _startProcess() async { | |
2405 final executable = io.Platform.executable; | |
2406 final arguments = ['utils/kernel-service/kernel-service.dart', '--batch']; | |
2407 | |
2408 try { | |
2409 _port = -1; | |
2410 _process = await io.Process.start(executable, arguments); | |
2411 _process.exitCode.then(_onExit); | |
2412 _process.stderr.transform(UTF8.decoder).listen(DebugLogger.error); | |
2413 | |
2414 final readyMsg = await _firstLine(_process.stdout); | |
2415 final data = readyMsg.split(' '); | |
2416 assert(data[0] == 'READY'); | |
2417 | |
2418 _port = int.parse(data[1]); | |
2419 } catch (e) { | |
2420 print("Process error:"); | |
2421 print(" Command: $executable ${arguments.join(' ')}"); | |
2422 print(" Error: $e"); | |
2423 // If there is an error starting a batch process, chances are that | |
2424 // it will always fail. So rather than re-trying a 1000+ times, we | |
2425 // exit. | |
2426 io.exit(1); | |
2427 return true; | |
2428 } | |
2429 } | |
2430 } | |
2431 | |
2432 /** | 2320 /** |
2433 * [TestCaseEnqueuer] takes a list of TestSuites, generates TestCases and | 2321 * [TestCaseEnqueuer] takes a list of TestSuites, generates TestCases and |
2434 * builds a dependency graph of all commands in every TestSuite. | 2322 * builds a dependency graph of all commands in every TestSuite. |
2435 * | 2323 * |
2436 * It will maintain three helper data structures | 2324 * It will maintain three helper data structures |
2437 * - command2node: A mapping from a [Command] to a node in the dependency graph | 2325 * - command2node: A mapping from a [Command] to a node in the dependency graph |
2438 * - command2testCases: A mapping from [Command] to all TestCases that it is | 2326 * - command2testCases: A mapping from [Command] to all TestCases that it is |
2439 * part of. | 2327 * part of. |
2440 * - remainingTestCases: A set of TestCases that were enqueued but are not | 2328 * - remainingTestCases: A set of TestCases that were enqueued but are not |
2441 * finished | 2329 * finished |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2730 final int maxProcesses; | 2618 final int maxProcesses; |
2731 final int maxBrowserProcesses; | 2619 final int maxBrowserProcesses; |
2732 AdbDevicePool adbDevicePool; | 2620 AdbDevicePool adbDevicePool; |
2733 | 2621 |
2734 // For dart2js and analyzer batch processing, | 2622 // For dart2js and analyzer batch processing, |
2735 // we keep a list of batch processes. | 2623 // we keep a list of batch processes. |
2736 final _batchProcesses = new Map<String, List<BatchRunnerProcess>>(); | 2624 final _batchProcesses = new Map<String, List<BatchRunnerProcess>>(); |
2737 // We keep a BrowserTestRunner for every configuration. | 2625 // We keep a BrowserTestRunner for every configuration. |
2738 final _browserTestRunners = new Map<Map, BrowserTestRunner>(); | 2626 final _browserTestRunners = new Map<Map, BrowserTestRunner>(); |
2739 | 2627 |
2740 List<BatchDFEProcess> _dfeProcesses = null; | |
2741 | |
2742 bool _finishing = false; | 2628 bool _finishing = false; |
2743 | 2629 |
2744 CommandExecutorImpl( | 2630 CommandExecutorImpl( |
2745 this.globalConfiguration, this.maxProcesses, this.maxBrowserProcesses, | 2631 this.globalConfiguration, this.maxProcesses, this.maxBrowserProcesses, |
2746 {this.adbDevicePool}); | 2632 {this.adbDevicePool}); |
2747 | 2633 |
2748 Future cleanup() { | 2634 Future cleanup() { |
2749 assert(!_finishing); | 2635 assert(!_finishing); |
2750 _finishing = true; | 2636 _finishing = true; |
2751 | 2637 |
2752 Future _terminateBatchRunners() { | 2638 Future _terminateBatchRunners() { |
2753 var futures = []; | 2639 var futures = []; |
2754 for (var runners in _batchProcesses.values) { | 2640 for (var runners in _batchProcesses.values) { |
2755 futures.addAll(runners.map((runner) => runner.terminate())); | 2641 futures.addAll(runners.map((runner) => runner.terminate())); |
2756 } | 2642 } |
2757 return Future.wait(futures); | 2643 return Future.wait(futures); |
2758 } | 2644 } |
2759 | 2645 |
2760 Future _terminateBrowserRunners() { | 2646 Future _terminateBrowserRunners() { |
2761 var futures = | 2647 var futures = |
2762 _browserTestRunners.values.map((runner) => runner.terminate()); | 2648 _browserTestRunners.values.map((runner) => runner.terminate()); |
2763 return Future.wait(futures); | 2649 return Future.wait(futures); |
2764 } | 2650 } |
2765 | 2651 |
2766 Future _terminateDFEWorkers() => | |
2767 Future.wait((_dfeProcesses ?? <BatchDFEProcess>[]).map((p) => p.terminate(
))); | |
2768 | |
2769 return Future.wait([ | 2652 return Future.wait([ |
2770 _terminateBatchRunners(), | 2653 _terminateBatchRunners(), |
2771 _terminateBrowserRunners(), | 2654 _terminateBrowserRunners(), |
2772 _terminateDFEWorkers() | |
2773 ]); | 2655 ]); |
2774 } | 2656 } |
2775 | 2657 |
2776 Future<CommandOutput> runCommand(node, Command command, int timeout) { | 2658 Future<CommandOutput> runCommand(node, Command command, int timeout) { |
2777 assert(!_finishing); | 2659 assert(!_finishing); |
2778 | 2660 |
2779 Future<CommandOutput> runCommand(int retriesLeft) { | 2661 Future<CommandOutput> runCommand(int retriesLeft) { |
2780 return _runCommand(command, timeout).then((CommandOutput output) { | 2662 return _runCommand(command, timeout).then((CommandOutput output) { |
2781 if (retriesLeft > 0 && shouldRetryCommand(output)) { | 2663 if (retriesLeft > 0 && shouldRetryCommand(output)) { |
2782 DebugLogger.warning("Rerunning Command: ($retriesLeft " | 2664 DebugLogger.warning("Rerunning Command: ($retriesLeft " |
(...skipping 28 matching lines...) Expand all Loading... |
2811 } else if (command is ScriptCommand) { | 2693 } else if (command is ScriptCommand) { |
2812 return command.run(); | 2694 return command.run(); |
2813 } else if (command is AdbPrecompilationCommand) { | 2695 } else if (command is AdbPrecompilationCommand) { |
2814 assert(adbDevicePool != null); | 2696 assert(adbDevicePool != null); |
2815 return adbDevicePool.acquireDevice().then((AdbDevice device) { | 2697 return adbDevicePool.acquireDevice().then((AdbDevice device) { |
2816 return _runAdbPrecompilationCommand(device, command, timeout) | 2698 return _runAdbPrecompilationCommand(device, command, timeout) |
2817 .whenComplete(() { | 2699 .whenComplete(() { |
2818 adbDevicePool.releaseDevice(device); | 2700 adbDevicePool.releaseDevice(device); |
2819 }); | 2701 }); |
2820 }); | 2702 }); |
2821 } else if (command is VmCommand && command.needsDFERunner) { | |
2822 final runner = _getDFEProcess(); | |
2823 return runner.acquire().then((port) { | |
2824 return new RunningProcess(command, timeout, | |
2825 configuration: globalConfiguration, | |
2826 preArguments: ['-DDFE_WORKER_PORT=${port}']).run(); | |
2827 }).whenComplete(() => runner.release()); | |
2828 } else if (command is VmBatchCommand) { | 2703 } else if (command is VmBatchCommand) { |
2829 var name = command.displayName; | 2704 var name = command.displayName; |
2830 return _getBatchRunner(command.displayName + command.dartFile) | 2705 return _getBatchRunner(command.displayName + command.dartFile) |
2831 .runCommand(name, command, timeout, command.arguments); | 2706 .runCommand(name, command, timeout, command.arguments); |
2832 } else { | 2707 } else { |
2833 return new RunningProcess( | 2708 return new RunningProcess( |
2834 command, timeout, configuration: globalConfiguration).run(); | 2709 command, timeout, configuration: globalConfiguration).run(); |
2835 } | 2710 } |
2836 } | 2711 } |
2837 | 2712 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2921 } | 2796 } |
2922 _batchProcesses[identifier] = runners; | 2797 _batchProcesses[identifier] = runners; |
2923 } | 2798 } |
2924 | 2799 |
2925 for (var runner in runners) { | 2800 for (var runner in runners) { |
2926 if (!runner._currentlyRunning) return runner; | 2801 if (!runner._currentlyRunning) return runner; |
2927 } | 2802 } |
2928 throw new Exception('Unable to find inactive batch runner.'); | 2803 throw new Exception('Unable to find inactive batch runner.'); |
2929 } | 2804 } |
2930 | 2805 |
2931 BatchDFEProcess _getDFEProcess() { | |
2932 _dfeProcesses ??= new List<BatchDFEProcess>.generate(maxProcesses, | |
2933 (_) => new BatchDFEProcess()); | |
2934 return _dfeProcesses.firstWhere((runner) => !runner.locked); | |
2935 } | |
2936 | |
2937 Future<CommandOutput> _startBrowserControllerTest( | 2806 Future<CommandOutput> _startBrowserControllerTest( |
2938 BrowserTestCommand browserCommand, int timeout) { | 2807 BrowserTestCommand browserCommand, int timeout) { |
2939 var completer = new Completer<CommandOutput>(); | 2808 var completer = new Completer<CommandOutput>(); |
2940 | 2809 |
2941 var callback = (BrowserTestOutput output) { | 2810 var callback = (BrowserTestOutput output) { |
2942 completer | 2811 completer |
2943 .complete(new BrowserControllerTestOutcome(browserCommand, output)); | 2812 .complete(new BrowserControllerTestOutcome(browserCommand, output)); |
2944 }; | 2813 }; |
2945 | 2814 |
2946 BrowserTest browserTest; | 2815 BrowserTest browserTest; |
(...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3358 } | 3227 } |
3359 } | 3228 } |
3360 | 3229 |
3361 void eventAllTestsDone() { | 3230 void eventAllTestsDone() { |
3362 for (var listener in _eventListener) { | 3231 for (var listener in _eventListener) { |
3363 listener.allDone(); | 3232 listener.allDone(); |
3364 } | 3233 } |
3365 _allDone(); | 3234 _allDone(); |
3366 } | 3235 } |
3367 } | 3236 } |
OLD | NEW |