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 throw new ApplicationException( | |
kevmoo
2015/04/14 22:52:12
Add a TODO to point to the dart site for content_s
nweiz
2015/04/14 23:05:31
Done.
| |
65 "You're using an expired content_shell. Upgrade to the latest " | |
66 "version:\n" | |
67 "http://gsdview.appspot.com/dart-archive/channels/stable/release/" | |
68 "latest/dartium/"); | |
69 } | |
70 | |
49 if (exitCode != 0) throw "Content shell failed with exit code $exitCode."; | 71 if (exitCode != 0) throw "Content shell failed with exit code $exitCode."; |
50 }).then(_onExitCompleter.complete) | 72 }).then(_onExitCompleter.complete) |
51 .catchError(_onExitCompleter.completeError); | 73 .catchError(_onExitCompleter.completeError); |
52 } | 74 } |
53 | 75 |
54 Future close() { | 76 Future close() { |
55 _onProcessStarted.then((_) => _process.kill()); | 77 _onProcessStarted.then((_) => _process.kill()); |
56 | 78 |
57 // Swallow exceptions. The user should explicitly use [onExit] for these. | 79 // Swallow exceptions. The user should explicitly use [onExit] for these. |
58 return onExit.catchError((_) {}); | 80 return onExit.catchError((_) {}); |
59 } | 81 } |
60 | 82 |
61 /// Return the default executable for the current operating system. | 83 /// Return the default executable for the current operating system. |
62 String _defaultExecutable() => | 84 String _defaultExecutable() => |
63 Platform.isWindows ? "content_shell.exe" : "content_shell"; | 85 Platform.isWindows ? "content_shell.exe" : "content_shell"; |
64 } | 86 } |
OLD | NEW |