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:path/path.dart' as p; | 12 import 'package:path/path.dart' as p; |
| 13 import 'package:stack_trace/stack_trace.dart'; |
13 | 14 |
14 import '../backend/invoker.dart'; | 15 import '../backend/invoker.dart'; |
15 import '../backend/metadata.dart'; | 16 import '../backend/metadata.dart'; |
16 import '../backend/suite.dart'; | 17 import '../backend/suite.dart'; |
17 import '../backend/test_platform.dart'; | 18 import '../backend/test_platform.dart'; |
18 import '../util/async_thunk.dart'; | 19 import '../util/async_thunk.dart'; |
19 import '../util/dart.dart' as dart; | 20 import '../util/dart.dart' as dart; |
20 import '../util/io.dart'; | 21 import '../util/io.dart'; |
21 import '../util/isolate_wrapper.dart'; | 22 import '../util/isolate_wrapper.dart'; |
22 import '../util/remote_exception.dart'; | 23 import '../util/remote_exception.dart'; |
23 import '../utils.dart'; | 24 import '../utils.dart'; |
24 import 'browser/server.dart'; | 25 import 'browser/server.dart'; |
25 import 'load_exception.dart'; | 26 import 'load_exception.dart'; |
| 27 import 'load_suite.dart'; |
26 import 'parse_metadata.dart'; | 28 import 'parse_metadata.dart'; |
27 import 'vm/isolate_test.dart'; | 29 import 'vm/isolate_test.dart'; |
28 | 30 |
29 /// A class for finding test files and loading them into a runnable form. | 31 /// A class for finding test files and loading them into a runnable form. |
30 class Loader { | 32 class Loader { |
31 /// All platforms for which tests should be loaded. | 33 /// All platforms for which tests should be loaded. |
32 final List<TestPlatform> _platforms; | 34 final List<TestPlatform> _platforms; |
33 | 35 |
34 /// Whether to enable colors for Dart compilation. | 36 /// Whether to enable colors for Dart compilation. |
35 final bool _color; | 37 final bool _color; |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
97 _root = root == null ? p.current : root, | 99 _root = root == null ? p.current : root, |
98 _packageRoot = packageRootFor(root, packageRoot), | 100 _packageRoot = packageRootFor(root, packageRoot), |
99 _color = color, | 101 _color = color, |
100 _jsTrace = jsTrace, | 102 _jsTrace = jsTrace, |
101 _metadata = metadata == null ? new Metadata() : metadata; | 103 _metadata = metadata == null ? new Metadata() : metadata; |
102 | 104 |
103 /// Loads all test suites in [dir]. | 105 /// Loads all test suites in [dir]. |
104 /// | 106 /// |
105 /// This will load tests from files that end in "_test.dart". Any tests that | 107 /// This will load tests from files that end in "_test.dart". Any tests that |
106 /// fail to load will be emitted as [LoadException]s. | 108 /// fail to load will be emitted as [LoadException]s. |
107 Stream<Suite> loadDir(String dir) { | 109 /// |
| 110 /// This emits [LoadSuite]s that must then be run to emit the actual [Suite]s |
| 111 /// defined in the file. |
| 112 Stream<LoadSuite> loadDir(String dir) { |
108 return mergeStreams(new Directory(dir).listSync(recursive: true) | 113 return mergeStreams(new Directory(dir).listSync(recursive: true) |
109 .map((entry) { | 114 .map((entry) { |
110 if (entry is! File) return new Stream.fromIterable([]); | 115 if (entry is! File) return new Stream.fromIterable([]); |
111 | 116 |
112 if (!entry.path.endsWith("_test.dart")) { | 117 if (!entry.path.endsWith("_test.dart")) { |
113 return new Stream.fromIterable([]); | 118 return new Stream.fromIterable([]); |
114 } | 119 } |
115 | 120 |
116 if (p.split(entry.path).contains('packages')) { | 121 if (p.split(entry.path).contains('packages')) { |
117 return new Stream.fromIterable([]); | 122 return new Stream.fromIterable([]); |
118 } | 123 } |
119 | 124 |
120 return loadFile(entry.path); | 125 return loadFile(entry.path); |
121 })); | 126 })); |
122 } | 127 } |
123 | 128 |
124 /// Loads a test suite from the file at [path]. | 129 /// Loads a test suite from the file at [path]. |
125 /// | 130 /// |
| 131 /// This emits [LoadSuite]s that must then be run to emit the actual [Suite]s |
| 132 /// defined in the file. |
| 133 /// |
126 /// This will emit a [LoadException] if the file fails to load. | 134 /// This will emit a [LoadException] if the file fails to load. |
127 Stream<Suite> loadFile(String path) async* { | 135 Stream<LoadSuite> loadFile(String path) async* { |
128 var suiteMetadata; | 136 var suiteMetadata; |
129 try { | 137 try { |
130 suiteMetadata = parseMetadata(path); | 138 suiteMetadata = parseMetadata(path); |
131 } on AnalyzerErrorGroup catch (_) { | 139 } on AnalyzerErrorGroup catch (_) { |
132 // Ignore the analyzer's error, since its formatting is much worse than | 140 // Ignore the analyzer's error, since its formatting is much worse than |
133 // the VM's or dart2js's. | 141 // the VM's or dart2js's. |
134 suiteMetadata = new Metadata(); | 142 suiteMetadata = new Metadata(); |
135 } on FormatException catch (error, stackTrace) { | 143 } on FormatException catch (error, stackTrace) { |
136 await new Future.error(new LoadException(path, error), stackTrace); | 144 yield new LoadSuite.forLoadException( |
| 145 new LoadException(path, error), stackTrace: stackTrace); |
| 146 return; |
137 } | 147 } |
138 suiteMetadata = _metadata.merge(suiteMetadata); | 148 suiteMetadata = _metadata.merge(suiteMetadata); |
139 | 149 |
| 150 if (_pubServeUrl != null && !p.isWithin('test', path)) { |
| 151 yield new LoadSuite.forLoadException(new LoadException( |
| 152 path, 'When using "pub serve", all test files must be in test/.')); |
| 153 return; |
| 154 } |
| 155 |
140 for (var platform in _platforms) { | 156 for (var platform in _platforms) { |
141 if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue; | 157 if (!suiteMetadata.testOn.evaluate(platform, os: currentOS)) continue; |
142 | 158 |
143 var metadata = suiteMetadata.forPlatform(platform, os: currentOS); | 159 var metadata = suiteMetadata.forPlatform(platform, os: currentOS); |
144 | 160 |
145 // Don't load a skipped suite. | 161 // Don't load a skipped suite. |
146 if (metadata.skip) { | 162 if (metadata.skip) { |
147 yield new Suite([ | 163 yield new LoadSuite.forSuite(new Suite([ |
148 new LocalTest(path, metadata, () {}) | 164 new LocalTest(path, metadata, () {}) |
149 ], path: path, platform: platform.name, metadata: metadata); | 165 ], path: path, platform: platform.name, metadata: metadata)); |
150 continue; | 166 continue; |
151 } | 167 } |
152 | 168 |
153 try { | 169 var name = (platform.isJS ? "compiling " : "loading ") + path; |
154 if (_pubServeUrl != null && !p.isWithin('test', path)) { | 170 yield new LoadSuite(name, () { |
155 throw new LoadException(path, | 171 return platform == TestPlatform.vm |
156 'When using "pub serve", all test files must be in test/.'); | 172 ? _loadVmFile(path, metadata) |
157 } | 173 : _loadBrowserFile(path, platform, metadata); |
158 | 174 }, platform: platform.name); |
159 var suite = platform == TestPlatform.vm | |
160 ? await _loadVmFile(path, metadata) | |
161 : await _loadBrowserFile(path, platform, metadata); | |
162 if (suite != null) yield suite; | |
163 } catch (error, stackTrace) { | |
164 yield* errorStream(error, stackTrace); | |
165 } | |
166 } | 175 } |
167 } | 176 } |
168 | 177 |
169 /// Load the test suite at [path] in [platform]. | 178 /// Load the test suite at [path] in [platform]. |
170 /// | 179 /// |
171 /// [metadata] is the suite-level metadata for the test. | 180 /// [metadata] is the suite-level metadata for the test. |
172 Future<Suite> _loadBrowserFile(String path, TestPlatform platform, | 181 Future<Suite> _loadBrowserFile(String path, TestPlatform platform, |
173 Metadata metadata) async => | 182 Metadata metadata) async => |
174 (await _browserServer).loadSuite(path, platform, metadata); | 183 (await _browserServer).loadSuite(path, platform, metadata); |
175 | 184 |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
225 'metadata': metadata.serialize() | 234 'metadata': metadata.serialize() |
226 }, packageRoot: p.toUri(_packageRoot), checked: true); | 235 }, packageRoot: p.toUri(_packageRoot), checked: true); |
227 } | 236 } |
228 } catch (error, stackTrace) { | 237 } catch (error, stackTrace) { |
229 receivePort.close(); | 238 receivePort.close(); |
230 if (error is LoadException) rethrow; | 239 if (error is LoadException) rethrow; |
231 await new Future.error(new LoadException(path, error), stackTrace); | 240 await new Future.error(new LoadException(path, error), stackTrace); |
232 } | 241 } |
233 | 242 |
234 _isolates.add(isolate); | 243 _isolates.add(isolate); |
235 var response = await receivePort.first; | |
236 | 244 |
237 if (response["type"] == "loadException") { | 245 var completer = new Completer(); |
238 throw new LoadException(path, response["message"]); | 246 |
239 } else if (response["type"] == "error") { | 247 var subscription = receivePort.listen((response) { |
240 var asyncError = RemoteException.deserialize(response["error"]); | 248 if (response["type"] == "print") { |
241 await new Future.error( | 249 print(response["line"]); |
242 new LoadException(path, asyncError.error), | 250 } else if (response["type"] == "loadException") { |
243 asyncError.stackTrace); | 251 completer.completeError( |
| 252 new LoadException(path, response["message"]), |
| 253 new Trace.current()); |
| 254 } else if (response["type"] == "error") { |
| 255 var asyncError = RemoteException.deserialize(response["error"]); |
| 256 completer.completeError( |
| 257 new LoadException(path, asyncError.error), |
| 258 asyncError.stackTrace); |
| 259 } else { |
| 260 assert(response["type"] == "success"); |
| 261 completer.complete(response["tests"]); |
| 262 } |
| 263 }); |
| 264 |
| 265 try { |
| 266 return new Suite((await completer.future).map((test) { |
| 267 var testMetadata = new Metadata.deserialize(test['metadata']); |
| 268 return new IsolateTest(test['name'], testMetadata, test['sendPort']); |
| 269 }), metadata: metadata, path: path, platform: "VM"); |
| 270 } finally { |
| 271 subscription.cancel(); |
244 } | 272 } |
245 | |
246 return new Suite(response["tests"].map((test) { | |
247 var testMetadata = new Metadata.deserialize(test['metadata']); | |
248 return new IsolateTest(test['name'], testMetadata, test['sendPort']); | |
249 }), metadata: metadata, path: path, platform: "VM"); | |
250 } | 273 } |
251 | 274 |
252 /// Closes the loader and releases all resources allocated by it. | 275 /// Closes the loader and releases all resources allocated by it. |
253 Future close() { | 276 Future close() { |
254 return _closeThunk.run(() async { | 277 return _closeThunk.run(() async { |
255 for (var isolate in _isolates) { | 278 for (var isolate in _isolates) { |
256 isolate.kill(); | 279 isolate.kill(); |
257 } | 280 } |
258 _isolates.clear(); | 281 _isolates.clear(); |
259 | 282 |
260 if (!_browserServerThunk.hasRun) return; | 283 if (!_browserServerThunk.hasRun) return; |
261 await (await _browserServer).close(); | 284 await (await _browserServer).close(); |
262 }); | 285 }); |
263 } | 286 } |
264 } | 287 } |
OLD | NEW |