Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(581)

Side by Side Diff: lib/src/runner/loader.dart

Issue 1685363002: Add a platform plugin infrastructure. (Closed) Base URL: git@github.com:dart-lang/test@master
Patch Set: Code review changes Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/runner/load_suite.dart ('k') | lib/src/runner/plugin/environment.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 import 'dart:async'; 5 import 'dart:async';
6 import 'dart:io'; 6 import 'dart:io';
7 import 'dart:isolate';
8 7
9 import 'package:analyzer/analyzer.dart' hide Configuration; 8 import 'package:analyzer/analyzer.dart' hide Configuration;
10 import 'package:async/async.dart'; 9 import 'package:async/async.dart';
11 import 'package:path/path.dart' as p; 10 import 'package:path/path.dart' as p;
12 import 'package:stack_trace/stack_trace.dart';
13 11
14 import '../backend/group.dart'; 12 import '../backend/group.dart';
15 import '../backend/metadata.dart'; 13 import '../backend/metadata.dart';
16 import '../backend/test.dart';
17 import '../backend/test_platform.dart'; 14 import '../backend/test_platform.dart';
18 import '../util/dart.dart' as dart;
19 import '../util/io.dart'; 15 import '../util/io.dart';
20 import '../util/remote_exception.dart';
21 import '../utils.dart'; 16 import '../utils.dart';
22 import 'browser/server.dart'; 17 import 'browser/server.dart';
23 import 'configuration.dart'; 18 import 'configuration.dart';
24 import 'hack_load_vm_file_hook.dart';
25 import 'load_exception.dart'; 19 import 'load_exception.dart';
26 import 'load_suite.dart'; 20 import 'load_suite.dart';
27 import 'parse_metadata.dart'; 21 import 'parse_metadata.dart';
22 import 'plugin/environment.dart';
23 import 'plugin/platform.dart';
28 import 'runner_suite.dart'; 24 import 'runner_suite.dart';
29 import 'vm/environment.dart'; 25 import 'vm/platform.dart';
30 import 'vm/isolate_test.dart';
31 26
32 /// A class for finding test files and loading them into a runnable form. 27 /// A class for finding test files and loading them into a runnable form.
33 class Loader { 28 class Loader {
34 /// The test runner configuration. 29 /// The test runner configuration.
35 final Configuration _config; 30 final Configuration _config;
36 31
37 /// The root directory that will be served for browser tests. 32 /// The root directory that will be served for browser tests.
38 final String _root; 33 final String _root;
39 34
40 /// All suites that have been created by the loader. 35 /// All suites that have been created by the loader.
41 final _suites = new Set<RunnerSuite>(); 36 final _suites = new Set<RunnerSuite>();
42 37
38 /// Plugins for loading test suites for various platforms.
39 ///
40 /// This includes the built-in [VMPlatform] plugin.
41 final _platformPlugins = <TestPlatform, PlatformPlugin>{};
42
43 /// The server that serves browser test pages. 43 /// The server that serves browser test pages.
44 /// 44 ///
45 /// This is lazily initialized the first time it's accessed. 45 /// This is lazily initialized the first time it's accessed.
46 Future<BrowserServer> get _browserServer { 46 Future<BrowserServer> get _browserServer {
47 return _browserServerMemo.runOnce(() { 47 return _browserServerMemo.runOnce(() {
48 return BrowserServer.start(_config, root: _root); 48 return BrowserServer.start(_config, root: _root);
49 }); 49 });
50 } 50 }
51 final _browserServerMemo = new AsyncMemoizer<BrowserServer>(); 51 final _browserServerMemo = new AsyncMemoizer<BrowserServer>();
52 52
53 /// The memoizer for running [close] exactly once. 53 /// The memoizer for running [close] exactly once.
54 final _closeMemo = new AsyncMemoizer(); 54 final _closeMemo = new AsyncMemoizer();
55 55
56 /// Creates a new loader that loads tests on platforms defined in [_config]. 56 /// Creates a new loader that loads tests on platforms defined in [_config].
57 /// 57 ///
58 /// [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
59 /// defaults to the working directory. 59 /// defaults to the working directory.
60 Loader(this._config, {String root}) 60 Loader(this._config, {String root})
61 : _root = root == null ? p.current : root; 61 : _root = root == null ? p.current : root {
62 registerPlatformPlugin(new VMPlatform(_config));
63 }
64
65 /// Registers [plugin] as a plugin for the platforms it defines in
66 /// [PlatformPlugin.platforms].
67 ///
68 /// This overwrites previous plugins for those platforms.
69 void registerPlatformPlugin(PlatformPlugin plugin) {
70 for (var platform in plugin.platforms) {
71 _platformPlugins[platform] = plugin;
72 }
73 }
62 74
63 /// Loads all test suites in [dir]. 75 /// Loads all test suites in [dir].
64 /// 76 ///
65 /// This will load tests from files that match the configuration's filename 77 /// This will load tests from files that match the configuration's filename
66 /// glob. Any tests that fail to load will be emitted as [LoadException]s. 78 /// glob. Any tests that fail to load will be emitted as [LoadException]s.
67 /// 79 ///
68 /// This emits [LoadSuite]s that must then be run to emit the actual 80 /// This emits [LoadSuite]s that must then be run to emit the actual
69 /// [RunnerSuite]s defined in the file. 81 /// [RunnerSuite]s defined in the file.
70 Stream<LoadSuite> loadDir(String dir) { 82 Stream<LoadSuite> loadDir(String dir) {
71 return mergeStreams(new Directory(dir).listSync(recursive: true) 83 return mergeStreams(new Directory(dir).listSync(recursive: true)
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
112 } 124 }
113 125
114 for (var platform in _config.platforms) { 126 for (var platform in _config.platforms) {
115 if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue; 127 if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue;
116 128
117 var metadata = suiteMetadata.forPlatform(platform, os: currentOS); 129 var metadata = suiteMetadata.forPlatform(platform, os: currentOS);
118 130
119 // Don't load a skipped suite. 131 // Don't load a skipped suite.
120 if (metadata.skip) { 132 if (metadata.skip) {
121 yield new LoadSuite.forSuite(new RunnerSuite( 133 yield new LoadSuite.forSuite(new RunnerSuite(
122 const VMEnvironment(), 134 const PluginEnvironment(),
123 new Group.root([], metadata: metadata), 135 new Group.root([], metadata: metadata),
124 path: path, platform: platform)); 136 path: path, platform: platform));
125 continue; 137 continue;
126 } 138 }
127 139
128 var name = (platform.isJS ? "compiling " : "loading ") + path; 140 var name = (platform.isJS ? "compiling " : "loading ") + path;
129 yield new LoadSuite(name, () { 141 yield new LoadSuite(name, () async {
130 return platform == TestPlatform.vm 142 var plugin = _platformPlugins[platform];
131 ? _loadVmFile(path, metadata) 143
132 : _loadBrowserFile(path, platform, metadata); 144 if (plugin != null) {
145 try {
146 return await plugin.load(path, platform, metadata);
147 } catch (error, stackTrace) {
148 if (error is LoadException) rethrow;
149 await new Future.error(new LoadException(path, error), stackTrace);
150 }
151 }
152
153 assert(platform.isBrowser);
154 return _loadBrowserFile(path, platform, metadata);
133 }, path: path, platform: platform); 155 }, path: path, platform: platform);
134 } 156 }
135 } 157 }
136 158
137 /// Load the test suite at [path] in [platform]. 159 /// Load the test suite at [path] in [platform].
138 /// 160 ///
139 /// [metadata] is the suite-level metadata for the test. 161 /// [metadata] is the suite-level metadata for the test.
140 Future<RunnerSuite> _loadBrowserFile(String path, TestPlatform platform, 162 Future<RunnerSuite> _loadBrowserFile(String path, TestPlatform platform,
141 Metadata metadata) async => 163 Metadata metadata) async =>
142 (await _browserServer).loadSuite(path, platform, metadata); 164 (await _browserServer).loadSuite(path, platform, metadata);
143 165
144 /// Load the test suite at [path] in VM isolate.
145 ///
146 /// [metadata] is the suite-level metadata for the test.
147 Future<RunnerSuite> _loadVmFile(String path, Metadata metadata) async {
148 if (loadVMFileHook != null) {
149 var suite = await loadVMFileHook(path, metadata, _config);
150 _suites.add(suite);
151 return suite;
152 }
153
154 var receivePort = new ReceivePort();
155
156 var isolate;
157 try {
158 if (_config.pubServeUrl != null) {
159 var url = _config.pubServeUrl.resolveUri(
160 p.toUri(p.relative(path, from: 'test') + '.vm_test.dart'));
161
162 try {
163 isolate = await Isolate.spawnUri(url, [], {
164 'reply': receivePort.sendPort,
165 'metadata': metadata.serialize()
166 }, checked: true);
167 } on IsolateSpawnException catch (error) {
168 if (error.message.contains("OS Error: Connection refused") ||
169 error.message.contains("The remote computer refused")) {
170 throw new LoadException(path,
171 "Error getting $url: Connection refused\n"
172 'Make sure "pub serve" is running.');
173 } else if (error.message.contains("404 Not Found")) {
174 throw new LoadException(path,
175 "Error getting $url: 404 Not Found\n"
176 'Make sure "pub serve" is serving the test/ directory.');
177 }
178
179 throw new LoadException(path, error);
180 }
181 } else {
182 isolate = await dart.runInIsolate('''
183 import "package:test/src/backend/metadata.dart";
184 import "package:test/src/runner/vm/isolate_listener.dart";
185
186 import "${p.toUri(p.absolute(path))}" as test;
187
188 void main(_, Map message) {
189 var sendPort = message['reply'];
190 var metadata = new Metadata.deserialize(message['metadata']);
191 IsolateListener.start(sendPort, metadata, () => test.main);
192 }
193 ''', {
194 'reply': receivePort.sendPort,
195 'metadata': metadata.serialize()
196 }, packageRoot: p.toUri(_config.packageRoot), checked: true);
197 }
198 } catch (error, stackTrace) {
199 receivePort.close();
200 if (error is LoadException) rethrow;
201 await new Future.error(new LoadException(path, error), stackTrace);
202 }
203
204 var completer = new Completer();
205
206 var subscription = receivePort.listen((response) {
207 if (response["type"] == "print") {
208 print(response["line"]);
209 } else if (response["type"] == "loadException") {
210 isolate.kill();
211 completer.completeError(
212 new LoadException(path, response["message"]),
213 new Trace.current());
214 } else if (response["type"] == "error") {
215 isolate.kill();
216 var asyncError = RemoteException.deserialize(response["error"]);
217 completer.completeError(
218 new LoadException(path, asyncError.error),
219 asyncError.stackTrace);
220 } else {
221 assert(response["type"] == "success");
222 completer.complete(response["root"]);
223 }
224 });
225
226 try {
227 var suite = new RunnerSuite(
228 const VMEnvironment(),
229 _deserializeGroup(await completer.future),
230 path: path,
231 platform: TestPlatform.vm,
232 os: currentOS,
233 onClose: isolate.kill);
234 _suites.add(suite);
235 return suite;
236 } finally {
237 subscription.cancel();
238 }
239 }
240
241 /// Deserializes [group] into a concrete [Group] class.
242 Group _deserializeGroup(Map group) {
243 var metadata = new Metadata.deserialize(group['metadata']);
244 return new Group(group['name'], group['entries'].map((entry) {
245 if (entry['type'] == 'group') return _deserializeGroup(entry);
246 return _deserializeTest(entry);
247 }),
248 metadata: metadata,
249 setUpAll: _deserializeTest(group['setUpAll']),
250 tearDownAll: _deserializeTest(group['tearDownAll']));
251 }
252
253 /// Deserializes [test] into a concrete [Test] class.
254 ///
255 /// Returns `null` if [test] is `null`.
256 Test _deserializeTest(Map test) {
257 if (test == null) return null;
258
259 var metadata = new Metadata.deserialize(test['metadata']);
260 return new IsolateTest(test['name'], metadata, test['sendPort']);
261 }
262
263 /// Close all the browsers that the loader currently has open. 166 /// Close all the browsers that the loader currently has open.
264 /// 167 ///
265 /// Note that this doesn't close the loader itself. Browser tests can still be 168 /// Note that this doesn't close the loader itself. Browser tests can still be
266 /// loaded, they'll just spawn new browsers. 169 /// loaded, they'll just spawn new browsers.
267 Future closeBrowsers() async { 170 Future closeBrowsers() async {
268 if (!_browserServerMemo.hasRun) return; 171 if (!_browserServerMemo.hasRun) return;
269 await (await _browserServer).closeBrowsers(); 172 await (await _browserServer).closeBrowsers();
270 } 173 }
271 174
272 /// Closes the loader and releases all resources allocated by it. 175 /// Closes the loader and releases all resources allocated by it.
273 Future close() { 176 Future close() {
274 return _closeMemo.runOnce(() async { 177 return _closeMemo.runOnce(() async {
275 await Future.wait(_suites.map((suite) => suite.close())); 178 await Future.wait(_suites.map((suite) => suite.close()));
276 _suites.clear(); 179 _suites.clear();
277 180
278 if (!_browserServerMemo.hasRun) return; 181 if (!_browserServerMemo.hasRun) return;
279 await (await _browserServer).close(); 182 await (await _browserServer).close();
280 }); 183 });
281 } 184 }
282 } 185 }
OLDNEW
« no previous file with comments | « lib/src/runner/load_suite.dart ('k') | lib/src/runner/plugin/environment.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698