| Index: sdk/lib/_internal/pub_generated/test/serve/utils.dart
|
| diff --git a/sdk/lib/_internal/pub_generated/test/serve/utils.dart b/sdk/lib/_internal/pub_generated/test/serve/utils.dart
|
| index 3460ac97e43499926971feeae11490b646e6339e..d7844180ea527c4613f8ed39ae76804cf67dada0 100644
|
| --- a/sdk/lib/_internal/pub_generated/test/serve/utils.dart
|
| +++ b/sdk/lib/_internal/pub_generated/test/serve/utils.dart
|
| @@ -1,21 +1,44 @@
|
| +// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS d.file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| library pub_tests;
|
| +
|
| import 'dart:async';
|
| import 'dart:convert';
|
| import 'dart:io';
|
| +
|
| import 'package:http/http.dart' as http;
|
| import 'package:scheduled_test/scheduled_process.dart';
|
| import 'package:scheduled_test/scheduled_stream.dart';
|
| import 'package:scheduled_test/scheduled_test.dart';
|
| import 'package:stack_trace/stack_trace.dart';
|
| +
|
| import '../../lib/src/utils.dart';
|
| import '../descriptor.dart' as d;
|
| import '../test_pub.dart';
|
| +
|
| +/// The pub process running "pub serve".
|
| ScheduledProcess _pubServer;
|
| +
|
| +/// The ephemeral port assign to the running admin server.
|
| int _adminPort;
|
| +
|
| +/// The ephemeral ports assigned to the running servers, associated with the
|
| +/// directories they're serving.
|
| final _ports = new Map<String, int>();
|
| +
|
| +/// A completer that completes when the server has been started and the served
|
| +/// ports are known.
|
| Completer _portsCompleter;
|
| +
|
| +/// The web socket connection to the running pub process, or `null` if no
|
| +/// connection has been made.
|
| WebSocket _webSocket;
|
| Stream _webSocketBroadcastStream;
|
| +
|
| +/// The code for a transformer that renames ".txt" files to ".out" and adds a
|
| +/// ".out" suffix.
|
| const REWRITE_TRANSFORMER = """
|
| import 'dart:async';
|
|
|
| @@ -34,6 +57,8 @@ class RewriteTransformer extends Transformer {
|
| }
|
| }
|
| """;
|
| +
|
| +/// The code for a lazy version of [REWRITE_TRANSFORMER].
|
| const LAZY_TRANSFORMER = """
|
| import 'dart:async';
|
|
|
| @@ -58,7 +83,24 @@ class LazyRewriteTransformer extends Transformer implements LazyTransformer {
|
| }
|
| }
|
| """;
|
| +
|
| +/// The web socket error code for a directory not being served.
|
| const NOT_SERVED = 1;
|
| +
|
| +/// Returns the source code for a Dart library defining a Transformer that
|
| +/// rewrites Dart files.
|
| +///
|
| +/// The transformer defines a constant named TOKEN whose value is [id]. When the
|
| +/// transformer transforms another Dart file, it will look for a "TOKEN"
|
| +/// constant definition there and modify it to include *this* transformer's
|
| +/// TOKEN value as well.
|
| +///
|
| +/// If [import] is passed, it should be the name of a package that defines its
|
| +/// own TOKEN constant. The primary library of that package will be imported
|
| +/// here and its TOKEN value will be added to this library's.
|
| +///
|
| +/// This transformer takes one configuration field: "addition". This is
|
| +/// concatenated to its TOKEN value before adding it to the output library.
|
| String dartTransformer(String id, {String import}) {
|
| if (import != null) {
|
| id = '$id imports \${$import.TOKEN}';
|
| @@ -66,6 +108,7 @@ String dartTransformer(String id, {String import}) {
|
| } else {
|
| import = '';
|
| }
|
| +
|
| return """
|
| import 'dart:async';
|
|
|
| @@ -99,75 +142,140 @@ class DartTransformer extends Transformer {
|
| }
|
| """;
|
| }
|
| +
|
| +/// Schedules starting the `pub serve` process.
|
| +///
|
| +/// Unlike [pubServe], this doesn't determine the port number of the server, and
|
| +/// so may be used to test for errors in the initialization process.
|
| +///
|
| +/// Returns the `pub serve` process.
|
| ScheduledProcess startPubServe({Iterable<String> args, bool createWebDir: true})
|
| {
|
| - var pubArgs = ["serve", "--port=0", "--force-poll", "--log-admin-url"];
|
| + var pubArgs = ["serve", "--port=0", // Use port 0 to get an ephemeral port.
|
| + "--force-poll", "--log-admin-url"];
|
| +
|
| if (args != null) pubArgs.addAll(args);
|
| +
|
| + // Dart2js can take a long time to compile dart code, so we increase the
|
| + // timeout to cope with that.
|
| currentSchedule.timeout *= 1.5;
|
| +
|
| if (createWebDir) d.dir(appPath, [d.dir("web")]).create();
|
| return startPub(args: pubArgs);
|
| }
|
| +
|
| +/// Schedules starting the "pub serve" process and records its port number for
|
| +/// future requests.
|
| +///
|
| +/// If [shouldGetFirst] is `true`, validates that pub get is run first.
|
| +///
|
| +/// If [createWebDir] is `true`, creates a `web/` directory if one doesn't exist
|
| +/// so pub doesn't complain about having nothing to serve.
|
| +///
|
| +/// Returns the `pub serve` process.
|
| ScheduledProcess pubServe({bool shouldGetFirst: false, bool createWebDir: true,
|
| Iterable<String> args}) {
|
| _pubServer = startPubServe(args: args, createWebDir: createWebDir);
|
| _portsCompleter = new Completer();
|
| +
|
| currentSchedule.onComplete.schedule(() {
|
| _portsCompleter = null;
|
| _ports.clear();
|
| +
|
| if (_webSocket != null) {
|
| _webSocket.close();
|
| _webSocket = null;
|
| _webSocketBroadcastStream = null;
|
| }
|
| });
|
| +
|
| if (shouldGetFirst) {
|
| _pubServer.stdout.expect(
|
| consumeThrough(
|
| anyOf(["Got dependencies!", matches(new RegExp(r"^Changed \d+ dependenc"))])));
|
| }
|
| +
|
| _pubServer.stdout.expect(startsWith("Loading source assets..."));
|
| _pubServer.stdout.expect(consumeWhile(matches("Loading .* transformers...")));
|
| +
|
| _pubServer.stdout.expect(predicate(_parseAdminPort));
|
| +
|
| + // The server should emit one or more ports.
|
| _pubServer.stdout.expect(
|
| consumeWhile(predicate(_parsePort, 'emits server url')));
|
| schedule(() {
|
| expect(_ports, isNot(isEmpty));
|
| _portsCompleter.complete();
|
| });
|
| +
|
| return _pubServer;
|
| }
|
| +
|
| +/// The regular expression for parsing pub's output line describing the URL for
|
| +/// the server.
|
| final _parsePortRegExp = new RegExp(r"([^ ]+) +on http://localhost:(\d+)");
|
| +
|
| +/// Parses the port number from the "Running admin server on localhost:1234"
|
| +/// line printed by pub serve.
|
| bool _parseAdminPort(String line) {
|
| var match = _parsePortRegExp.firstMatch(line);
|
| if (match == null) return false;
|
| _adminPort = int.parse(match[2]);
|
| return true;
|
| }
|
| +
|
| +/// Parses the port number from the "Serving blah on localhost:1234" line
|
| +/// printed by pub serve.
|
| bool _parsePort(String line) {
|
| var match = _parsePortRegExp.firstMatch(line);
|
| if (match == null) return false;
|
| _ports[match[1]] = int.parse(match[2]);
|
| return true;
|
| }
|
| +
|
| void endPubServe() {
|
| _pubServer.kill();
|
| }
|
| +
|
| +/// Schedules an HTTP request to the running pub server with [urlPath] and
|
| +/// invokes [callback] with the response.
|
| +///
|
| +/// [root] indicates which server should be accessed, and defaults to "web".
|
| Future<http.Response> scheduleRequest(String urlPath, {String root}) {
|
| return schedule(() {
|
| return http.get(_getServerUrlSync(root, urlPath));
|
| }, "request $urlPath");
|
| }
|
| +
|
| +/// Schedules an HTTP request to the running pub server with [urlPath] and
|
| +/// verifies that it responds with a body that matches [expectation].
|
| +///
|
| +/// [expectation] may either be a [Matcher] or a string to match an exact body.
|
| +/// [root] indicates which server should be accessed, and defaults to "web".
|
| +/// [headers] may be either a [Matcher] or a map to match an exact headers map.
|
| void requestShouldSucceed(String urlPath, expectation, {String root, headers}) {
|
| scheduleRequest(urlPath, root: root).then((response) {
|
| if (expectation != null) expect(response.body, expectation);
|
| if (headers != null) expect(response.headers, headers);
|
| });
|
| }
|
| +
|
| +/// Schedules an HTTP request to the running pub server with [urlPath] and
|
| +/// verifies that it responds with a 404.
|
| +///
|
| +/// [root] indicates which server should be accessed, and defaults to "web".
|
| void requestShould404(String urlPath, {String root}) {
|
| scheduleRequest(urlPath, root: root).then((response) {
|
| expect(response.statusCode, equals(404));
|
| });
|
| }
|
| +
|
| +/// Schedules an HTTP request to the running pub server with [urlPath] and
|
| +/// verifies that it responds with a redirect to the given [redirectTarget].
|
| +///
|
| +/// [redirectTarget] may be either a [Matcher] or a string to match an exact
|
| +/// URL. [root] indicates which server should be accessed, and defaults to
|
| +/// "web".
|
| void requestShouldRedirect(String urlPath, redirectTarget, {String root}) {
|
| schedule(() {
|
| var request =
|
| @@ -179,6 +287,11 @@ void requestShouldRedirect(String urlPath, redirectTarget, {String root}) {
|
| });
|
| }, "request $urlPath");
|
| }
|
| +
|
| +/// Schedules an HTTP POST to the running pub server with [urlPath] and verifies
|
| +/// that it responds with a 405.
|
| +///
|
| +/// [root] indicates which server should be accessed, and defaults to "web".
|
| void postShould405(String urlPath, {String root}) {
|
| schedule(() {
|
| return http.post(_getServerUrlSync(root, urlPath)).then((response) {
|
| @@ -186,6 +299,11 @@ void postShould405(String urlPath, {String root}) {
|
| });
|
| }, "request $urlPath");
|
| }
|
| +
|
| +/// Schedules an HTTP request to the (theoretically) running pub server with
|
| +/// [urlPath] and verifies that it cannot be connected to.
|
| +///
|
| +/// [root] indicates which server should be accessed, and defaults to "web".
|
| void requestShouldNotConnect(String urlPath, {String root}) {
|
| schedule(() {
|
| return expect(
|
| @@ -193,28 +311,55 @@ void requestShouldNotConnect(String urlPath, {String root}) {
|
| throwsA(new isInstanceOf<SocketException>()));
|
| }, "request $urlPath");
|
| }
|
| +
|
| +/// Reads lines from pub serve's stdout until it prints the build success
|
| +/// message.
|
| +///
|
| +/// The schedule will not proceed until the output is found. If not found, it
|
| +/// will eventually time out.
|
| void waitForBuildSuccess() =>
|
| _pubServer.stdout.expect(consumeThrough(contains("successfully")));
|
| +
|
| +/// Schedules opening a web socket connection to the currently running pub
|
| +/// serve.
|
| Future _ensureWebSocket() {
|
| + // Use the existing one if already connected.
|
| if (_webSocket != null) return new Future.value();
|
| +
|
| + // Server should already be running.
|
| expect(_pubServer, isNotNull);
|
| expect(_adminPort, isNotNull);
|
| +
|
| return WebSocket.connect("ws://localhost:$_adminPort").then((socket) {
|
| _webSocket = socket;
|
| + // TODO(rnystrom): Works around #13913.
|
| _webSocketBroadcastStream = _webSocket.map(JSON.decode).asBroadcastStream();
|
| });
|
| }
|
| +
|
| +/// Schedules closing the web socket connection to the currently-running pub
|
| +/// serve.
|
| void closeWebSocket() {
|
| schedule(() {
|
| return _ensureWebSocket().then((_) => _webSocket.close()).then((_) => _webSocket =
|
| null);
|
| }, "closing web socket");
|
| }
|
| +
|
| +/// Sends a JSON RPC 2.0 request to the running pub serve's web socket
|
| +/// connection.
|
| +///
|
| +/// This calls a method named [method] with the given [params] (or no
|
| +/// parameters, if it's not passed). [params] may contain Futures, in which case
|
| +/// this will wait until they've completed before sending the request.
|
| +///
|
| +/// This schedules the request, but doesn't block the schedule on the response.
|
| +/// It returns the response as a [Future].
|
| Future<Map> webSocketRequest(String method, [Map params]) {
|
| var completer = new Completer();
|
| schedule(() {
|
| return Future.wait(
|
| - [_ensureWebSocket(), awaitObject(params)]).then((results) {
|
| + [_ensureWebSocket(), awaitObject(params),]).then((results) {
|
| var resolvedParams = results[1];
|
| chainToCompleter(
|
| currentSchedule.wrapFuture(_jsonRpcRequest(method, resolvedParams)),
|
| @@ -223,6 +368,19 @@ Future<Map> webSocketRequest(String method, [Map params]) {
|
| }, "send $method with $params to web socket");
|
| return completer.future;
|
| }
|
| +
|
| +/// Sends a JSON RPC 2.0 request to the running pub serve's web socket
|
| +/// connection, waits for a reply, then verifies the result.
|
| +///
|
| +/// This calls a method named [method] with the given [params]. [params] may
|
| +/// contain Futures, in which case this will wait until they've completed before
|
| +/// sending the request.
|
| +///
|
| +/// The result is validated using [result], which may be a [Matcher] or a [Map]
|
| +/// containing [Matcher]s and [Future]s. This will wait until any futures are
|
| +/// completed before sending the request.
|
| +///
|
| +/// Returns a [Future] that completes to the call's result.
|
| Future<Map> expectWebSocketResult(String method, Map params, result) {
|
| return schedule(() {
|
| return Future.wait(
|
| @@ -234,25 +392,52 @@ Future<Map> expectWebSocketResult(String method, Map params, result) {
|
| });
|
| }, "send $method with $params to web socket and expect $result");
|
| }
|
| +
|
| +/// Sends a JSON RPC 2.0 request to the running pub serve's web socket
|
| +/// connection, waits for a reply, then verifies the error response.
|
| +///
|
| +/// This calls a method named [method] with the given [params]. [params] may
|
| +/// contain Futures, in which case this will wait until they've completed before
|
| +/// sending the request.
|
| +///
|
| +/// The error response is validated using [errorCode] and [errorMessage]. Both
|
| +/// of these must be provided. The error code is checked against [errorCode] and
|
| +/// the error message is checked against [errorMessage]. Either of these may be
|
| +/// matchers.
|
| +///
|
| +/// If [data] is provided, it is a JSON value or matcher used to validate the
|
| +/// "data" value of the error response.
|
| +///
|
| +/// Returns a [Future] that completes to the error's [data] field.
|
| Future expectWebSocketError(String method, Map params, errorCode, errorMessage,
|
| {data}) {
|
| return schedule(() {
|
| return webSocketRequest(method, params).then((response) {
|
| expect(response["error"]["code"], errorCode);
|
| expect(response["error"]["message"], errorMessage);
|
| +
|
| if (data != null) {
|
| expect(response["error"]["data"], data);
|
| }
|
| +
|
| return response["error"]["data"];
|
| });
|
| }, "send $method with $params to web socket and expect error $errorCode");
|
| }
|
| +
|
| +/// Validates that [root] was not bound to a port when pub serve started.
|
| Future expectNotServed(String root) {
|
| return schedule(() {
|
| expect(_ports.containsKey(root), isFalse);
|
| });
|
| }
|
| +
|
| +/// The next id to use for a JSON-RPC 2.0 request.
|
| var _rpcId = 0;
|
| +
|
| +/// Sends a JSON-RPC 2.0 request calling [method] with [params].
|
| +///
|
| +/// Returns the response object.
|
| Future<Map> _jsonRpcRequest(String method, [Map params]) {
|
| var id = _rpcId++;
|
| var message = {
|
| @@ -262,19 +447,39 @@ Future<Map> _jsonRpcRequest(String method, [Map params]) {
|
| };
|
| if (params != null) message["params"] = params;
|
| _webSocket.add(JSON.encode(message));
|
| +
|
| return _webSocketBroadcastStream.firstWhere(
|
| (response) => response["id"] == id).then((value) {
|
| currentSchedule.addDebugInfo(
|
| "Web Socket request $method with params $params\n" "Result: $value");
|
| +
|
| expect(value["id"], equals(id));
|
| return value;
|
| });
|
| }
|
| +
|
| +/// Returns a [Future] that completes to a URL string for the server serving
|
| +/// [path] from [root].
|
| +///
|
| +/// If [root] is omitted, defaults to "web". If [path] is omitted, no path is
|
| +/// included. The Future will complete once the server is up and running and
|
| +/// the bound ports are known.
|
| Future<String> getServerUrl([String root, String path]) =>
|
| _portsCompleter.future.then((_) => _getServerUrlSync(root, path));
|
| +
|
| +/// Records that [root] has been bound to [port].
|
| +///
|
| +/// Used for testing the Web Socket API for binding new root directories to
|
| +/// ports after pub serve has been started.
|
| registerServerPort(String root, int port) {
|
| _ports[root] = port;
|
| }
|
| +
|
| +/// Returns a URL string for the server serving [path] from [root].
|
| +///
|
| +/// If [root] is omitted, defaults to "web". If [path] is omitted, no path is
|
| +/// included. Unlike [getServerUrl], this should only be called after the ports
|
| +/// are known.
|
| String _getServerUrlSync([String root, String path]) {
|
| if (root == null) root = 'web';
|
| expect(_ports, contains(root));
|
| @@ -282,3 +487,4 @@ String _getServerUrlSync([String root, String path]) {
|
| if (path != null) url = "$url/$path";
|
| return url;
|
| }
|
| +
|
|
|