| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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.loader; | 5 library test.runner.loader; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 import 'dart:isolate'; | 9 import 'dart:isolate'; |
| 10 | 10 |
| 11 import 'package:analyzer/analyzer.dart'; | 11 import 'package:analyzer/analyzer.dart'; |
| 12 import 'package:async/async.dart'; | 12 import 'package:async/async.dart'; |
| 13 import 'package:path/path.dart' as p; | 13 import 'package:path/path.dart' as p; |
| 14 import 'package:stack_trace/stack_trace.dart'; | 14 import 'package:stack_trace/stack_trace.dart'; |
| 15 | 15 |
| 16 import '../backend/invoker.dart'; | 16 import '../backend/invoker.dart'; |
| 17 import '../backend/metadata.dart'; | 17 import '../backend/metadata.dart'; |
| 18 import '../backend/test_platform.dart'; | 18 import '../backend/test_platform.dart'; |
| 19 import '../util/dart.dart' as dart; | 19 import '../util/dart.dart' as dart; |
| 20 import '../util/io.dart'; | 20 import '../util/io.dart'; |
| 21 import '../util/remote_exception.dart'; | 21 import '../util/remote_exception.dart'; |
| 22 import '../utils.dart'; | 22 import '../utils.dart'; |
| 23 import 'configuration.dart'; |
| 23 import 'browser/server.dart'; | 24 import 'browser/server.dart'; |
| 24 import 'load_exception.dart'; | 25 import 'load_exception.dart'; |
| 25 import 'load_suite.dart'; | 26 import 'load_suite.dart'; |
| 26 import 'parse_metadata.dart'; | 27 import 'parse_metadata.dart'; |
| 27 import 'runner_suite.dart'; | 28 import 'runner_suite.dart'; |
| 28 import 'vm/environment.dart'; | 29 import 'vm/environment.dart'; |
| 29 import 'vm/isolate_test.dart'; | 30 import 'vm/isolate_test.dart'; |
| 30 | 31 |
| 31 /// A class for finding test files and loading them into a runnable form. | 32 /// A class for finding test files and loading them into a runnable form. |
| 32 class Loader { | 33 class Loader { |
| 33 /// All platforms for which tests should be loaded. | 34 /// The test runner configuration. |
| 34 final List<TestPlatform> _platforms; | 35 final Configuration _config; |
| 35 | |
| 36 /// Whether to enable colors for Dart compilation. | |
| 37 final bool _color; | |
| 38 | |
| 39 /// Whether raw JavaScript stack traces should be used for tests that are | |
| 40 /// compiled to JavaScript. | |
| 41 final bool _jsTrace; | |
| 42 | |
| 43 /// Global metadata that applies to all test suites. | |
| 44 final Metadata _metadata; | |
| 45 | 36 |
| 46 /// The root directory that will be served for browser tests. | 37 /// The root directory that will be served for browser tests. |
| 47 final String _root; | 38 final String _root; |
| 48 | 39 |
| 49 /// The package root to use for loading tests. | |
| 50 final String _packageRoot; | |
| 51 | |
| 52 /// The URL for the `pub serve` instance to use to load tests. | |
| 53 /// | |
| 54 /// This is `null` if tests should be loaded from the filesystem. | |
| 55 final Uri _pubServeUrl; | |
| 56 | |
| 57 /// All suites that have been created by the loader. | 40 /// All suites that have been created by the loader. |
| 58 final _suites = new Set<RunnerSuite>(); | 41 final _suites = new Set<RunnerSuite>(); |
| 59 | 42 |
| 60 /// The server that serves browser test pages. | 43 /// The server that serves browser test pages. |
| 61 /// | 44 /// |
| 62 /// This is lazily initialized the first time it's accessed. | 45 /// This is lazily initialized the first time it's accessed. |
| 63 Future<BrowserServer> get _browserServer { | 46 Future<BrowserServer> get _browserServer { |
| 64 return _browserServerMemo.runOnce(() { | 47 return _browserServerMemo.runOnce(() { |
| 65 return BrowserServer.start( | 48 return BrowserServer.start(_config, root: _root); |
| 66 root: _root, | |
| 67 packageRoot: _packageRoot, | |
| 68 pubServeUrl: _pubServeUrl, | |
| 69 color: _color, | |
| 70 jsTrace: _jsTrace); | |
| 71 }); | 49 }); |
| 72 } | 50 } |
| 73 final _browserServerMemo = new AsyncMemoizer<BrowserServer>(); | 51 final _browserServerMemo = new AsyncMemoizer<BrowserServer>(); |
| 74 | 52 |
| 75 /// The memoizer for running [close] exactly once. | 53 /// The memoizer for running [close] exactly once. |
| 76 final _closeMemo = new AsyncMemoizer(); | 54 final _closeMemo = new AsyncMemoizer(); |
| 77 | 55 |
| 78 /// Creates a new loader. | 56 /// Creates a new loader that loads tests on platforms defined in [_config]. |
| 79 /// | 57 /// |
| 80 /// [root] is the root directory that will be served for browser tests. It | 58 /// [root] is the root directory that will be served for browser tests. It |
| 81 /// defaults to the working directory. | 59 /// defaults to the working directory. |
| 82 /// | 60 Loader(this._config, {String root}) |
| 83 /// If [packageRoot] is passed, it's used as the package root for all loaded | 61 : _root = root == null ? p.current : root; |
| 84 /// tests. Otherwise, it's inferred from [root]. | |
| 85 /// | |
| 86 /// If [pubServeUrl] is passed, tests will be loaded from the `pub serve` | |
| 87 /// instance at that URL rather than from the filesystem. | |
| 88 /// | |
| 89 /// If [color] is true, console colors will be used when compiling Dart. | |
| 90 /// | |
| 91 /// [metadata] is the global metadata for all test suites. | |
| 92 /// | |
| 93 /// If the package root doesn't exist, throws an [ApplicationException]. | |
| 94 Loader(Iterable<TestPlatform> platforms, {String root, String packageRoot, | |
| 95 Uri pubServeUrl, bool color: false, bool jsTrace: false, | |
| 96 Metadata metadata}) | |
| 97 : _platforms = platforms.toList(), | |
| 98 _pubServeUrl = pubServeUrl, | |
| 99 _root = root == null ? p.current : root, | |
| 100 _packageRoot = packageRootFor(root, packageRoot), | |
| 101 _color = color, | |
| 102 _jsTrace = jsTrace, | |
| 103 _metadata = metadata == null ? new Metadata() : metadata; | |
| 104 | 62 |
| 105 /// Loads all test suites in [dir]. | 63 /// Loads all test suites in [dir]. |
| 106 /// | 64 /// |
| 107 /// This will load tests from files that end in "_test.dart". Any tests that | 65 /// This will load tests from files that end in "_test.dart". Any tests that |
| 108 /// fail to load will be emitted as [LoadException]s. | 66 /// fail to load will be emitted as [LoadException]s. |
| 109 /// | 67 /// |
| 110 /// This emits [LoadSuite]s that must then be run to emit the actual | 68 /// This emits [LoadSuite]s that must then be run to emit the actual |
| 111 /// [RunnerSuite]s defined in the file. | 69 /// [RunnerSuite]s defined in the file. |
| 112 Stream<LoadSuite> loadDir(String dir) { | 70 Stream<LoadSuite> loadDir(String dir) { |
| 113 return mergeStreams(new Directory(dir).listSync(recursive: true) | 71 return mergeStreams(new Directory(dir).listSync(recursive: true) |
| (...skipping 24 matching lines...) Expand all Loading... |
| 138 suiteMetadata = parseMetadata(path); | 96 suiteMetadata = parseMetadata(path); |
| 139 } on AnalyzerErrorGroup catch (_) { | 97 } on AnalyzerErrorGroup catch (_) { |
| 140 // Ignore the analyzer's error, since its formatting is much worse than | 98 // Ignore the analyzer's error, since its formatting is much worse than |
| 141 // the VM's or dart2js's. | 99 // the VM's or dart2js's. |
| 142 suiteMetadata = new Metadata(); | 100 suiteMetadata = new Metadata(); |
| 143 } on FormatException catch (error, stackTrace) { | 101 } on FormatException catch (error, stackTrace) { |
| 144 yield new LoadSuite.forLoadException( | 102 yield new LoadSuite.forLoadException( |
| 145 new LoadException(path, error), stackTrace: stackTrace); | 103 new LoadException(path, error), stackTrace: stackTrace); |
| 146 return; | 104 return; |
| 147 } | 105 } |
| 148 suiteMetadata = _metadata.merge(suiteMetadata); | 106 suiteMetadata = _config.metadata.merge(suiteMetadata); |
| 149 | 107 |
| 150 if (_pubServeUrl != null && !p.isWithin('test', path)) { | 108 if (_config.pubServeUrl != null && !p.isWithin('test', path)) { |
| 151 yield new LoadSuite.forLoadException(new LoadException( | 109 yield new LoadSuite.forLoadException(new LoadException( |
| 152 path, 'When using "pub serve", all test files must be in test/.')); | 110 path, 'When using "pub serve", all test files must be in test/.')); |
| 153 return; | 111 return; |
| 154 } | 112 } |
| 155 | 113 |
| 156 for (var platform in _platforms) { | 114 for (var platform in _config.platforms) { |
| 157 if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue; | 115 if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue; |
| 158 | 116 |
| 159 var metadata = suiteMetadata.forPlatform(platform, os: currentOS); | 117 var metadata = suiteMetadata.forPlatform(platform, os: currentOS); |
| 160 | 118 |
| 161 // Don't load a skipped suite. | 119 // Don't load a skipped suite. |
| 162 if (metadata.skip) { | 120 if (metadata.skip) { |
| 163 yield new LoadSuite.forSuite(new RunnerSuite(const VMEnvironment(), [ | 121 yield new LoadSuite.forSuite(new RunnerSuite(const VMEnvironment(), [ |
| 164 new LocalTest(path, metadata, () {}) | 122 new LocalTest(path, metadata, () {}) |
| 165 ], path: path, platform: platform, metadata: metadata)); | 123 ], path: path, platform: platform, metadata: metadata)); |
| 166 continue; | 124 continue; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 183 (await _browserServer).loadSuite(path, platform, metadata); | 141 (await _browserServer).loadSuite(path, platform, metadata); |
| 184 | 142 |
| 185 /// Load the test suite at [path] in VM isolate. | 143 /// Load the test suite at [path] in VM isolate. |
| 186 /// | 144 /// |
| 187 /// [metadata] is the suite-level metadata for the test. | 145 /// [metadata] is the suite-level metadata for the test. |
| 188 Future<RunnerSuite> _loadVmFile(String path, Metadata metadata) async { | 146 Future<RunnerSuite> _loadVmFile(String path, Metadata metadata) async { |
| 189 var receivePort = new ReceivePort(); | 147 var receivePort = new ReceivePort(); |
| 190 | 148 |
| 191 var isolate; | 149 var isolate; |
| 192 try { | 150 try { |
| 193 if (_pubServeUrl != null) { | 151 if (_config.pubServeUrl != null) { |
| 194 var url = _pubServeUrl.resolveUri( | 152 var url = _config.pubServeUrl.resolveUri( |
| 195 p.toUri(p.relative(path, from: 'test') + '.vm_test.dart')); | 153 p.toUri(p.relative(path, from: 'test') + '.vm_test.dart')); |
| 196 | 154 |
| 197 // TODO(nweiz): Remove new Future.sync() once issue 23498 has been fixed | 155 // TODO(nweiz): Remove new Future.sync() once issue 23498 has been fixed |
| 198 // in two stable versions. | 156 // in two stable versions. |
| 199 await new Future.sync(() async { | 157 await new Future.sync(() async { |
| 200 try { | 158 try { |
| 201 isolate = await dart.spawnUri(url, { | 159 isolate = await dart.spawnUri(url, { |
| 202 'reply': receivePort.sendPort, | 160 'reply': receivePort.sendPort, |
| 203 'metadata': metadata.serialize() | 161 'metadata': metadata.serialize() |
| 204 }, checked: true); | 162 }, checked: true); |
| (...skipping 20 matching lines...) Expand all Loading... |
| 225 import "${p.toUri(p.absolute(path))}" as test; | 183 import "${p.toUri(p.absolute(path))}" as test; |
| 226 | 184 |
| 227 void main(_, Map message) { | 185 void main(_, Map message) { |
| 228 var sendPort = message['reply']; | 186 var sendPort = message['reply']; |
| 229 var metadata = new Metadata.deserialize(message['metadata']); | 187 var metadata = new Metadata.deserialize(message['metadata']); |
| 230 IsolateListener.start(sendPort, metadata, () => test.main); | 188 IsolateListener.start(sendPort, metadata, () => test.main); |
| 231 } | 189 } |
| 232 ''', { | 190 ''', { |
| 233 'reply': receivePort.sendPort, | 191 'reply': receivePort.sendPort, |
| 234 'metadata': metadata.serialize() | 192 'metadata': metadata.serialize() |
| 235 }, packageRoot: p.toUri(_packageRoot), checked: true); | 193 }, packageRoot: p.toUri(_config.packageRoot), checked: true); |
| 236 } | 194 } |
| 237 } catch (error, stackTrace) { | 195 } catch (error, stackTrace) { |
| 238 receivePort.close(); | 196 receivePort.close(); |
| 239 if (error is LoadException) rethrow; | 197 if (error is LoadException) rethrow; |
| 240 await new Future.error(new LoadException(path, error), stackTrace); | 198 await new Future.error(new LoadException(path, error), stackTrace); |
| 241 } | 199 } |
| 242 | 200 |
| 243 var completer = new Completer(); | 201 var completer = new Completer(); |
| 244 | 202 |
| 245 var subscription = receivePort.listen((response) { | 203 var subscription = receivePort.listen((response) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 284 Future close() { | 242 Future close() { |
| 285 return _closeMemo.runOnce(() async { | 243 return _closeMemo.runOnce(() async { |
| 286 await Future.wait(_suites.map((suite) => suite.close())); | 244 await Future.wait(_suites.map((suite) => suite.close())); |
| 287 _suites.clear(); | 245 _suites.clear(); |
| 288 | 246 |
| 289 if (!_browserServerMemo.hasRun) return; | 247 if (!_browserServerMemo.hasRun) return; |
| 290 await (await _browserServer).close(); | 248 await (await _browserServer).close(); |
| 291 }); | 249 }); |
| 292 } | 250 } |
| 293 } | 251 } |
| OLD | NEW |