| 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'; | |
| 15 import '../../backend/metadata.dart'; | 14 import '../../backend/metadata.dart'; |
| 16 import '../../backend/test.dart'; | |
| 17 import '../../backend/test_platform.dart'; | 15 import '../../backend/test_platform.dart'; |
| 18 import '../../util/multi_channel.dart'; | 16 import '../../util/multi_channel.dart'; |
| 19 import '../../util/remote_exception.dart'; | |
| 20 import '../../util/stack_trace_mapper.dart'; | 17 import '../../util/stack_trace_mapper.dart'; |
| 21 import '../../utils.dart'; | 18 import '../../utils.dart'; |
| 22 import '../application_exception.dart'; | 19 import '../application_exception.dart'; |
| 23 import '../environment.dart'; | 20 import '../environment.dart'; |
| 24 import '../load_exception.dart'; | |
| 25 import '../runner_suite.dart'; | 21 import '../runner_suite.dart'; |
| 26 import 'browser.dart'; | 22 import 'browser.dart'; |
| 27 import 'chrome.dart'; | 23 import 'chrome.dart'; |
| 28 import 'content_shell.dart'; | 24 import 'content_shell.dart'; |
| 29 import 'dartium.dart'; | 25 import 'dartium.dart'; |
| 30 import 'firefox.dart'; | 26 import 'firefox.dart'; |
| 31 import 'iframe_test.dart'; | |
| 32 import 'internet_explorer.dart'; | 27 import 'internet_explorer.dart'; |
| 33 import 'phantom_js.dart'; | 28 import 'phantom_js.dart'; |
| 34 import 'safari.dart'; | 29 import 'safari.dart'; |
| 30 import 'suite.dart'; |
| 35 | 31 |
| 36 /// A class that manages the connection to a single running browser. | 32 /// A class that manages the connection to a single running browser. |
| 37 /// | 33 /// |
| 38 /// This is in charge of telling the browser which test suites to load and | 34 /// This is in charge of telling the browser which test suites to load and |
| 39 /// converting its responses into [Suite] objects. | 35 /// converting its responses into [Suite] objects. |
| 40 class BrowserManager { | 36 class BrowserManager { |
| 41 /// The browser instance that this is connected to via [_channel]. | 37 /// The browser instance that this is connected to via [_channel]. |
| 42 final Browser _browser; | 38 final Browser _browser; |
| 43 | 39 |
| 44 // TODO(nweiz): Consider removing the duplication between this and | 40 // TODO(nweiz): Consider removing the duplication between this and |
| (...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 173 {StackTraceMapper mapper}) async { | 169 {StackTraceMapper mapper}) async { |
| 174 url = url.replace(fragment: Uri.encodeFull(JSON.encode({ | 170 url = url.replace(fragment: Uri.encodeFull(JSON.encode({ |
| 175 "metadata": metadata.serialize(), | 171 "metadata": metadata.serialize(), |
| 176 "browser": _platform.identifier | 172 "browser": _platform.identifier |
| 177 }))); | 173 }))); |
| 178 | 174 |
| 179 // The stream may close before emitting a value if the browser is killed | 175 // The stream may close before emitting a value if the browser is killed |
| 180 // prematurely (e.g. via Control-C). | 176 // prematurely (e.g. via Control-C). |
| 181 var suiteVirtualChannel = _channel.virtualChannel(); | 177 var suiteVirtualChannel = _channel.virtualChannel(); |
| 182 var suiteId = _suiteId++; | 178 var suiteId = _suiteId++; |
| 183 var suiteChannel; | |
| 184 | 179 |
| 185 closeIframe() { | 180 closeIframe() { |
| 186 if (_closed) return; | 181 if (_closed) return; |
| 187 suiteChannel.sink.close(); | |
| 188 _channel.sink.add({ | 182 _channel.sink.add({ |
| 189 "command": "closeSuite", | 183 "command": "closeSuite", |
| 190 "id": suiteId | 184 "id": suiteId |
| 191 }); | 185 }); |
| 192 } | 186 } |
| 193 | 187 |
| 194 var response = await _pool.withResource(() { | 188 return await _pool.withResource(() async { |
| 195 _channel.sink.add({ | 189 _channel.sink.add({ |
| 196 "command": "loadSuite", | 190 "command": "loadSuite", |
| 197 "url": url.toString(), | 191 "url": url.toString(), |
| 198 "id": suiteId, | 192 "id": suiteId, |
| 199 "channel": suiteVirtualChannel.id | 193 "channel": suiteVirtualChannel.id |
| 200 }); | 194 }); |
| 201 | 195 |
| 202 // Create a nested MultiChannel because the iframe will be using a channel | 196 try { |
| 203 // wrapped within the host's channel. | 197 return await loadBrowserSuite( |
| 204 suiteChannel = new MultiChannel( | 198 suiteVirtualChannel, await _environment, path, |
| 205 suiteVirtualChannel.stream, suiteVirtualChannel.sink); | 199 mapper: mapper, platform: _platform, onClose: () => closeIframe()); |
| 206 | 200 } catch (_) { |
| 207 var completer = new Completer(); | 201 closeIframe(); |
| 208 suiteChannel.stream.listen((response) { | 202 rethrow; |
| 209 if (response["type"] == "print") { | 203 } |
| 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 }); | 204 }); |
| 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); | |
| 260 } | |
| 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); | |
| 281 } | 205 } |
| 282 | 206 |
| 283 /// An implementation of [Environment.displayPause]. | 207 /// An implementation of [Environment.displayPause]. |
| 284 CancelableOperation _displayPause() { | 208 CancelableOperation _displayPause() { |
| 285 if (_pauseCompleter != null) return _pauseCompleter.operation; | 209 if (_pauseCompleter != null) return _pauseCompleter.operation; |
| 286 | 210 |
| 287 _pauseCompleter = new CancelableCompleter(onCancel: () { | 211 _pauseCompleter = new CancelableCompleter(onCancel: () { |
| 288 _channel.sink.add({"command": "resume"}); | 212 _channel.sink.add({"command": "resume"}); |
| 289 _pauseCompleter = null; | 213 _pauseCompleter = null; |
| 290 }); | 214 }); |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 324 | 248 |
| 325 final Uri observatoryUrl; | 249 final Uri observatoryUrl; |
| 326 | 250 |
| 327 final Uri remoteDebuggerUrl; | 251 final Uri remoteDebuggerUrl; |
| 328 | 252 |
| 329 _BrowserEnvironment(this._manager, this.observatoryUrl, | 253 _BrowserEnvironment(this._manager, this.observatoryUrl, |
| 330 this.remoteDebuggerUrl); | 254 this.remoteDebuggerUrl); |
| 331 | 255 |
| 332 CancelableOperation displayPause() => _manager._displayPause(); | 256 CancelableOperation displayPause() => _manager._displayPause(); |
| 333 } | 257 } |
| OLD | NEW |