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 |