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 |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
142 } | 142 } |
143 """; | 143 """; |
144 } | 144 } |
145 | 145 |
146 /// Schedules starting the `pub serve` process. | 146 /// Schedules starting the `pub serve` process. |
147 /// | 147 /// |
148 /// Unlike [pubServe], this doesn't determine the port number of the server, and | 148 /// Unlike [pubServe], this doesn't determine the port number of the server, and |
149 /// so may be used to test for errors in the initialization process. | 149 /// so may be used to test for errors in the initialization process. |
150 /// | 150 /// |
151 /// Returns the `pub serve` process. | 151 /// Returns the `pub serve` process. |
152 ScheduledProcess startPubServe({Iterable<String> args, | 152 ScheduledProcess startPubServe({Iterable<String> args, bool createWebDir: true}) |
153 bool createWebDir: true}) { | 153 { |
154 var pubArgs = [ | 154 var pubArgs = ["serve", "--port=0", // Use port 0 to get an ephemeral port. |
155 "serve", | 155 "--force-poll", "--log-admin-url"]; |
156 "--port=0", // Use port 0 to get an ephemeral port. | |
157 "--force-poll", | |
158 "--log-admin-url" | |
159 ]; | |
160 | 156 |
161 if (args != null) pubArgs.addAll(args); | 157 if (args != null) pubArgs.addAll(args); |
162 | 158 |
163 // Dart2js can take a long time to compile dart code, so we increase the | 159 // Dart2js can take a long time to compile dart code, so we increase the |
164 // timeout to cope with that. | 160 // timeout to cope with that. |
165 currentSchedule.timeout *= 1.5; | 161 currentSchedule.timeout *= 1.5; |
166 | 162 |
167 if (createWebDir) d.dir(appPath, [d.dir("web")]).create(); | 163 if (createWebDir) d.dir(appPath, [d.dir("web")]).create(); |
168 return startPub(args: pubArgs); | 164 return startPub(args: pubArgs); |
169 } | 165 } |
(...skipping 17 matching lines...) Expand all Loading... |
187 _ports.clear(); | 183 _ports.clear(); |
188 | 184 |
189 if (_webSocket != null) { | 185 if (_webSocket != null) { |
190 _webSocket.close(); | 186 _webSocket.close(); |
191 _webSocket = null; | 187 _webSocket = null; |
192 _webSocketBroadcastStream = null; | 188 _webSocketBroadcastStream = null; |
193 } | 189 } |
194 }); | 190 }); |
195 | 191 |
196 if (shouldGetFirst) { | 192 if (shouldGetFirst) { |
197 _pubServer.stdout.expect(consumeThrough(anyOf([ | 193 _pubServer.stdout.expect( |
198 "Got dependencies!", | 194 consumeThrough( |
199 matches(new RegExp(r"^Changed \d+ dependenc")) | 195 anyOf(["Got dependencies!", matches(new RegExp(r"^Changed \d+ depend
enc"))]))); |
200 ]))); | |
201 } | 196 } |
202 | 197 |
203 _pubServer.stdout.expect(startsWith("Loading source assets...")); | 198 _pubServer.stdout.expect(startsWith("Loading source assets...")); |
204 _pubServer.stdout.expect(consumeWhile(matches("Loading .* transformers..."))); | 199 _pubServer.stdout.expect(consumeWhile(matches("Loading .* transformers..."))); |
205 | 200 |
206 _pubServer.stdout.expect(predicate(_parseAdminPort)); | 201 _pubServer.stdout.expect(predicate(_parseAdminPort)); |
207 | 202 |
208 // The server should emit one or more ports. | 203 // The server should emit one or more ports. |
209 _pubServer.stdout.expect( | 204 _pubServer.stdout.expect( |
210 consumeWhile(predicate(_parsePort, 'emits server url'))); | 205 consumeWhile(predicate(_parsePort, 'emits server url'))); |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 } | 272 } |
278 | 273 |
279 /// Schedules an HTTP request to the running pub server with [urlPath] and | 274 /// Schedules an HTTP request to the running pub server with [urlPath] and |
280 /// verifies that it responds with a redirect to the given [redirectTarget]. | 275 /// verifies that it responds with a redirect to the given [redirectTarget]. |
281 /// | 276 /// |
282 /// [redirectTarget] may be either a [Matcher] or a string to match an exact | 277 /// [redirectTarget] may be either a [Matcher] or a string to match an exact |
283 /// URL. [root] indicates which server should be accessed, and defaults to | 278 /// URL. [root] indicates which server should be accessed, and defaults to |
284 /// "web". | 279 /// "web". |
285 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) { | 280 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) { |
286 schedule(() { | 281 schedule(() { |
287 var request = new http.Request("GET", | 282 var request = |
288 Uri.parse(_getServerUrlSync(root, urlPath))); | 283 new http.Request("GET", Uri.parse(_getServerUrlSync(root, urlPath))); |
289 request.followRedirects = false; | 284 request.followRedirects = false; |
290 return request.send().then((response) { | 285 return request.send().then((response) { |
291 expect(response.statusCode ~/ 100, equals(3)); | 286 expect(response.statusCode ~/ 100, equals(3)); |
292 expect(response.headers, containsPair('location', redirectTarget)); | 287 expect(response.headers, containsPair('location', redirectTarget)); |
293 }); | 288 }); |
294 }, "request $urlPath"); | 289 }, "request $urlPath"); |
295 } | 290 } |
296 | 291 |
297 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies | 292 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies |
298 /// that it responds with a 405. | 293 /// that it responds with a 405. |
299 /// | 294 /// |
300 /// [root] indicates which server should be accessed, and defaults to "web". | 295 /// [root] indicates which server should be accessed, and defaults to "web". |
301 void postShould405(String urlPath, {String root}) { | 296 void postShould405(String urlPath, {String root}) { |
302 schedule(() { | 297 schedule(() { |
303 return http.post(_getServerUrlSync(root, urlPath)).then((response) { | 298 return http.post(_getServerUrlSync(root, urlPath)).then((response) { |
304 expect(response.statusCode, equals(405)); | 299 expect(response.statusCode, equals(405)); |
305 }); | 300 }); |
306 }, "request $urlPath"); | 301 }, "request $urlPath"); |
307 } | 302 } |
308 | 303 |
309 /// Schedules an HTTP request to the (theoretically) running pub server with | 304 /// Schedules an HTTP request to the (theoretically) running pub server with |
310 /// [urlPath] and verifies that it cannot be connected to. | 305 /// [urlPath] and verifies that it cannot be connected to. |
311 /// | 306 /// |
312 /// [root] indicates which server should be accessed, and defaults to "web". | 307 /// [root] indicates which server should be accessed, and defaults to "web". |
313 void requestShouldNotConnect(String urlPath, {String root}) { | 308 void requestShouldNotConnect(String urlPath, {String root}) { |
314 schedule(() { | 309 schedule(() { |
315 return expect(http.get(_getServerUrlSync(root, urlPath)), | 310 return expect( |
| 311 http.get(_getServerUrlSync(root, urlPath)), |
316 throwsA(new isInstanceOf<SocketException>())); | 312 throwsA(new isInstanceOf<SocketException>())); |
317 }, "request $urlPath"); | 313 }, "request $urlPath"); |
318 } | 314 } |
319 | 315 |
320 /// Reads lines from pub serve's stdout until it prints the build success | 316 /// Reads lines from pub serve's stdout until it prints the build success |
321 /// message. | 317 /// message. |
322 /// | 318 /// |
323 /// The schedule will not proceed until the output is found. If not found, it | 319 /// The schedule will not proceed until the output is found. If not found, it |
324 /// will eventually time out. | 320 /// will eventually time out. |
325 void waitForBuildSuccess() => | 321 void waitForBuildSuccess() => |
326 _pubServer.stdout.expect(consumeThrough(contains("successfully"))); | 322 _pubServer.stdout.expect(consumeThrough(contains("successfully"))); |
327 | 323 |
328 /// Schedules opening a web socket connection to the currently running pub | 324 /// Schedules opening a web socket connection to the currently running pub |
329 /// serve. | 325 /// serve. |
330 Future _ensureWebSocket() { | 326 Future _ensureWebSocket() { |
331 // Use the existing one if already connected. | 327 // Use the existing one if already connected. |
332 if (_webSocket != null) return new Future.value(); | 328 if (_webSocket != null) return new Future.value(); |
333 | 329 |
334 // Server should already be running. | 330 // Server should already be running. |
335 expect(_pubServer, isNotNull); | 331 expect(_pubServer, isNotNull); |
336 expect(_adminPort, isNotNull); | 332 expect(_adminPort, isNotNull); |
337 | 333 |
338 return WebSocket.connect("ws://localhost:$_adminPort").then((socket) { | 334 return WebSocket.connect("ws://localhost:$_adminPort").then((socket) { |
339 _webSocket = socket; | 335 _webSocket = socket; |
340 // TODO(rnystrom): Works around #13913. | 336 // TODO(rnystrom): Works around #13913. |
341 _webSocketBroadcastStream = _webSocket.map(JSON.decode).asBroadcastStream(); | 337 _webSocketBroadcastStream = _webSocket.map(JSON.decode).asBroadcastStream(); |
342 }); | 338 }); |
343 } | 339 } |
344 | 340 |
345 /// Schedules closing the web socket connection to the currently-running pub | 341 /// Schedules closing the web socket connection to the currently-running pub |
346 /// serve. | 342 /// serve. |
347 void closeWebSocket() { | 343 void closeWebSocket() { |
348 schedule(() { | 344 schedule(() { |
349 return _ensureWebSocket().then((_) => _webSocket.close()) | 345 return _ensureWebSocket().then((_) => _webSocket.close()).then((_) => _webSo
cket = |
350 .then((_) => _webSocket = null); | 346 null); |
351 }, "closing web socket"); | 347 }, "closing web socket"); |
352 } | 348 } |
353 | 349 |
354 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket | 350 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket |
355 /// connection. | 351 /// connection. |
356 /// | 352 /// |
357 /// This calls a method named [method] with the given [params] (or no | 353 /// This calls a method named [method] with the given [params] (or no |
358 /// parameters, if it's not passed). [params] may contain Futures, in which case | 354 /// parameters, if it's not passed). [params] may contain Futures, in which case |
359 /// this will wait until they've completed before sending the request. | 355 /// this will wait until they've completed before sending the request. |
360 /// | 356 /// |
361 /// This schedules the request, but doesn't block the schedule on the response. | 357 /// This schedules the request, but doesn't block the schedule on the response. |
362 /// It returns the response as a [Future]. | 358 /// It returns the response as a [Future]. |
363 Future<Map> webSocketRequest(String method, [Map params]) { | 359 Future<Map> webSocketRequest(String method, [Map params]) { |
364 var completer = new Completer(); | 360 var completer = new Completer(); |
365 schedule(() { | 361 schedule(() { |
366 return Future.wait([ | 362 return Future.wait( |
367 _ensureWebSocket(), | 363 [_ensureWebSocket(), awaitObject(params),]).then((results) { |
368 awaitObject(params), | |
369 ]).then((results) { | |
370 var resolvedParams = results[1]; | 364 var resolvedParams = results[1]; |
371 chainToCompleter( | 365 chainToCompleter( |
372 currentSchedule.wrapFuture(_jsonRpcRequest(method, resolvedParams)), | 366 currentSchedule.wrapFuture(_jsonRpcRequest(method, resolvedParams)), |
373 completer); | 367 completer); |
374 }); | 368 }); |
375 }, "send $method with $params to web socket"); | 369 }, "send $method with $params to web socket"); |
376 return completer.future; | 370 return completer.future; |
377 } | 371 } |
378 | 372 |
379 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket | 373 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket |
380 /// connection, waits for a reply, then verifies the result. | 374 /// connection, waits for a reply, then verifies the result. |
381 /// | 375 /// |
382 /// This calls a method named [method] with the given [params]. [params] may | 376 /// This calls a method named [method] with the given [params]. [params] may |
383 /// contain Futures, in which case this will wait until they've completed before | 377 /// contain Futures, in which case this will wait until they've completed before |
384 /// sending the request. | 378 /// sending the request. |
385 /// | 379 /// |
386 /// The result is validated using [result], which may be a [Matcher] or a [Map] | 380 /// The result is validated using [result], which may be a [Matcher] or a [Map] |
387 /// containing [Matcher]s and [Future]s. This will wait until any futures are | 381 /// containing [Matcher]s and [Future]s. This will wait until any futures are |
388 /// completed before sending the request. | 382 /// completed before sending the request. |
389 /// | 383 /// |
390 /// Returns a [Future] that completes to the call's result. | 384 /// Returns a [Future] that completes to the call's result. |
391 Future<Map> expectWebSocketResult(String method, Map params, result) { | 385 Future<Map> expectWebSocketResult(String method, Map params, result) { |
392 return schedule(() { | 386 return schedule(() { |
393 return Future.wait([ | 387 return Future.wait( |
394 webSocketRequest(method, params), | 388 [webSocketRequest(method, params), awaitObject(result)]).then((results)
{ |
395 awaitObject(result) | |
396 ]).then((results) { | |
397 var response = results[0]; | 389 var response = results[0]; |
398 var resolvedResult = results[1]; | 390 var resolvedResult = results[1]; |
399 expect(response["result"], resolvedResult); | 391 expect(response["result"], resolvedResult); |
400 return response["result"]; | 392 return response["result"]; |
401 }); | 393 }); |
402 }, "send $method with $params to web socket and expect $result"); | 394 }, "send $method with $params to web socket and expect $result"); |
403 } | 395 } |
404 | 396 |
405 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket | 397 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket |
406 /// connection, waits for a reply, then verifies the error response. | 398 /// connection, waits for a reply, then verifies the error response. |
407 /// | 399 /// |
408 /// This calls a method named [method] with the given [params]. [params] may | 400 /// This calls a method named [method] with the given [params]. [params] may |
409 /// contain Futures, in which case this will wait until they've completed before | 401 /// contain Futures, in which case this will wait until they've completed before |
410 /// sending the request. | 402 /// sending the request. |
411 /// | 403 /// |
412 /// The error response is validated using [errorCode] and [errorMessage]. Both | 404 /// The error response is validated using [errorCode] and [errorMessage]. Both |
413 /// of these must be provided. The error code is checked against [errorCode] and | 405 /// of these must be provided. The error code is checked against [errorCode] and |
414 /// the error message is checked against [errorMessage]. Either of these may be | 406 /// the error message is checked against [errorMessage]. Either of these may be |
415 /// matchers. | 407 /// matchers. |
416 /// | 408 /// |
417 /// If [data] is provided, it is a JSON value or matcher used to validate the | 409 /// If [data] is provided, it is a JSON value or matcher used to validate the |
418 /// "data" value of the error response. | 410 /// "data" value of the error response. |
419 /// | 411 /// |
420 /// Returns a [Future] that completes to the error's [data] field. | 412 /// Returns a [Future] that completes to the error's [data] field. |
421 Future expectWebSocketError(String method, Map params, errorCode, | 413 Future expectWebSocketError(String method, Map params, errorCode, errorMessage, |
422 errorMessage, {data}) { | 414 {data}) { |
423 return schedule(() { | 415 return schedule(() { |
424 return webSocketRequest(method, params).then((response) { | 416 return webSocketRequest(method, params).then((response) { |
425 expect(response["error"]["code"], errorCode); | 417 expect(response["error"]["code"], errorCode); |
426 expect(response["error"]["message"], errorMessage); | 418 expect(response["error"]["message"], errorMessage); |
427 | 419 |
428 if (data != null) { | 420 if (data != null) { |
429 expect(response["error"]["data"], data); | 421 expect(response["error"]["data"], data); |
430 } | 422 } |
431 | 423 |
432 return response["error"]["data"]; | 424 return response["error"]["data"]; |
(...skipping 17 matching lines...) Expand all Loading... |
450 Future<Map> _jsonRpcRequest(String method, [Map params]) { | 442 Future<Map> _jsonRpcRequest(String method, [Map params]) { |
451 var id = _rpcId++; | 443 var id = _rpcId++; |
452 var message = { | 444 var message = { |
453 "jsonrpc": "2.0", | 445 "jsonrpc": "2.0", |
454 "method": method, | 446 "method": method, |
455 "id": id | 447 "id": id |
456 }; | 448 }; |
457 if (params != null) message["params"] = params; | 449 if (params != null) message["params"] = params; |
458 _webSocket.add(JSON.encode(message)); | 450 _webSocket.add(JSON.encode(message)); |
459 | 451 |
460 return _webSocketBroadcastStream | 452 return _webSocketBroadcastStream.firstWhere( |
461 .firstWhere((response) => response["id"] == id).then((value) { | 453 (response) => response["id"] == id).then((value) { |
462 currentSchedule.addDebugInfo( | 454 currentSchedule.addDebugInfo( |
463 "Web Socket request $method with params $params\n" | 455 "Web Socket request $method with params $params\n" "Result: $value"); |
464 "Result: $value"); | |
465 | 456 |
466 expect(value["id"], equals(id)); | 457 expect(value["id"], equals(id)); |
467 return value; | 458 return value; |
468 }); | 459 }); |
469 } | 460 } |
470 | 461 |
471 /// Returns a [Future] that completes to a URL string for the server serving | 462 /// Returns a [Future] that completes to a URL string for the server serving |
472 /// [path] from [root]. | 463 /// [path] from [root]. |
473 /// | 464 /// |
474 /// If [root] is omitted, defaults to "web". If [path] is omitted, no path is | 465 /// If [root] is omitted, defaults to "web". If [path] is omitted, no path is |
(...skipping 16 matching lines...) Expand all Loading... |
491 /// included. Unlike [getServerUrl], this should only be called after the ports | 482 /// included. Unlike [getServerUrl], this should only be called after the ports |
492 /// are known. | 483 /// are known. |
493 String _getServerUrlSync([String root, String path]) { | 484 String _getServerUrlSync([String root, String path]) { |
494 if (root == null) root = 'web'; | 485 if (root == null) root = 'web'; |
495 expect(_ports, contains(root)); | 486 expect(_ports, contains(root)); |
496 var url = "http://localhost:${_ports[root]}"; | 487 var url = "http://localhost:${_ports[root]}"; |
497 if (path != null) url = "$url/$path"; | 488 if (path != null) url = "$url/$path"; |
498 return url; | 489 return url; |
499 } | 490 } |
500 | 491 |
OLD | NEW |