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.content_shell; | 5 library test.runner.browser.content_shell; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:convert'; |
8 import 'dart:io'; | 9 import 'dart:io'; |
9 | 10 |
| 11 import '../application_exception.dart'; |
10 import 'browser.dart'; | 12 import 'browser.dart'; |
11 | 13 |
| 14 /// A converter that transforms a byte stream into a stream of lines. |
| 15 final _lines = UTF8.decoder.fuse(const LineSplitter()); |
| 16 |
12 /// A class for running an instance of the Dartium content shell. | 17 /// A class for running an instance of the Dartium content shell. |
13 /// | 18 /// |
14 /// Most of the communication with the browser is expected to happen via HTTP, | 19 /// Most of the communication with the browser is expected to happen via HTTP, |
15 /// so this exposes a bare-bones API. The browser starts as soon as the class is | 20 /// so this exposes a bare-bones API. The browser starts as soon as the class is |
16 /// constructed, and is killed when [close] is called. | 21 /// constructed, and is killed when [close] is called. |
17 /// | 22 /// |
18 /// Any errors starting or running the process are reported through [onExit]. | 23 /// Any errors starting or running the process are reported through [onExit]. |
19 class ContentShell implements Browser { | 24 class ContentShell implements Browser { |
20 /// The underlying process. | 25 /// The underlying process. |
21 Process _process; | 26 Process _process; |
22 | 27 |
23 Future get onExit => _onExitCompleter.future; | 28 Future get onExit => _onExitCompleter.future; |
24 final _onExitCompleter = new Completer(); | 29 final _onExitCompleter = new Completer(); |
25 | 30 |
26 /// A future that completes when the browser process has started. | 31 /// A future that completes when the browser process has started. |
27 /// | 32 /// |
28 /// This is used to ensure that [close] works regardless of when it's called. | 33 /// This is used to ensure that [close] works regardless of when it's called. |
29 Future get _onProcessStarted => _onProcessStartedCompleter.future; | 34 Future get _onProcessStarted => _onProcessStartedCompleter.future; |
30 final _onProcessStartedCompleter = new Completer(); | 35 final _onProcessStartedCompleter = new Completer(); |
31 | 36 |
32 /// Starts a new instance of content shell open to the given [url], which may | 37 /// Starts a new instance of content shell open to the given [url], which may |
33 /// be a [Uri] or a [String]. | 38 /// be a [Uri] or a [String]. |
34 /// | 39 /// |
35 /// If [executable] is passed, it's used as the content shell executable. | 40 /// If [executable] is passed, it's used as the content shell executable. |
36 /// Otherwise the default executable name for the current OS will be used. | 41 /// Otherwise the default executable name for the current OS will be used. |
37 ContentShell(url, {String executable}) { | 42 ContentShell(url, {String executable}) { |
38 if (executable == null) executable = _defaultExecutable(); | 43 if (executable == null) executable = _defaultExecutable(); |
39 | 44 |
| 45 // Whether we killed content shell because it used an expired Dart version. |
| 46 var expired = false; |
| 47 |
40 // Don't return a Future here because there's no need for the caller to wait | 48 // Don't return a Future here because there's no need for the caller to wait |
41 // for the process to actually start. They should just wait for the HTTP | 49 // for the process to actually start. They should just wait for the HTTP |
42 // request instead. | 50 // request instead. |
43 Process.start(executable, ["--dump-render-tree", url.toString()], | 51 Process.start(executable, ["--dump-render-tree", url.toString()], |
44 environment: {"DART_FLAGS": "--checked"}).then((process) { | 52 environment: {"DART_FLAGS": "--checked"}).then((process) { |
| 53 _lines.bind(process.stderr).listen((line) { |
| 54 if (line != "[dartToStderr]: Dartium build has expired") return; |
| 55 expired = true; |
| 56 process.kill(); |
| 57 }); |
| 58 |
45 _process = process; | 59 _process = process; |
46 _onProcessStartedCompleter.complete(); | 60 _onProcessStartedCompleter.complete(); |
47 return _process.exitCode; | 61 return _process.exitCode; |
48 }).then((exitCode) { | 62 }).then((exitCode) { |
| 63 if (expired) { |
| 64 // TODO(nweiz): link to dartlang.org once it has download links for |
| 65 // content shell |
| 66 // (https://github.com/dart-lang/www.dartlang.org/issues/1164). |
| 67 throw new ApplicationException( |
| 68 "You're using an expired content_shell. Upgrade to the latest " |
| 69 "version:\n" |
| 70 "http://gsdview.appspot.com/dart-archive/channels/stable/release/" |
| 71 "latest/dartium/"); |
| 72 } |
| 73 |
49 if (exitCode != 0) throw "Content shell failed with exit code $exitCode."; | 74 if (exitCode != 0) throw "Content shell failed with exit code $exitCode."; |
50 }).then(_onExitCompleter.complete) | 75 }).then(_onExitCompleter.complete) |
51 .catchError(_onExitCompleter.completeError); | 76 .catchError(_onExitCompleter.completeError); |
52 } | 77 } |
53 | 78 |
54 Future close() { | 79 Future close() { |
55 _onProcessStarted.then((_) => _process.kill()); | 80 _onProcessStarted.then((_) => _process.kill()); |
56 | 81 |
57 // Swallow exceptions. The user should explicitly use [onExit] for these. | 82 // Swallow exceptions. The user should explicitly use [onExit] for these. |
58 return onExit.catchError((_) {}); | 83 return onExit.catchError((_) {}); |
59 } | 84 } |
60 | 85 |
61 /// Return the default executable for the current operating system. | 86 /// Return the default executable for the current operating system. |
62 String _defaultExecutable() => | 87 String _defaultExecutable() => |
63 Platform.isWindows ? "content_shell.exe" : "content_shell"; | 88 Platform.isWindows ? "content_shell.exe" : "content_shell"; |
64 } | 89 } |
OLD | NEW |