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.server; | 5 library test.runner.browser.server; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 | 10 |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
57 final String _compiledDir; | 57 final String _compiledDir; |
58 | 58 |
59 /// The package root which is passed to `dart2js`. | 59 /// The package root which is passed to `dart2js`. |
60 final String _packageRoot; | 60 final String _packageRoot; |
61 | 61 |
62 /// The browser in which test suites are loaded and run. | 62 /// The browser in which test suites are loaded and run. |
63 /// | 63 /// |
64 /// This is `null` until a suite is loaded. | 64 /// This is `null` until a suite is loaded. |
65 Chrome _browser; | 65 Chrome _browser; |
66 | 66 |
| 67 /// Whether [close] has been called. |
| 68 bool get _closed => _closeCompleter != null; |
| 69 |
| 70 /// The completer for the [Future] returned by [close]. |
| 71 Completer _closeCompleter; |
| 72 |
67 /// A future that will complete to the [BrowserManager] for [_browser]. | 73 /// A future that will complete to the [BrowserManager] for [_browser]. |
68 /// | 74 /// |
69 /// The first time this is called, it will start both the browser and the | 75 /// The first time this is called, it will start both the browser and the |
70 /// browser manager. Any further calls will return the existing manager. | 76 /// browser manager. Any further calls will return the existing manager. |
71 Future<BrowserManager> get _browserManager { | 77 Future<BrowserManager> get _browserManager { |
72 if (_browserManagerCompleter == null) { | 78 if (_browserManagerCompleter == null) { |
73 _browserManagerCompleter = new Completer(); | 79 _browserManagerCompleter = new Completer(); |
74 var path = _webSocketHandler.create(webSocketHandler((webSocket) { | 80 var path = _webSocketHandler.create(webSocketHandler((webSocket) { |
75 _browserManagerCompleter.complete(new BrowserManager(webSocket)); | 81 _browserManagerCompleter.complete(new BrowserManager(webSocket)); |
76 })); | 82 })); |
77 | 83 |
78 var webSocketUrl = url.replace(scheme: 'ws', path: '/$path'); | 84 var webSocketUrl = url.replace(scheme: 'ws', path: '/$path'); |
79 _browser = new Chrome(url.replace(queryParameters: { | 85 _browser = new Chrome(url.replace(queryParameters: { |
80 'managerUrl': webSocketUrl.toString() | 86 'managerUrl': webSocketUrl.toString() |
81 })); | 87 })); |
82 | 88 |
83 // TODO(nweiz): Gracefully handle the browser being killed before the | 89 // TODO(nweiz): Gracefully handle the browser being killed before the |
84 // tests complete. | 90 // tests complete. |
85 _browser.onExit.catchError((error, stackTrace) { | 91 _browser.onExit.catchError((error, stackTrace) { |
86 if (_browserManagerCompleter.isCompleted) return; | 92 if (_browserManagerCompleter.isCompleted) return; |
87 _browserManagerCompleter.completeError(error, stackTrace); | 93 _browserManagerCompleter.completeError(error, stackTrace); |
88 }); | 94 }); |
89 } | 95 } |
90 return _browserManagerCompleter.future; | 96 return _browserManagerCompleter.future; |
91 } | 97 } |
92 Completer<BrowserManager> _browserManagerCompleter; | 98 Completer<BrowserManager> _browserManagerCompleter; |
93 | 99 |
94 BrowserServer._(this._packageRoot, bool color) | 100 BrowserServer._(this._packageRoot, bool color) |
95 : _compiledDir = Directory.systemTemp.createTempSync('test_').path, | 101 : _compiledDir = createTempDir(), |
96 _compilers = new CompilerPool(color: color); | 102 _compilers = new CompilerPool(color: color); |
97 | 103 |
98 /// Starts the underlying server. | 104 /// Starts the underlying server. |
99 Future _load() { | 105 Future _load() { |
100 var staticPath = p.join(libDir(packageRoot: _packageRoot), | 106 var staticPath = p.join(libDir(packageRoot: _packageRoot), |
101 'src/runner/browser/static'); | 107 'src/runner/browser/static'); |
102 var cascade = new shelf.Cascade() | 108 var cascade = new shelf.Cascade() |
103 .add(_webSocketHandler.handler) | 109 .add(_webSocketHandler.handler) |
104 .add(createStaticHandler(staticPath, defaultDocument: 'index.html')) | 110 .add(createStaticHandler(staticPath, defaultDocument: 'index.html')) |
105 .add(createStaticHandler(_compiledDir, defaultDocument: 'index.html')); | 111 .add(createStaticHandler(_compiledDir, defaultDocument: 'index.html')); |
106 | 112 |
107 return shelf_io.serve(cascade.handler, 'localhost', 0).then((server) { | 113 return shelf_io.serve(cascade.handler, 'localhost', 0).then((server) { |
108 _server = server; | 114 _server = server; |
109 }); | 115 }); |
110 } | 116 } |
111 | 117 |
112 /// Loads the test suite at [path]. | 118 /// Loads the test suite at [path]. |
113 /// | 119 /// |
114 /// This will start a browser to load the suite if one isn't already running. | 120 /// This will start a browser to load the suite if one isn't already running. |
115 Future<Suite> loadSuite(String path) { | 121 Future<Suite> loadSuite(String path) { |
116 return _compileSuite(path).then((dir) { | 122 return _compileSuite(path).then((dir) { |
| 123 if (_closed) return null; |
| 124 |
117 // TODO(nweiz): Don't start the browser until all the suites are compiled. | 125 // TODO(nweiz): Don't start the browser until all the suites are compiled. |
118 return _browserManager.then((browserManager) { | 126 return _browserManager.then((browserManager) { |
| 127 if (_closed) return null; |
| 128 |
119 // Add a trailing slash because at least on Chrome, the iframe's | 129 // Add a trailing slash because at least on Chrome, the iframe's |
120 // window.location.href will do so automatically, and if that differs | 130 // window.location.href will do so automatically, and if that differs |
121 // from the original URL communication will fail. | 131 // from the original URL communication will fail. |
122 var suiteUrl = url.resolve( | 132 var suiteUrl = url.resolve( |
123 "/" + p.toUri(p.relative(dir, from: _compiledDir)).path + "/"); | 133 "/" + p.toUri(p.relative(dir, from: _compiledDir)).path + "/"); |
124 return browserManager.loadSuite(path, suiteUrl); | 134 return browserManager.loadSuite(path, suiteUrl); |
125 }); | 135 }); |
126 }); | 136 }); |
127 } | 137 } |
128 | 138 |
129 /// Compile the test suite at [dartPath] to JavaScript. | 139 /// Compile the test suite at [dartPath] to JavaScript. |
130 /// | 140 /// |
131 /// Returns a [Future] that completes to the path to the JavaScript. | 141 /// Returns a [Future] that completes to the path to the JavaScript. |
132 Future<String> _compileSuite(String dartPath) { | 142 Future<String> _compileSuite(String dartPath) { |
133 var dir = new Directory(_compiledDir).createTempSync('test_').path; | 143 var dir = new Directory(_compiledDir).createTempSync('test_').path; |
134 var jsPath = p.join(dir, p.basename(dartPath) + ".js"); | 144 var jsPath = p.join(dir, p.basename(dartPath) + ".js"); |
135 return _compilers.compile(dartPath, jsPath, | 145 return _compilers.compile(dartPath, jsPath, |
136 packageRoot: packageRootFor(dartPath, _packageRoot)) | 146 packageRoot: packageRootFor(dartPath, _packageRoot)) |
137 .then((_) { | 147 .then((_) { |
| 148 if (_closed) return null; |
| 149 |
138 // TODO(nweiz): support user-authored HTML files. | 150 // TODO(nweiz): support user-authored HTML files. |
139 new File(p.join(dir, "index.html")).writeAsStringSync(''' | 151 new File(p.join(dir, "index.html")).writeAsStringSync(''' |
140 <!DOCTYPE html> | 152 <!DOCTYPE html> |
141 <html> | 153 <html> |
142 <head> | 154 <head> |
143 <title>${HTML_ESCAPE.convert(dartPath)} Test</title> | 155 <title>${HTML_ESCAPE.convert(dartPath)} Test</title> |
144 <script src="${HTML_ESCAPE.convert(p.basename(jsPath))}"></script> | 156 <script src="${HTML_ESCAPE.convert(p.basename(jsPath))}"></script> |
145 </head> | 157 </head> |
146 </html> | 158 </html> |
147 '''); | 159 '''); |
148 return dir; | 160 return dir; |
149 }); | 161 }); |
150 } | 162 } |
151 | 163 |
152 /// Closes the server and releases all its resources. | 164 /// Closes the server and releases all its resources. |
153 /// | 165 /// |
154 /// Returns a [Future] that completes once the server is closed and its | 166 /// Returns a [Future] that completes once the server is closed and its |
155 /// resources have been fully released. | 167 /// resources have been fully released. |
156 Future close() { | 168 Future close() { |
157 new Directory(_compiledDir).deleteSync(recursive: true); | 169 if (_closeCompleter != null) return _closeCompleter.future; |
158 return _server.close().then((_) { | 170 _closeCompleter = new Completer(); |
| 171 |
| 172 return Future.wait([ |
| 173 _server.close(), |
| 174 _compilers.close() |
| 175 ]).then((_) { |
159 if (_browserManagerCompleter == null) return null; | 176 if (_browserManagerCompleter == null) return null; |
160 return _browserManager.then((_) => _browser.close()); | 177 return _browserManager.then((_) => _browser.close()); |
161 }); | 178 }).then((_) { |
| 179 new Directory(_compiledDir).deleteSync(recursive: true); |
| 180 _closeCompleter.complete(); |
| 181 }).catchError(_closeCompleter.completeError); |
162 } | 182 } |
163 } | 183 } |
OLD | NEW |