| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.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 pub_tests; | 5 library pub_tests; |
| 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 |
| 11 import 'package:http/http.dart' as http; | 11 import 'package:http/http.dart' as http; |
| 12 import 'package:path/path.dart' as p; | |
| 13 import 'package:scheduled_test/scheduled_process.dart'; | 12 import 'package:scheduled_test/scheduled_process.dart'; |
| 14 import 'package:scheduled_test/scheduled_stream.dart'; | 13 import 'package:scheduled_test/scheduled_stream.dart'; |
| 15 import 'package:scheduled_test/scheduled_test.dart'; | 14 import 'package:scheduled_test/scheduled_test.dart'; |
| 16 | 15 |
| 16 import '../../lib/src/utils.dart'; |
| 17 import '../descriptor.dart' as d; | 17 import '../descriptor.dart' as d; |
| 18 import '../test_pub.dart'; | 18 import '../test_pub.dart'; |
| 19 | 19 |
| 20 /// The pub process running "pub serve". | 20 /// The pub process running "pub serve". |
| 21 ScheduledProcess _pubServer; | 21 ScheduledProcess _pubServer; |
| 22 | 22 |
| 23 /// The ephemeral ports assigned to the running servers, associated with the | 23 /// The ephemeral ports assigned to the running servers, associated with the |
| 24 /// directories they're serving. | 24 /// directories they're serving. |
| 25 final _ports = new Map<String, int>(); | 25 final _ports = new Map<String, int>(); |
| 26 | 26 |
| 27 /// A completer that completes when the server has been started and the served |
| 28 /// ports are known. |
| 29 Completer _portsCompleter; |
| 30 |
| 27 /// The web socket connection to the running pub process, or `null` if no | 31 /// The web socket connection to the running pub process, or `null` if no |
| 28 /// connection has been made. | 32 /// connection has been made. |
| 29 WebSocket _webSocket; | 33 WebSocket _webSocket; |
| 30 Stream _webSocketBroadcastStream; | 34 Stream _webSocketBroadcastStream; |
| 31 | 35 |
| 32 /// The code for a transformer that renames ".txt" files to ".out" and adds a | 36 /// The code for a transformer that renames ".txt" files to ".out" and adds a |
| 33 /// ".out" suffix. | 37 /// ".out" suffix. |
| 34 const REWRITE_TRANSFORMER = """ | 38 const REWRITE_TRANSFORMER = """ |
| 35 import 'dart:async'; | 39 import 'dart:async'; |
| 36 | 40 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 122 /// | 126 /// |
| 123 /// If [shouldGetFirst] is `true`, validates that pub get is run first. | 127 /// If [shouldGetFirst] is `true`, validates that pub get is run first. |
| 124 /// | 128 /// |
| 125 /// If [createWebDir] is `true`, creates a `web/` directory if one doesn't exist | 129 /// If [createWebDir] is `true`, creates a `web/` directory if one doesn't exist |
| 126 /// so pub doesn't complain about having nothing to serve. | 130 /// so pub doesn't complain about having nothing to serve. |
| 127 /// | 131 /// |
| 128 /// Returns the `pub serve` process. | 132 /// Returns the `pub serve` process. |
| 129 ScheduledProcess pubServe({bool shouldGetFirst: false, bool createWebDir: true, | 133 ScheduledProcess pubServe({bool shouldGetFirst: false, bool createWebDir: true, |
| 130 Iterable<String> args}) { | 134 Iterable<String> args}) { |
| 131 _pubServer = startPubServe(args: args, createWebDir: createWebDir); | 135 _pubServer = startPubServe(args: args, createWebDir: createWebDir); |
| 136 _portsCompleter = new Completer(); |
| 132 | 137 |
| 133 currentSchedule.onComplete.schedule(() { | 138 currentSchedule.onComplete.schedule(() { |
| 139 _portsCompleter = null; |
| 134 _ports.clear(); | 140 _ports.clear(); |
| 135 | 141 |
| 136 if (_webSocket != null) { | 142 if (_webSocket != null) { |
| 137 _webSocket.close(); | 143 _webSocket.close(); |
| 138 _webSocket = null; | 144 _webSocket = null; |
| 139 _webSocketBroadcastStream = null; | 145 _webSocketBroadcastStream = null; |
| 140 } | 146 } |
| 141 }); | 147 }); |
| 142 | 148 |
| 143 if (shouldGetFirst) { | 149 if (shouldGetFirst) { |
| 144 _pubServer.stdout.expect(consumeThrough("Got dependencies!")); | 150 _pubServer.stdout.expect(consumeThrough("Got dependencies!")); |
| 145 } | 151 } |
| 146 | 152 |
| 147 // The server should emit one or more ports. | 153 // The server should emit one or more ports. |
| 148 _pubServer.stdout.expect( | 154 _pubServer.stdout.expect( |
| 149 consumeWhile(predicate(_parsePort, 'emits server url'))); | 155 consumeWhile(predicate(_parsePort, 'emits server url'))); |
| 150 schedule(() => expect(_ports, isNot(isEmpty))); | 156 schedule(() { |
| 157 expect(_ports, isNot(isEmpty)); |
| 158 _portsCompleter.complete(); |
| 159 }); |
| 151 | 160 |
| 152 return _pubServer; | 161 return _pubServer; |
| 153 } | 162 } |
| 154 | 163 |
| 155 /// The regular expression for parsing pub's output line describing the URL for | 164 /// The regular expression for parsing pub's output line describing the URL for |
| 156 /// the server. | 165 /// the server. |
| 157 final _parsePortRegExp = new RegExp(r"([^ ]+) +on http://127\.0\.0\.1:(\d+)"); | 166 final _parsePortRegExp = new RegExp(r"([^ ]+) +on http://127\.0\.0\.1:(\d+)"); |
| 158 | 167 |
| 159 /// Parses the port number from the "Serving blah on 127.0.0.1:1234" line | 168 /// Parses the port number from the "Serving blah on 127.0.0.1:1234" line |
| 160 /// printed by pub serve. | 169 /// printed by pub serve. |
| 161 bool _parsePort(String line) { | 170 bool _parsePort(String line) { |
| 162 var match = _parsePortRegExp.firstMatch(line); | 171 var match = _parsePortRegExp.firstMatch(line); |
| 163 if (match == null) return false; | 172 if (match == null) return false; |
| 164 _ports[match[1]] = int.parse(match[2]); | 173 _ports[match[1]] = int.parse(match[2]); |
| 165 return true; | 174 return true; |
| 166 } | 175 } |
| 167 | 176 |
| 168 void endPubServe() { | 177 void endPubServe() { |
| 169 _pubServer.kill(); | 178 _pubServer.kill(); |
| 170 } | 179 } |
| 171 | 180 |
| 172 /// Schedules an HTTP request to the running pub server with [urlPath] and | 181 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 173 /// verifies that it responds with a body that matches [expectation]. | 182 /// verifies that it responds with a body that matches [expectation]. |
| 174 /// | 183 /// |
| 175 /// [expectation] may either be a [Matcher] or a string to match an exact body. | 184 /// [expectation] may either be a [Matcher] or a string to match an exact body. |
| 176 /// [root] indicates which server should be accessed, and defaults to "web". | 185 /// [root] indicates which server should be accessed, and defaults to "web". |
| 177 /// [headers] may be either a [Matcher] or a map to match an exact headers map. | 186 /// [headers] may be either a [Matcher] or a map to match an exact headers map. |
| 178 void requestShouldSucceed(String urlPath, expectation, {String root, headers}) { | 187 void requestShouldSucceed(String urlPath, expectation, {String root, headers}) { |
| 179 schedule(() { | 188 schedule(() { |
| 180 return http.get("${_serverUrl(root)}/$urlPath").then((response) { | 189 return http.get(_getServerUrlSync(root, urlPath)).then((response) { |
| 181 if (expectation != null) expect(response.body, expectation); | 190 if (expectation != null) expect(response.body, expectation); |
| 182 if (headers != null) expect(response.headers, headers); | 191 if (headers != null) expect(response.headers, headers); |
| 183 }); | 192 }); |
| 184 }, "request $urlPath"); | 193 }, "request $urlPath"); |
| 185 } | 194 } |
| 186 | 195 |
| 187 /// Schedules an HTTP request to the running pub server with [urlPath] and | 196 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 188 /// verifies that it responds with a 404. | 197 /// verifies that it responds with a 404. |
| 189 /// | 198 /// |
| 190 /// [root] indicates which server should be accessed, and defaults to "web". | 199 /// [root] indicates which server should be accessed, and defaults to "web". |
| 191 void requestShould404(String urlPath, {String root}) { | 200 void requestShould404(String urlPath, {String root}) { |
| 192 schedule(() { | 201 schedule(() { |
| 193 return http.get("${_serverUrl(root)}/$urlPath").then((response) { | 202 return http.get(_getServerUrlSync(root, urlPath)).then((response) { |
| 194 expect(response.statusCode, equals(404)); | 203 expect(response.statusCode, equals(404)); |
| 195 }); | 204 }); |
| 196 }, "request $urlPath"); | 205 }, "request $urlPath"); |
| 197 } | 206 } |
| 198 | 207 |
| 199 /// Schedules an HTTP request to the running pub server with [urlPath] and | 208 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 200 /// verifies that it responds with a redirect to the given [redirectTarget]. | 209 /// verifies that it responds with a redirect to the given [redirectTarget]. |
| 201 /// | 210 /// |
| 202 /// [redirectTarget] may be either a [Matcher] or a string to match an exact | 211 /// [redirectTarget] may be either a [Matcher] or a string to match an exact |
| 203 /// URL. [root] indicates which server should be accessed, and defaults to | 212 /// URL. [root] indicates which server should be accessed, and defaults to |
| 204 /// "web". | 213 /// "web". |
| 205 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) { | 214 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) { |
| 206 schedule(() { | 215 schedule(() { |
| 207 var request = new http.Request("GET", | 216 var request = new http.Request("GET", |
| 208 Uri.parse("${_serverUrl(root)}/$urlPath")); | 217 Uri.parse(_getServerUrlSync(root, urlPath))); |
| 209 request.followRedirects = false; | 218 request.followRedirects = false; |
| 210 return request.send().then((response) { | 219 return request.send().then((response) { |
| 211 expect(response.statusCode ~/ 100, equals(3)); | 220 expect(response.statusCode ~/ 100, equals(3)); |
| 212 | 221 |
| 213 expect(response.headers, containsPair('location', redirectTarget)); | 222 expect(response.headers, containsPair('location', redirectTarget)); |
| 214 }); | 223 }); |
| 215 }, "request $urlPath"); | 224 }, "request $urlPath"); |
| 216 } | 225 } |
| 217 | 226 |
| 218 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies | 227 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies |
| 219 /// that it responds with a 405. | 228 /// that it responds with a 405. |
| 220 /// | 229 /// |
| 221 /// [root] indicates which server should be accessed, and defaults to "web". | 230 /// [root] indicates which server should be accessed, and defaults to "web". |
| 222 void postShould405(String urlPath, {String root}) { | 231 void postShould405(String urlPath, {String root}) { |
| 223 schedule(() { | 232 schedule(() { |
| 224 return http.post("${_serverUrl(root)}/$urlPath").then((response) { | 233 return http.post(_getServerUrlSync(root, urlPath)).then((response) { |
| 225 expect(response.statusCode, equals(405)); | 234 expect(response.statusCode, equals(405)); |
| 226 }); | 235 }); |
| 227 }, "request $urlPath"); | 236 }, "request $urlPath"); |
| 228 } | 237 } |
| 229 | 238 |
| 230 /// Reads lines from pub serve's stdout until it prints the build success | 239 /// Reads lines from pub serve's stdout until it prints the build success |
| 231 /// message. | 240 /// message. |
| 232 /// | 241 /// |
| 233 /// The schedule will not proceed until the output is found. If not found, it | 242 /// The schedule will not proceed until the output is found. If not found, it |
| 234 /// will eventually time out. | 243 /// will eventually time out. |
| (...skipping 14 matching lines...) Expand all Loading... |
| 249 // server, use that port for the websocket interface. | 258 // server, use that port for the websocket interface. |
| 250 var port = _ports.values.first; | 259 var port = _ports.values.first; |
| 251 return WebSocket.connect("ws://127.0.0.1:$port").then((socket) { | 260 return WebSocket.connect("ws://127.0.0.1:$port").then((socket) { |
| 252 _webSocket = socket; | 261 _webSocket = socket; |
| 253 // TODO(rnystrom): Works around #13913. | 262 // TODO(rnystrom): Works around #13913. |
| 254 _webSocketBroadcastStream = _webSocket.asBroadcastStream(); | 263 _webSocketBroadcastStream = _webSocket.asBroadcastStream(); |
| 255 }); | 264 }); |
| 256 } | 265 } |
| 257 | 266 |
| 258 /// Sends [request] (an arbitrary JSON object) to the running pub serve's web | 267 /// Sends [request] (an arbitrary JSON object) to the running pub serve's web |
| 259 /// socket connection, waits for a reply, then verifies that the reply matches | 268 /// socket connection, waits for a reply, then verifies that the reply is |
| 260 /// [expectation]. | 269 /// either equal to [replyEquals] or matches [replyMatches]. |
| 270 /// |
| 271 /// Only one of [replyEquals] or [replyMatches] may be provided. |
| 272 /// |
| 273 /// [request] and [replyMatches] may contain futures, in which case this will |
| 274 /// wait until they've completed before matching. |
| 261 /// | 275 /// |
| 262 /// If [encodeRequest] is `false`, then [request] will be sent as-is over the | 276 /// If [encodeRequest] is `false`, then [request] will be sent as-is over the |
| 263 /// socket. It omitted, request is JSON encoded to a string first. | 277 /// socket. It omitted, request is JSON encoded to a string first. |
| 264 void webSocketShouldReply(request, expectation, {bool encodeRequest: true}) { | 278 void expectWebSocketCall(request, {Map replyEquals, replyMatches, |
| 279 bool encodeRequest: true}) { |
| 280 assert((replyEquals == null) != (replyMatches == null)); |
| 281 |
| 265 schedule(() => _ensureWebSocket().then((_) { | 282 schedule(() => _ensureWebSocket().then((_) { |
| 266 if (encodeRequest) request = JSON.encode(request); | 283 var matcherFuture; |
| 267 _webSocket.add(request); | 284 if (replyMatches != null) { |
| 268 return _webSocketBroadcastStream.first.then((value) { | 285 matcherFuture = new Future.value(replyMatches); |
| 269 expect(JSON.decode(value), expectation); | 286 } else { |
| 287 matcherFuture = awaitObject(replyEquals).then((reply) => equals(reply)); |
| 288 } |
| 289 |
| 290 return matcherFuture.then((matcher) { |
| 291 return awaitObject(request).then((completeRequest) { |
| 292 if (encodeRequest) completeRequest = JSON.encode(completeRequest); |
| 293 _webSocket.add(completeRequest); |
| 294 |
| 295 return _webSocketBroadcastStream.first.then((value) { |
| 296 expect(JSON.decode(value), matcher); |
| 297 }); |
| 298 }); |
| 270 }); | 299 }); |
| 271 }), "send $request to web socket and expect reply that $expectation"); | 300 }), "send $request to web socket and expect reply $replyEquals"); |
| 272 } | 301 } |
| 273 | 302 |
| 274 /// Returns the URL for the server serving from [root]. | 303 /// Returns a [Future] that completes to a URL string for the server serving |
| 275 String _serverUrl([String root]) { | 304 /// [path] from [root]. |
| 305 /// |
| 306 /// If [root] is omitted, defaults to "web". If [path] is omitted, no path is |
| 307 /// included. The Future will complete once the server is up and running and |
| 308 /// the bound ports are known. |
| 309 Future<String> getServerUrl([String root, String path]) => |
| 310 _portsCompleter.future.then((_) => _getServerUrlSync(root, path)); |
| 311 |
| 312 /// Returns a URL string for the server serving [path] from [root]. |
| 313 /// |
| 314 /// If [root] is omitted, defaults to "web". If [path] is omitted, no path is |
| 315 /// included. Unlike [getServerUrl], this should only be called after the ports |
| 316 /// are known. |
| 317 String _getServerUrlSync([String root, String path]) { |
| 276 if (root == null) root = 'web'; | 318 if (root == null) root = 'web'; |
| 277 expect(_ports, contains(root)); | 319 expect(_ports, contains(root)); |
| 278 return "http://127.0.0.1:${_ports[root]}"; | 320 var url = "http://127.0.0.1:${_ports[root]}"; |
| 279 } | 321 if (path != null) url = "$url/$path"; |
| 322 return url; |
| 323 } |
| 324 |
| OLD | NEW |