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.browser.browser_manager; | 5 library test.runner.browser.browser_manager; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 | 9 |
10 import 'package:async/async.dart'; | 10 import 'package:async/async.dart'; |
11 import 'package:http_parser/http_parser.dart'; | 11 import 'package:http_parser/http_parser.dart'; |
12 import 'package:pool/pool.dart'; | 12 import 'package:pool/pool.dart'; |
13 | 13 |
| 14 import '../../backend/group.dart'; |
14 import '../../backend/metadata.dart'; | 15 import '../../backend/metadata.dart'; |
| 16 import '../../backend/test.dart'; |
15 import '../../backend/test_platform.dart'; | 17 import '../../backend/test_platform.dart'; |
16 import '../../util/multi_channel.dart'; | 18 import '../../util/multi_channel.dart'; |
| 19 import '../../util/remote_exception.dart'; |
17 import '../../util/stack_trace_mapper.dart'; | 20 import '../../util/stack_trace_mapper.dart'; |
18 import '../../utils.dart'; | 21 import '../../utils.dart'; |
19 import '../application_exception.dart'; | 22 import '../application_exception.dart'; |
20 import '../environment.dart'; | 23 import '../environment.dart'; |
| 24 import '../load_exception.dart'; |
21 import '../runner_suite.dart'; | 25 import '../runner_suite.dart'; |
22 import 'browser.dart'; | 26 import 'browser.dart'; |
23 import 'chrome.dart'; | 27 import 'chrome.dart'; |
24 import 'content_shell.dart'; | 28 import 'content_shell.dart'; |
25 import 'dartium.dart'; | 29 import 'dartium.dart'; |
26 import 'firefox.dart'; | 30 import 'firefox.dart'; |
| 31 import 'iframe_test.dart'; |
27 import 'internet_explorer.dart'; | 32 import 'internet_explorer.dart'; |
28 import 'phantom_js.dart'; | 33 import 'phantom_js.dart'; |
29 import 'safari.dart'; | 34 import 'safari.dart'; |
30 import 'suite.dart'; | |
31 | 35 |
32 /// A class that manages the connection to a single running browser. | 36 /// A class that manages the connection to a single running browser. |
33 /// | 37 /// |
34 /// This is in charge of telling the browser which test suites to load and | 38 /// This is in charge of telling the browser which test suites to load and |
35 /// converting its responses into [Suite] objects. | 39 /// converting its responses into [Suite] objects. |
36 class BrowserManager { | 40 class BrowserManager { |
37 /// The browser instance that this is connected to via [_channel]. | 41 /// The browser instance that this is connected to via [_channel]. |
38 final Browser _browser; | 42 final Browser _browser; |
39 | 43 |
40 // TODO(nweiz): Consider removing the duplication between this and | 44 // TODO(nweiz): Consider removing the duplication between this and |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
169 {StackTraceMapper mapper}) async { | 173 {StackTraceMapper mapper}) async { |
170 url = url.replace(fragment: Uri.encodeFull(JSON.encode({ | 174 url = url.replace(fragment: Uri.encodeFull(JSON.encode({ |
171 "metadata": metadata.serialize(), | 175 "metadata": metadata.serialize(), |
172 "browser": _platform.identifier | 176 "browser": _platform.identifier |
173 }))); | 177 }))); |
174 | 178 |
175 // The stream may close before emitting a value if the browser is killed | 179 // The stream may close before emitting a value if the browser is killed |
176 // prematurely (e.g. via Control-C). | 180 // prematurely (e.g. via Control-C). |
177 var suiteVirtualChannel = _channel.virtualChannel(); | 181 var suiteVirtualChannel = _channel.virtualChannel(); |
178 var suiteId = _suiteId++; | 182 var suiteId = _suiteId++; |
| 183 var suiteChannel; |
179 | 184 |
180 closeIframe() { | 185 closeIframe() { |
181 if (_closed) return; | 186 if (_closed) return; |
| 187 suiteChannel.sink.close(); |
182 _channel.sink.add({ | 188 _channel.sink.add({ |
183 "command": "closeSuite", | 189 "command": "closeSuite", |
184 "id": suiteId | 190 "id": suiteId |
185 }); | 191 }); |
186 } | 192 } |
187 | 193 |
188 return await _pool.withResource(() async { | 194 var response = await _pool.withResource(() { |
189 _channel.sink.add({ | 195 _channel.sink.add({ |
190 "command": "loadSuite", | 196 "command": "loadSuite", |
191 "url": url.toString(), | 197 "url": url.toString(), |
192 "id": suiteId, | 198 "id": suiteId, |
193 "channel": suiteVirtualChannel.id | 199 "channel": suiteVirtualChannel.id |
194 }); | 200 }); |
195 | 201 |
196 try { | 202 // Create a nested MultiChannel because the iframe will be using a channel |
197 return await loadBrowserSuite( | 203 // wrapped within the host's channel. |
198 suiteVirtualChannel, await _environment, path, | 204 suiteChannel = new MultiChannel( |
199 mapper: mapper, platform: _platform, onClose: () => closeIframe()); | 205 suiteVirtualChannel.stream, suiteVirtualChannel.sink); |
200 } catch (_) { | 206 |
201 closeIframe(); | 207 var completer = new Completer(); |
202 rethrow; | 208 suiteChannel.stream.listen((response) { |
| 209 if (response["type"] == "print") { |
| 210 print(response["line"]); |
| 211 } else { |
| 212 completer.complete(response); |
| 213 } |
| 214 }, onDone: () { |
| 215 if (!completer.isCompleted) completer.complete(); |
| 216 }); |
| 217 |
| 218 return completer.future.timeout(new Duration(minutes: 1), onTimeout: () { |
| 219 throw new LoadException( |
| 220 path, |
| 221 "Timed out waiting for the test suite to connect on " |
| 222 "${_platform.name}."); |
| 223 }); |
| 224 }); |
| 225 |
| 226 if (response == null) { |
| 227 closeIframe(); |
| 228 throw new LoadException( |
| 229 path, "Connection closed before test suite loaded."); |
| 230 } |
| 231 |
| 232 if (response["type"] == "loadException") { |
| 233 closeIframe(); |
| 234 throw new LoadException(path, response["message"]); |
| 235 } |
| 236 |
| 237 if (response["type"] == "error") { |
| 238 closeIframe(); |
| 239 var asyncError = RemoteException.deserialize(response["error"]); |
| 240 await new Future.error( |
| 241 new LoadException(path, asyncError.error), |
| 242 asyncError.stackTrace); |
| 243 } |
| 244 |
| 245 return new RunnerSuite( |
| 246 await _environment, |
| 247 _deserializeGroup(suiteChannel, mapper, response["root"]), |
| 248 platform: _platform, |
| 249 path: path, |
| 250 onClose: () => closeIframe()); |
| 251 } |
| 252 |
| 253 /// Deserializes [group] into a concrete [Group] class. |
| 254 Group _deserializeGroup(MultiChannel suiteChannel, |
| 255 StackTraceMapper mapper, Map group) { |
| 256 var metadata = new Metadata.deserialize(group['metadata']); |
| 257 return new Group(group['name'], group['entries'].map((entry) { |
| 258 if (entry['type'] == 'group') { |
| 259 return _deserializeGroup(suiteChannel, mapper, entry); |
203 } | 260 } |
204 }); | 261 |
| 262 return _deserializeTest(suiteChannel, mapper, entry); |
| 263 }), |
| 264 metadata: metadata, |
| 265 setUpAll: _deserializeTest(suiteChannel, mapper, group['setUpAll']), |
| 266 tearDownAll: |
| 267 _deserializeTest(suiteChannel, mapper, group['tearDownAll'])); |
| 268 } |
| 269 |
| 270 /// Deserializes [test] into a concrete [Test] class. |
| 271 /// |
| 272 /// Returns `null` if [test] is `null`. |
| 273 Test _deserializeTest(MultiChannel suiteChannel, StackTraceMapper mapper, |
| 274 Map test) { |
| 275 if (test == null) return null; |
| 276 |
| 277 var metadata = new Metadata.deserialize(test['metadata']); |
| 278 var testChannel = suiteChannel.virtualChannel(test['channel']); |
| 279 return new IframeTest(test['name'], metadata, testChannel, |
| 280 mapper: mapper); |
205 } | 281 } |
206 | 282 |
207 /// An implementation of [Environment.displayPause]. | 283 /// An implementation of [Environment.displayPause]. |
208 CancelableOperation _displayPause() { | 284 CancelableOperation _displayPause() { |
209 if (_pauseCompleter != null) return _pauseCompleter.operation; | 285 if (_pauseCompleter != null) return _pauseCompleter.operation; |
210 | 286 |
211 _pauseCompleter = new CancelableCompleter(onCancel: () { | 287 _pauseCompleter = new CancelableCompleter(onCancel: () { |
212 _channel.sink.add({"command": "resume"}); | 288 _channel.sink.add({"command": "resume"}); |
213 _pauseCompleter = null; | 289 _pauseCompleter = null; |
214 }); | 290 }); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 | 324 |
249 final Uri observatoryUrl; | 325 final Uri observatoryUrl; |
250 | 326 |
251 final Uri remoteDebuggerUrl; | 327 final Uri remoteDebuggerUrl; |
252 | 328 |
253 _BrowserEnvironment(this._manager, this.observatoryUrl, | 329 _BrowserEnvironment(this._manager, this.observatoryUrl, |
254 this.remoteDebuggerUrl); | 330 this.remoteDebuggerUrl); |
255 | 331 |
256 CancelableOperation displayPause() => _manager._displayPause(); | 332 CancelableOperation displayPause() => _manager._displayPause(); |
257 } | 333 } |
OLD | NEW |