Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(568)

Side by Side Diff: sdk/lib/_internal/pub/test/serve/utils.dart

Issue 167103003: Support serving from multiple directories using "pub serve". (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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; 12 import 'package:path/path.dart' as p;
13 import 'package:scheduled_test/scheduled_process.dart'; 13 import 'package:scheduled_test/scheduled_process.dart';
14 import 'package:scheduled_test/scheduled_stream.dart'; 14 import 'package:scheduled_test/scheduled_stream.dart';
15 import 'package:scheduled_test/scheduled_test.dart'; 15 import 'package:scheduled_test/scheduled_test.dart';
16 16
17 import '../descriptor.dart' as d;
17 import '../test_pub.dart'; 18 import '../test_pub.dart';
18 19
19 /// The pub process running "pub serve". 20 /// The pub process running "pub serve".
20 ScheduledProcess _pubServer; 21 ScheduledProcess _pubServer;
21 22
22 /// The ephemeral port assigned to the running server. 23 /// The ephemeral ports assigned to the running servers, associated with the
23 int _port; 24 /// directories they're serving.
25 final _ports = new Map<String, int>();
24 26
25 /// The web socket connection to the running pub process, or `null` if no 27 /// The web socket connection to the running pub process, or `null` if no
26 /// connection has been made. 28 /// connection has been made.
27 WebSocket _webSocket; 29 WebSocket _webSocket;
28 Stream _webSocketBroadcastStream; 30 Stream _webSocketBroadcastStream;
29 31
30 /// The code for a transformer that renames ".txt" files to ".out" and adds a 32 /// The code for a transformer that renames ".txt" files to ".out" and adds a
31 /// ".out" suffix. 33 /// ".out" suffix.
32 const REWRITE_TRANSFORMER = """ 34 const REWRITE_TRANSFORMER = """
33 import 'dart:async'; 35 import 'dart:async';
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
93 } 95 }
94 """; 96 """;
95 } 97 }
96 98
97 /// Schedules starting the `pub serve` process. 99 /// Schedules starting the `pub serve` process.
98 /// 100 ///
99 /// Unlike [pubServe], this doesn't determine the port number of the server, and 101 /// Unlike [pubServe], this doesn't determine the port number of the server, and
100 /// so may be used to test for errors in the initialization process. 102 /// so may be used to test for errors in the initialization process.
101 /// 103 ///
102 /// Returns the `pub serve` process. 104 /// Returns the `pub serve` process.
103 ScheduledProcess startPubServe([Iterable<String> args]) { 105 ScheduledProcess startPubServe({Iterable<String> args,
106 bool createWebDir: true}) {
104 // Use port 0 to get an ephemeral port. 107 // Use port 0 to get an ephemeral port.
105 var pubArgs = ["serve", "--port=0", "--hostname=127.0.0.1", "--force-poll"]; 108 var pubArgs = ["serve", "--port=0", "--hostname=127.0.0.1", "--force-poll"];
106 109
107 if (args != null) pubArgs.addAll(args); 110 if (args != null) pubArgs.addAll(args);
108 111
109 // Dart2js can take a long time to compile dart code, so we increase the 112 // Dart2js can take a long time to compile dart code, so we increase the
110 // timeout to cope with that. 113 // timeout to cope with that.
111 currentSchedule.timeout *= 1.5; 114 currentSchedule.timeout *= 1.5;
112 115
116 if (createWebDir) d.dir(appPath, [d.dir("web")]).create();
113 return startPub(args: pubArgs); 117 return startPub(args: pubArgs);
114 } 118 }
115 119
116 /// Schedules starting the "pub serve" process and records its port number for 120 /// Schedules starting the "pub serve" process and records its port number for
117 /// future requests. 121 /// future requests.
118 /// 122 ///
119 /// If [shouldGetFirst] is `true`, validates that pub get is run first. 123 /// If [shouldGetFirst] is `true`, validates that pub get is run first.
120 /// 124 ///
125 /// If [createWebDir] is `true`, creates a `web/` directory if one doesn't exist
126 /// so pub doesn't complain about having nothing to serve.
127 ///
121 /// Returns the `pub serve` process. 128 /// Returns the `pub serve` process.
122 ScheduledProcess pubServe({bool shouldGetFirst: false, Iterable<String> args}) { 129 ScheduledProcess pubServe({bool shouldGetFirst: false, bool createWebDir: true,
123 _pubServer = startPubServe(args); 130 Iterable<String> args}) {
131 _pubServer = startPubServe(args: args, createWebDir: createWebDir);
124 132
125 currentSchedule.onComplete.schedule(() { 133 currentSchedule.onComplete.schedule(() {
134 _ports.clear();
135
126 if (_webSocket != null) { 136 if (_webSocket != null) {
127 _webSocket.close(); 137 _webSocket.close();
128 _webSocket = null; 138 _webSocket = null;
129 _webSocketBroadcastStream = null; 139 _webSocketBroadcastStream = null;
130 } 140 }
131 }); 141 });
132 142
133 if (shouldGetFirst) { 143 if (shouldGetFirst) {
134 _pubServer.stdout.expect(consumeThrough("Got dependencies!")); 144 _pubServer.stdout.expect(consumeThrough("Got dependencies!"));
135 } 145 }
136 146
137 expect(schedule(() => _pubServer.stdout.next()).then(_parsePort), completes); 147 // The server should emit one or more ports.
148 _pubServer.stdout.expect(predicate(_parsePort, 'emits server url'));
Bob Nystrom 2014/02/19 00:36:52 Given the isNot(isEmpty) below, is this line neces
nweiz 2014/02/19 01:25:58 Nope, removed.
149 _pubServer.stdout.expect(
150 consumeWhile(predicate(_parsePort, 'emits server url')));
151 schedule(() => expect(_ports, isNot(isEmpty)));
152
138 return _pubServer; 153 return _pubServer;
139 } 154 }
140 155
156 /// The regular expression for parsing pub's output line describing the URL for
157 /// the server.
158 final _parsePortRegExp = new RegExp(r"([^ ]+)/ +on http://127\.0\.0\.1:(\d+)");
159
141 /// Parses the port number from the "Serving blah on 127.0.0.1:1234" line 160 /// Parses the port number from the "Serving blah on 127.0.0.1:1234" line
142 /// printed by pub serve. 161 /// printed by pub serve.
143 void _parsePort(String line) { 162 bool _parsePort(String line) {
144 var match = new RegExp(r"127\.0\.0\.1:(\d+)").firstMatch(line); 163 var match = _parsePortRegExp.firstMatch(line);
145 assert(match != null); 164 if (match == null) return false;
146 _port = int.parse(match[1]); 165 _ports[match[1]] = int.parse(match[2]);
166 return true;
147 } 167 }
148 168
149 void endPubServe() { 169 void endPubServe() {
150 _pubServer.kill(); 170 _pubServer.kill();
151 } 171 }
152 172
153 /// Schedules an HTTP request to the running pub server with [urlPath] and 173 /// Schedules an HTTP request to the running pub server with [urlPath] and
154 /// verifies that it responds with a body that matches [expectation]. 174 /// verifies that it responds with a body that matches [expectation].
155 /// 175 ///
156 /// [expectation] may either be a [Matcher] or a string to match an exact body. 176 /// [expectation] may either be a [Matcher] or a string to match an exact body.
177 /// [root] indicates which server should be accessed, and defaults to "web".
157 /// [headers] may be either a [Matcher] or a map to match an exact headers map. 178 /// [headers] may be either a [Matcher] or a map to match an exact headers map.
158 void requestShouldSucceed(String urlPath, expectation, {headers}) { 179 void requestShouldSucceed(String urlPath, expectation, {String root, headers}) {
159 schedule(() { 180 schedule(() {
160 return http.get("http://127.0.0.1:$_port/$urlPath").then((response) { 181 return http.get("${_serverUrl(root)}/$urlPath").then((response) {
161 if (expectation != null) expect(response.body, expectation); 182 if (expectation != null) expect(response.body, expectation);
162 if (headers != null) expect(response.headers, headers); 183 if (headers != null) expect(response.headers, headers);
163 }); 184 });
164 }, "request $urlPath"); 185 }, "request $urlPath");
165 } 186 }
166 187
167 /// Schedules an HTTP request to the running pub server with [urlPath] and 188 /// Schedules an HTTP request to the running pub server with [urlPath] and
168 /// verifies that it responds with a 404. 189 /// verifies that it responds with a 404.
169 void requestShould404(String urlPath) { 190 ///
191 /// [root] indicates which server should be accessed, and defaults to "web".
192 void requestShould404(String urlPath, {String root}) {
170 schedule(() { 193 schedule(() {
171 return http.get("http://127.0.0.1:$_port/$urlPath").then((response) { 194 return http.get("${_serverUrl(root)}/$urlPath").then((response) {
172 expect(response.statusCode, equals(404)); 195 expect(response.statusCode, equals(404));
173 }); 196 });
174 }, "request $urlPath"); 197 }, "request $urlPath");
175 } 198 }
176 199
177 /// Schedules an HTTP request to the running pub server with [urlPath] and 200 /// Schedules an HTTP request to the running pub server with [urlPath] and
178 /// verifies that it responds with a redirect to the given [redirectTarget]. 201 /// verifies that it responds with a redirect to the given [redirectTarget].
179 /// 202 ///
180 /// [redirectTarget] may be either a [Matcher] or a string to match an exact 203 /// [redirectTarget] may be either a [Matcher] or a string to match an exact
181 /// URL. 204 /// URL. [root] indicates which server should be accessed, and defaults to
182 void requestShouldRedirect(String urlPath, redirectTarget) { 205 /// "web".
206 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) {
183 schedule(() { 207 schedule(() {
184 var request = new http.Request("GET", 208 var request = new http.Request("GET",
185 Uri.parse("http://127.0.0.1:$_port/$urlPath")); 209 Uri.parse("${_serverUrl(root)}/$urlPath"));
186 request.followRedirects = false; 210 request.followRedirects = false;
187 return request.send().then((response) { 211 return request.send().then((response) {
188 expect(response.statusCode ~/ 100, equals(3)); 212 expect(response.statusCode ~/ 100, equals(3));
189 213
190 expect(response.headers, containsPair('location', redirectTarget)); 214 expect(response.headers, containsPair('location', redirectTarget));
191 }); 215 });
192 }, "request $urlPath"); 216 }, "request $urlPath");
193 } 217 }
194 218
195 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies 219 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies
196 /// that it responds with a 405. 220 /// that it responds with a 405.
197 void postShould405(String urlPath) { 221 ///
222 /// [root] indicates which server should be accessed, and defaults to "web".
223 void postShould405(String urlPath, {String root}) {
198 schedule(() { 224 schedule(() {
199 return http.post("http://127.0.0.1:$_port/$urlPath").then((response) { 225 return http.post("${_serverUrl(root)}/$urlPath").then((response) {
200 expect(response.statusCode, equals(405)); 226 expect(response.statusCode, equals(405));
201 }); 227 });
202 }, "request $urlPath"); 228 }, "request $urlPath");
203 } 229 }
204 230
205 /// Reads lines from pub serve's stdout until it prints the build success 231 /// Reads lines from pub serve's stdout until it prints the build success
206 /// message. 232 /// message.
207 /// 233 ///
208 /// The schedule will not proceed until the output is found. If not found, it 234 /// The schedule will not proceed until the output is found. If not found, it
209 /// will eventually time out. 235 /// will eventually time out.
210 void waitForBuildSuccess() => 236 void waitForBuildSuccess() =>
211 _pubServer.stdout.expect(consumeThrough(contains("successfully"))); 237 _pubServer.stdout.expect(consumeThrough(contains("successfully")));
212 238
213 /// Schedules opening a web socket connection to the currently running pub 239 /// Schedules opening a web socket connection to the currently running pub
214 /// serve. 240 /// serve.
215 Future _ensureWebSocket() { 241 Future _ensureWebSocket() {
216 // Use the existing one if already connected. 242 // Use the existing one if already connected.
217 if (_webSocket != null) return new Future.value(); 243 if (_webSocket != null) return new Future.value();
218 244
219 // Server should already be running. 245 // Server should already be running.
220 assert(_pubServer != null); 246 expect(_pubServer, isNotNull);
221 assert(_port != null); 247 expect(_ports, isNot(isEmpty));
222 248
223 return WebSocket.connect("ws://127.0.0.1:$_port").then((socket) { 249 var port = _ports.values.first;
Bob Nystrom 2014/02/19 00:36:52 Do we intend to support the Web Socket API on ever
nweiz 2014/02/19 01:25:58 Done.
250 return WebSocket.connect("ws://127.0.0.1:$port").then((socket) {
224 _webSocket = socket; 251 _webSocket = socket;
225 // TODO(rnystrom): Works around #13913. 252 // TODO(rnystrom): Works around #13913.
226 _webSocketBroadcastStream = _webSocket.asBroadcastStream(); 253 _webSocketBroadcastStream = _webSocket.asBroadcastStream();
227 }); 254 });
228 } 255 }
229 256
230 /// Sends [request] (an arbitrary JSON object) to the running pub serve's web 257 /// Sends [request] (an arbitrary JSON object) to the running pub serve's web
231 /// socket connection, waits for a reply, then verifies that the reply matches 258 /// socket connection, waits for a reply, then verifies that the reply matches
232 /// [expectation]. 259 /// [expectation].
233 /// 260 ///
234 /// If [encodeRequest] is `false`, then [request] will be sent as-is over the 261 /// If [encodeRequest] is `false`, then [request] will be sent as-is over the
235 /// socket. It omitted, request is JSON encoded to a string first. 262 /// socket. It omitted, request is JSON encoded to a string first.
236 void webSocketShouldReply(request, expectation, {bool encodeRequest: true}) { 263 void webSocketShouldReply(request, expectation, {bool encodeRequest: true}) {
237 schedule(() => _ensureWebSocket().then((_) { 264 schedule(() => _ensureWebSocket().then((_) {
238 if (encodeRequest) request = JSON.encode(request); 265 if (encodeRequest) request = JSON.encode(request);
239 _webSocket.add(request); 266 _webSocket.add(request);
240 return _webSocketBroadcastStream.first.then((value) { 267 return _webSocketBroadcastStream.first.then((value) {
241 expect(JSON.decode(value), expectation); 268 expect(JSON.decode(value), expectation);
242 }); 269 });
243 }), "send $request to web socket and expect reply that $expectation"); 270 }), "send $request to web socket and expect reply that $expectation");
271 }
272
273 /// Returns the URL for the server serving from [root].
274 String _serverUrl([String root]) {
275 if (root == null) root = 'web';
276 expect(_ports, contains(root));
277 return "http://127.0.0.1:${_ports[root]}";
244 } 278 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698