| OLD | NEW |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 1 library pub_tests; | 5 library pub_tests; |
| 6 |
| 2 import 'dart:async'; | 7 import 'dart:async'; |
| 3 import 'dart:convert'; | 8 import 'dart:convert'; |
| 4 import 'dart:io'; | 9 import 'dart:io'; |
| 10 |
| 5 import 'package:http/http.dart' as http; | 11 import 'package:http/http.dart' as http; |
| 6 import 'package:scheduled_test/scheduled_process.dart'; | 12 import 'package:scheduled_test/scheduled_process.dart'; |
| 7 import 'package:scheduled_test/scheduled_stream.dart'; | 13 import 'package:scheduled_test/scheduled_stream.dart'; |
| 8 import 'package:scheduled_test/scheduled_test.dart'; | 14 import 'package:scheduled_test/scheduled_test.dart'; |
| 9 import 'package:stack_trace/stack_trace.dart'; | 15 import 'package:stack_trace/stack_trace.dart'; |
| 16 |
| 10 import '../../lib/src/utils.dart'; | 17 import '../../lib/src/utils.dart'; |
| 11 import '../descriptor.dart' as d; | 18 import '../descriptor.dart' as d; |
| 12 import '../test_pub.dart'; | 19 import '../test_pub.dart'; |
| 20 |
| 21 /// The pub process running "pub serve". |
| 13 ScheduledProcess _pubServer; | 22 ScheduledProcess _pubServer; |
| 23 |
| 24 /// The ephemeral port assign to the running admin server. |
| 14 int _adminPort; | 25 int _adminPort; |
| 26 |
| 27 /// The ephemeral ports assigned to the running servers, associated with the |
| 28 /// directories they're serving. |
| 15 final _ports = new Map<String, int>(); | 29 final _ports = new Map<String, int>(); |
| 30 |
| 31 /// A completer that completes when the server has been started and the served |
| 32 /// ports are known. |
| 16 Completer _portsCompleter; | 33 Completer _portsCompleter; |
| 34 |
| 35 /// The web socket connection to the running pub process, or `null` if no |
| 36 /// connection has been made. |
| 17 WebSocket _webSocket; | 37 WebSocket _webSocket; |
| 18 Stream _webSocketBroadcastStream; | 38 Stream _webSocketBroadcastStream; |
| 39 |
| 40 /// The code for a transformer that renames ".txt" files to ".out" and adds a |
| 41 /// ".out" suffix. |
| 19 const REWRITE_TRANSFORMER = """ | 42 const REWRITE_TRANSFORMER = """ |
| 20 import 'dart:async'; | 43 import 'dart:async'; |
| 21 | 44 |
| 22 import 'package:barback/barback.dart'; | 45 import 'package:barback/barback.dart'; |
| 23 | 46 |
| 24 class RewriteTransformer extends Transformer { | 47 class RewriteTransformer extends Transformer { |
| 25 RewriteTransformer.asPlugin(); | 48 RewriteTransformer.asPlugin(); |
| 26 | 49 |
| 27 String get allowedExtensions => '.txt'; | 50 String get allowedExtensions => '.txt'; |
| 28 | 51 |
| 29 Future apply(Transform transform) { | 52 Future apply(Transform transform) { |
| 30 return transform.primaryInput.readAsString().then((contents) { | 53 return transform.primaryInput.readAsString().then((contents) { |
| 31 var id = transform.primaryInput.id.changeExtension(".out"); | 54 var id = transform.primaryInput.id.changeExtension(".out"); |
| 32 transform.addOutput(new Asset.fromString(id, "\$contents.out")); | 55 transform.addOutput(new Asset.fromString(id, "\$contents.out")); |
| 33 }); | 56 }); |
| 34 } | 57 } |
| 35 } | 58 } |
| 36 """; | 59 """; |
| 60 |
| 61 /// The code for a lazy version of [REWRITE_TRANSFORMER]. |
| 37 const LAZY_TRANSFORMER = """ | 62 const LAZY_TRANSFORMER = """ |
| 38 import 'dart:async'; | 63 import 'dart:async'; |
| 39 | 64 |
| 40 import 'package:barback/barback.dart'; | 65 import 'package:barback/barback.dart'; |
| 41 | 66 |
| 42 class LazyRewriteTransformer extends Transformer implements LazyTransformer { | 67 class LazyRewriteTransformer extends Transformer implements LazyTransformer { |
| 43 LazyRewriteTransformer.asPlugin(); | 68 LazyRewriteTransformer.asPlugin(); |
| 44 | 69 |
| 45 String get allowedExtensions => '.txt'; | 70 String get allowedExtensions => '.txt'; |
| 46 | 71 |
| 47 Future apply(Transform transform) { | 72 Future apply(Transform transform) { |
| 48 transform.logger.info('Rewriting \${transform.primaryInput.id}.'); | 73 transform.logger.info('Rewriting \${transform.primaryInput.id}.'); |
| 49 return transform.primaryInput.readAsString().then((contents) { | 74 return transform.primaryInput.readAsString().then((contents) { |
| 50 var id = transform.primaryInput.id.changeExtension(".out"); | 75 var id = transform.primaryInput.id.changeExtension(".out"); |
| 51 transform.addOutput(new Asset.fromString(id, "\$contents.out")); | 76 transform.addOutput(new Asset.fromString(id, "\$contents.out")); |
| 52 }); | 77 }); |
| 53 } | 78 } |
| 54 | 79 |
| 55 Future declareOutputs(DeclaringTransform transform) { | 80 Future declareOutputs(DeclaringTransform transform) { |
| 56 transform.declareOutput(transform.primaryId.changeExtension(".out")); | 81 transform.declareOutput(transform.primaryId.changeExtension(".out")); |
| 57 return new Future.value(); | 82 return new Future.value(); |
| 58 } | 83 } |
| 59 } | 84 } |
| 60 """; | 85 """; |
| 86 |
| 87 /// The web socket error code for a directory not being served. |
| 61 const NOT_SERVED = 1; | 88 const NOT_SERVED = 1; |
| 89 |
| 90 /// Returns the source code for a Dart library defining a Transformer that |
| 91 /// rewrites Dart files. |
| 92 /// |
| 93 /// The transformer defines a constant named TOKEN whose value is [id]. When the |
| 94 /// transformer transforms another Dart file, it will look for a "TOKEN" |
| 95 /// constant definition there and modify it to include *this* transformer's |
| 96 /// TOKEN value as well. |
| 97 /// |
| 98 /// If [import] is passed, it should be the name of a package that defines its |
| 99 /// own TOKEN constant. The primary library of that package will be imported |
| 100 /// here and its TOKEN value will be added to this library's. |
| 101 /// |
| 102 /// This transformer takes one configuration field: "addition". This is |
| 103 /// concatenated to its TOKEN value before adding it to the output library. |
| 62 String dartTransformer(String id, {String import}) { | 104 String dartTransformer(String id, {String import}) { |
| 63 if (import != null) { | 105 if (import != null) { |
| 64 id = '$id imports \${$import.TOKEN}'; | 106 id = '$id imports \${$import.TOKEN}'; |
| 65 import = 'import "package:$import/$import.dart" as $import;'; | 107 import = 'import "package:$import/$import.dart" as $import;'; |
| 66 } else { | 108 } else { |
| 67 import = ''; | 109 import = ''; |
| 68 } | 110 } |
| 111 |
| 69 return """ | 112 return """ |
| 70 import 'dart:async'; | 113 import 'dart:async'; |
| 71 | 114 |
| 72 import 'package:barback/barback.dart'; | 115 import 'package:barback/barback.dart'; |
| 73 $import | 116 $import |
| 74 | 117 |
| 75 import 'dart:io'; | 118 import 'dart:io'; |
| 76 | 119 |
| 77 const TOKEN = "$id"; | 120 const TOKEN = "$id"; |
| 78 | 121 |
| (...skipping 13 matching lines...) Expand all Loading... |
| 92 var token = TOKEN; | 135 var token = TOKEN; |
| 93 var addition = _settings.configuration["addition"]; | 136 var addition = _settings.configuration["addition"]; |
| 94 if (addition != null) token += addition; | 137 if (addition != null) token += addition; |
| 95 return 'const TOKEN = "(\${match[1]}, \$token)";'; | 138 return 'const TOKEN = "(\${match[1]}, \$token)";'; |
| 96 }))); | 139 }))); |
| 97 }); | 140 }); |
| 98 } | 141 } |
| 99 } | 142 } |
| 100 """; | 143 """; |
| 101 } | 144 } |
| 145 |
| 146 /// Schedules starting the `pub serve` process. |
| 147 /// |
| 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. |
| 150 /// |
| 151 /// Returns the `pub serve` process. |
| 102 ScheduledProcess startPubServe({Iterable<String> args, bool createWebDir: true}) | 152 ScheduledProcess startPubServe({Iterable<String> args, bool createWebDir: true}) |
| 103 { | 153 { |
| 104 var pubArgs = ["serve", "--port=0", "--force-poll", "--log-admin-url"]; | 154 var pubArgs = ["serve", "--port=0", // Use port 0 to get an ephemeral port. |
| 155 "--force-poll", "--log-admin-url"]; |
| 156 |
| 105 if (args != null) pubArgs.addAll(args); | 157 if (args != null) pubArgs.addAll(args); |
| 158 |
| 159 // Dart2js can take a long time to compile dart code, so we increase the |
| 160 // timeout to cope with that. |
| 106 currentSchedule.timeout *= 1.5; | 161 currentSchedule.timeout *= 1.5; |
| 162 |
| 107 if (createWebDir) d.dir(appPath, [d.dir("web")]).create(); | 163 if (createWebDir) d.dir(appPath, [d.dir("web")]).create(); |
| 108 return startPub(args: pubArgs); | 164 return startPub(args: pubArgs); |
| 109 } | 165 } |
| 166 |
| 167 /// Schedules starting the "pub serve" process and records its port number for |
| 168 /// future requests. |
| 169 /// |
| 170 /// If [shouldGetFirst] is `true`, validates that pub get is run first. |
| 171 /// |
| 172 /// If [createWebDir] is `true`, creates a `web/` directory if one doesn't exist |
| 173 /// so pub doesn't complain about having nothing to serve. |
| 174 /// |
| 175 /// Returns the `pub serve` process. |
| 110 ScheduledProcess pubServe({bool shouldGetFirst: false, bool createWebDir: true, | 176 ScheduledProcess pubServe({bool shouldGetFirst: false, bool createWebDir: true, |
| 111 Iterable<String> args}) { | 177 Iterable<String> args}) { |
| 112 _pubServer = startPubServe(args: args, createWebDir: createWebDir); | 178 _pubServer = startPubServe(args: args, createWebDir: createWebDir); |
| 113 _portsCompleter = new Completer(); | 179 _portsCompleter = new Completer(); |
| 180 |
| 114 currentSchedule.onComplete.schedule(() { | 181 currentSchedule.onComplete.schedule(() { |
| 115 _portsCompleter = null; | 182 _portsCompleter = null; |
| 116 _ports.clear(); | 183 _ports.clear(); |
| 184 |
| 117 if (_webSocket != null) { | 185 if (_webSocket != null) { |
| 118 _webSocket.close(); | 186 _webSocket.close(); |
| 119 _webSocket = null; | 187 _webSocket = null; |
| 120 _webSocketBroadcastStream = null; | 188 _webSocketBroadcastStream = null; |
| 121 } | 189 } |
| 122 }); | 190 }); |
| 191 |
| 123 if (shouldGetFirst) { | 192 if (shouldGetFirst) { |
| 124 _pubServer.stdout.expect( | 193 _pubServer.stdout.expect( |
| 125 consumeThrough( | 194 consumeThrough( |
| 126 anyOf(["Got dependencies!", matches(new RegExp(r"^Changed \d+ depend
enc"))]))); | 195 anyOf(["Got dependencies!", matches(new RegExp(r"^Changed \d+ depend
enc"))]))); |
| 127 } | 196 } |
| 197 |
| 128 _pubServer.stdout.expect(startsWith("Loading source assets...")); | 198 _pubServer.stdout.expect(startsWith("Loading source assets...")); |
| 129 _pubServer.stdout.expect(consumeWhile(matches("Loading .* transformers..."))); | 199 _pubServer.stdout.expect(consumeWhile(matches("Loading .* transformers..."))); |
| 200 |
| 130 _pubServer.stdout.expect(predicate(_parseAdminPort)); | 201 _pubServer.stdout.expect(predicate(_parseAdminPort)); |
| 202 |
| 203 // The server should emit one or more ports. |
| 131 _pubServer.stdout.expect( | 204 _pubServer.stdout.expect( |
| 132 consumeWhile(predicate(_parsePort, 'emits server url'))); | 205 consumeWhile(predicate(_parsePort, 'emits server url'))); |
| 133 schedule(() { | 206 schedule(() { |
| 134 expect(_ports, isNot(isEmpty)); | 207 expect(_ports, isNot(isEmpty)); |
| 135 _portsCompleter.complete(); | 208 _portsCompleter.complete(); |
| 136 }); | 209 }); |
| 210 |
| 137 return _pubServer; | 211 return _pubServer; |
| 138 } | 212 } |
| 213 |
| 214 /// The regular expression for parsing pub's output line describing the URL for |
| 215 /// the server. |
| 139 final _parsePortRegExp = new RegExp(r"([^ ]+) +on http://localhost:(\d+)"); | 216 final _parsePortRegExp = new RegExp(r"([^ ]+) +on http://localhost:(\d+)"); |
| 217 |
| 218 /// Parses the port number from the "Running admin server on localhost:1234" |
| 219 /// line printed by pub serve. |
| 140 bool _parseAdminPort(String line) { | 220 bool _parseAdminPort(String line) { |
| 141 var match = _parsePortRegExp.firstMatch(line); | 221 var match = _parsePortRegExp.firstMatch(line); |
| 142 if (match == null) return false; | 222 if (match == null) return false; |
| 143 _adminPort = int.parse(match[2]); | 223 _adminPort = int.parse(match[2]); |
| 144 return true; | 224 return true; |
| 145 } | 225 } |
| 226 |
| 227 /// Parses the port number from the "Serving blah on localhost:1234" line |
| 228 /// printed by pub serve. |
| 146 bool _parsePort(String line) { | 229 bool _parsePort(String line) { |
| 147 var match = _parsePortRegExp.firstMatch(line); | 230 var match = _parsePortRegExp.firstMatch(line); |
| 148 if (match == null) return false; | 231 if (match == null) return false; |
| 149 _ports[match[1]] = int.parse(match[2]); | 232 _ports[match[1]] = int.parse(match[2]); |
| 150 return true; | 233 return true; |
| 151 } | 234 } |
| 235 |
| 152 void endPubServe() { | 236 void endPubServe() { |
| 153 _pubServer.kill(); | 237 _pubServer.kill(); |
| 154 } | 238 } |
| 239 |
| 240 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 241 /// invokes [callback] with the response. |
| 242 /// |
| 243 /// [root] indicates which server should be accessed, and defaults to "web". |
| 155 Future<http.Response> scheduleRequest(String urlPath, {String root}) { | 244 Future<http.Response> scheduleRequest(String urlPath, {String root}) { |
| 156 return schedule(() { | 245 return schedule(() { |
| 157 return http.get(_getServerUrlSync(root, urlPath)); | 246 return http.get(_getServerUrlSync(root, urlPath)); |
| 158 }, "request $urlPath"); | 247 }, "request $urlPath"); |
| 159 } | 248 } |
| 249 |
| 250 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 251 /// verifies that it responds with a body that matches [expectation]. |
| 252 /// |
| 253 /// [expectation] may either be a [Matcher] or a string to match an exact body. |
| 254 /// [root] indicates which server should be accessed, and defaults to "web". |
| 255 /// [headers] may be either a [Matcher] or a map to match an exact headers map. |
| 160 void requestShouldSucceed(String urlPath, expectation, {String root, headers}) { | 256 void requestShouldSucceed(String urlPath, expectation, {String root, headers}) { |
| 161 scheduleRequest(urlPath, root: root).then((response) { | 257 scheduleRequest(urlPath, root: root).then((response) { |
| 162 if (expectation != null) expect(response.body, expectation); | 258 if (expectation != null) expect(response.body, expectation); |
| 163 if (headers != null) expect(response.headers, headers); | 259 if (headers != null) expect(response.headers, headers); |
| 164 }); | 260 }); |
| 165 } | 261 } |
| 262 |
| 263 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 264 /// verifies that it responds with a 404. |
| 265 /// |
| 266 /// [root] indicates which server should be accessed, and defaults to "web". |
| 166 void requestShould404(String urlPath, {String root}) { | 267 void requestShould404(String urlPath, {String root}) { |
| 167 scheduleRequest(urlPath, root: root).then((response) { | 268 scheduleRequest(urlPath, root: root).then((response) { |
| 168 expect(response.statusCode, equals(404)); | 269 expect(response.statusCode, equals(404)); |
| 169 }); | 270 }); |
| 170 } | 271 } |
| 272 |
| 273 /// Schedules an HTTP request to the running pub server with [urlPath] and |
| 274 /// verifies that it responds with a redirect to the given [redirectTarget]. |
| 275 /// |
| 276 /// [redirectTarget] may be either a [Matcher] or a string to match an exact |
| 277 /// URL. [root] indicates which server should be accessed, and defaults to |
| 278 /// "web". |
| 171 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) { | 279 void requestShouldRedirect(String urlPath, redirectTarget, {String root}) { |
| 172 schedule(() { | 280 schedule(() { |
| 173 var request = | 281 var request = |
| 174 new http.Request("GET", Uri.parse(_getServerUrlSync(root, urlPath))); | 282 new http.Request("GET", Uri.parse(_getServerUrlSync(root, urlPath))); |
| 175 request.followRedirects = false; | 283 request.followRedirects = false; |
| 176 return request.send().then((response) { | 284 return request.send().then((response) { |
| 177 expect(response.statusCode ~/ 100, equals(3)); | 285 expect(response.statusCode ~/ 100, equals(3)); |
| 178 expect(response.headers, containsPair('location', redirectTarget)); | 286 expect(response.headers, containsPair('location', redirectTarget)); |
| 179 }); | 287 }); |
| 180 }, "request $urlPath"); | 288 }, "request $urlPath"); |
| 181 } | 289 } |
| 290 |
| 291 /// Schedules an HTTP POST to the running pub server with [urlPath] and verifies |
| 292 /// that it responds with a 405. |
| 293 /// |
| 294 /// [root] indicates which server should be accessed, and defaults to "web". |
| 182 void postShould405(String urlPath, {String root}) { | 295 void postShould405(String urlPath, {String root}) { |
| 183 schedule(() { | 296 schedule(() { |
| 184 return http.post(_getServerUrlSync(root, urlPath)).then((response) { | 297 return http.post(_getServerUrlSync(root, urlPath)).then((response) { |
| 185 expect(response.statusCode, equals(405)); | 298 expect(response.statusCode, equals(405)); |
| 186 }); | 299 }); |
| 187 }, "request $urlPath"); | 300 }, "request $urlPath"); |
| 188 } | 301 } |
| 302 |
| 303 /// Schedules an HTTP request to the (theoretically) running pub server with |
| 304 /// [urlPath] and verifies that it cannot be connected to. |
| 305 /// |
| 306 /// [root] indicates which server should be accessed, and defaults to "web". |
| 189 void requestShouldNotConnect(String urlPath, {String root}) { | 307 void requestShouldNotConnect(String urlPath, {String root}) { |
| 190 schedule(() { | 308 schedule(() { |
| 191 return expect( | 309 return expect( |
| 192 http.get(_getServerUrlSync(root, urlPath)), | 310 http.get(_getServerUrlSync(root, urlPath)), |
| 193 throwsA(new isInstanceOf<SocketException>())); | 311 throwsA(new isInstanceOf<SocketException>())); |
| 194 }, "request $urlPath"); | 312 }, "request $urlPath"); |
| 195 } | 313 } |
| 314 |
| 315 /// Reads lines from pub serve's stdout until it prints the build success |
| 316 /// message. |
| 317 /// |
| 318 /// The schedule will not proceed until the output is found. If not found, it |
| 319 /// will eventually time out. |
| 196 void waitForBuildSuccess() => | 320 void waitForBuildSuccess() => |
| 197 _pubServer.stdout.expect(consumeThrough(contains("successfully"))); | 321 _pubServer.stdout.expect(consumeThrough(contains("successfully"))); |
| 322 |
| 323 /// Schedules opening a web socket connection to the currently running pub |
| 324 /// serve. |
| 198 Future _ensureWebSocket() { | 325 Future _ensureWebSocket() { |
| 326 // Use the existing one if already connected. |
| 199 if (_webSocket != null) return new Future.value(); | 327 if (_webSocket != null) return new Future.value(); |
| 328 |
| 329 // Server should already be running. |
| 200 expect(_pubServer, isNotNull); | 330 expect(_pubServer, isNotNull); |
| 201 expect(_adminPort, isNotNull); | 331 expect(_adminPort, isNotNull); |
| 332 |
| 202 return WebSocket.connect("ws://localhost:$_adminPort").then((socket) { | 333 return WebSocket.connect("ws://localhost:$_adminPort").then((socket) { |
| 203 _webSocket = socket; | 334 _webSocket = socket; |
| 335 // TODO(rnystrom): Works around #13913. |
| 204 _webSocketBroadcastStream = _webSocket.map(JSON.decode).asBroadcastStream(); | 336 _webSocketBroadcastStream = _webSocket.map(JSON.decode).asBroadcastStream(); |
| 205 }); | 337 }); |
| 206 } | 338 } |
| 339 |
| 340 /// Schedules closing the web socket connection to the currently-running pub |
| 341 /// serve. |
| 207 void closeWebSocket() { | 342 void closeWebSocket() { |
| 208 schedule(() { | 343 schedule(() { |
| 209 return _ensureWebSocket().then((_) => _webSocket.close()).then((_) => _webSo
cket = | 344 return _ensureWebSocket().then((_) => _webSocket.close()).then((_) => _webSo
cket = |
| 210 null); | 345 null); |
| 211 }, "closing web socket"); | 346 }, "closing web socket"); |
| 212 } | 347 } |
| 348 |
| 349 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket |
| 350 /// connection. |
| 351 /// |
| 352 /// This calls a method named [method] with the given [params] (or no |
| 353 /// parameters, if it's not passed). [params] may contain Futures, in which case |
| 354 /// this will wait until they've completed before sending the request. |
| 355 /// |
| 356 /// This schedules the request, but doesn't block the schedule on the response. |
| 357 /// It returns the response as a [Future]. |
| 213 Future<Map> webSocketRequest(String method, [Map params]) { | 358 Future<Map> webSocketRequest(String method, [Map params]) { |
| 214 var completer = new Completer(); | 359 var completer = new Completer(); |
| 215 schedule(() { | 360 schedule(() { |
| 216 return Future.wait( | 361 return Future.wait( |
| 217 [_ensureWebSocket(), awaitObject(params)]).then((results) { | 362 [_ensureWebSocket(), awaitObject(params),]).then((results) { |
| 218 var resolvedParams = results[1]; | 363 var resolvedParams = results[1]; |
| 219 chainToCompleter( | 364 chainToCompleter( |
| 220 currentSchedule.wrapFuture(_jsonRpcRequest(method, resolvedParams)), | 365 currentSchedule.wrapFuture(_jsonRpcRequest(method, resolvedParams)), |
| 221 completer); | 366 completer); |
| 222 }); | 367 }); |
| 223 }, "send $method with $params to web socket"); | 368 }, "send $method with $params to web socket"); |
| 224 return completer.future; | 369 return completer.future; |
| 225 } | 370 } |
| 371 |
| 372 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket |
| 373 /// connection, waits for a reply, then verifies the result. |
| 374 /// |
| 375 /// This calls a method named [method] with the given [params]. [params] may |
| 376 /// contain Futures, in which case this will wait until they've completed before |
| 377 /// sending the request. |
| 378 /// |
| 379 /// The result is validated using [result], which may be a [Matcher] or a [Map] |
| 380 /// containing [Matcher]s and [Future]s. This will wait until any futures are |
| 381 /// completed before sending the request. |
| 382 /// |
| 383 /// Returns a [Future] that completes to the call's result. |
| 226 Future<Map> expectWebSocketResult(String method, Map params, result) { | 384 Future<Map> expectWebSocketResult(String method, Map params, result) { |
| 227 return schedule(() { | 385 return schedule(() { |
| 228 return Future.wait( | 386 return Future.wait( |
| 229 [webSocketRequest(method, params), awaitObject(result)]).then((results)
{ | 387 [webSocketRequest(method, params), awaitObject(result)]).then((results)
{ |
| 230 var response = results[0]; | 388 var response = results[0]; |
| 231 var resolvedResult = results[1]; | 389 var resolvedResult = results[1]; |
| 232 expect(response["result"], resolvedResult); | 390 expect(response["result"], resolvedResult); |
| 233 return response["result"]; | 391 return response["result"]; |
| 234 }); | 392 }); |
| 235 }, "send $method with $params to web socket and expect $result"); | 393 }, "send $method with $params to web socket and expect $result"); |
| 236 } | 394 } |
| 395 |
| 396 /// Sends a JSON RPC 2.0 request to the running pub serve's web socket |
| 397 /// connection, waits for a reply, then verifies the error response. |
| 398 /// |
| 399 /// This calls a method named [method] with the given [params]. [params] may |
| 400 /// contain Futures, in which case this will wait until they've completed before |
| 401 /// sending the request. |
| 402 /// |
| 403 /// The error response is validated using [errorCode] and [errorMessage]. Both |
| 404 /// of these must be provided. The error code is checked against [errorCode] and |
| 405 /// the error message is checked against [errorMessage]. Either of these may be |
| 406 /// matchers. |
| 407 /// |
| 408 /// If [data] is provided, it is a JSON value or matcher used to validate the |
| 409 /// "data" value of the error response. |
| 410 /// |
| 411 /// Returns a [Future] that completes to the error's [data] field. |
| 237 Future expectWebSocketError(String method, Map params, errorCode, errorMessage, | 412 Future expectWebSocketError(String method, Map params, errorCode, errorMessage, |
| 238 {data}) { | 413 {data}) { |
| 239 return schedule(() { | 414 return schedule(() { |
| 240 return webSocketRequest(method, params).then((response) { | 415 return webSocketRequest(method, params).then((response) { |
| 241 expect(response["error"]["code"], errorCode); | 416 expect(response["error"]["code"], errorCode); |
| 242 expect(response["error"]["message"], errorMessage); | 417 expect(response["error"]["message"], errorMessage); |
| 418 |
| 243 if (data != null) { | 419 if (data != null) { |
| 244 expect(response["error"]["data"], data); | 420 expect(response["error"]["data"], data); |
| 245 } | 421 } |
| 422 |
| 246 return response["error"]["data"]; | 423 return response["error"]["data"]; |
| 247 }); | 424 }); |
| 248 }, "send $method with $params to web socket and expect error $errorCode"); | 425 }, "send $method with $params to web socket and expect error $errorCode"); |
| 249 } | 426 } |
| 427 |
| 428 /// Validates that [root] was not bound to a port when pub serve started. |
| 250 Future expectNotServed(String root) { | 429 Future expectNotServed(String root) { |
| 251 return schedule(() { | 430 return schedule(() { |
| 252 expect(_ports.containsKey(root), isFalse); | 431 expect(_ports.containsKey(root), isFalse); |
| 253 }); | 432 }); |
| 254 } | 433 } |
| 434 |
| 435 /// The next id to use for a JSON-RPC 2.0 request. |
| 255 var _rpcId = 0; | 436 var _rpcId = 0; |
| 437 |
| 438 /// Sends a JSON-RPC 2.0 request calling [method] with [params]. |
| 439 /// |
| 440 /// Returns the response object. |
| 256 Future<Map> _jsonRpcRequest(String method, [Map params]) { | 441 Future<Map> _jsonRpcRequest(String method, [Map params]) { |
| 257 var id = _rpcId++; | 442 var id = _rpcId++; |
| 258 var message = { | 443 var message = { |
| 259 "jsonrpc": "2.0", | 444 "jsonrpc": "2.0", |
| 260 "method": method, | 445 "method": method, |
| 261 "id": id | 446 "id": id |
| 262 }; | 447 }; |
| 263 if (params != null) message["params"] = params; | 448 if (params != null) message["params"] = params; |
| 264 _webSocket.add(JSON.encode(message)); | 449 _webSocket.add(JSON.encode(message)); |
| 450 |
| 265 return _webSocketBroadcastStream.firstWhere( | 451 return _webSocketBroadcastStream.firstWhere( |
| 266 (response) => response["id"] == id).then((value) { | 452 (response) => response["id"] == id).then((value) { |
| 267 currentSchedule.addDebugInfo( | 453 currentSchedule.addDebugInfo( |
| 268 "Web Socket request $method with params $params\n" "Result: $value"); | 454 "Web Socket request $method with params $params\n" "Result: $value"); |
| 455 |
| 269 expect(value["id"], equals(id)); | 456 expect(value["id"], equals(id)); |
| 270 return value; | 457 return value; |
| 271 }); | 458 }); |
| 272 } | 459 } |
| 460 |
| 461 /// Returns a [Future] that completes to a URL string for the server serving |
| 462 /// [path] from [root]. |
| 463 /// |
| 464 /// If [root] is omitted, defaults to "web". If [path] is omitted, no path is |
| 465 /// included. The Future will complete once the server is up and running and |
| 466 /// the bound ports are known. |
| 273 Future<String> getServerUrl([String root, String path]) => | 467 Future<String> getServerUrl([String root, String path]) => |
| 274 _portsCompleter.future.then((_) => _getServerUrlSync(root, path)); | 468 _portsCompleter.future.then((_) => _getServerUrlSync(root, path)); |
| 469 |
| 470 /// Records that [root] has been bound to [port]. |
| 471 /// |
| 472 /// Used for testing the Web Socket API for binding new root directories to |
| 473 /// ports after pub serve has been started. |
| 275 registerServerPort(String root, int port) { | 474 registerServerPort(String root, int port) { |
| 276 _ports[root] = port; | 475 _ports[root] = port; |
| 277 } | 476 } |
| 477 |
| 478 /// Returns a URL string for the server serving [path] from [root]. |
| 479 /// |
| 480 /// If [root] is omitted, defaults to "web". If [path] is omitted, no path is |
| 481 /// included. Unlike [getServerUrl], this should only be called after the ports |
| 482 /// are known. |
| 278 String _getServerUrlSync([String root, String path]) { | 483 String _getServerUrlSync([String root, String path]) { |
| 279 if (root == null) root = 'web'; | 484 if (root == null) root = 'web'; |
| 280 expect(_ports, contains(root)); | 485 expect(_ports, contains(root)); |
| 281 var url = "http://localhost:${_ports[root]}"; | 486 var url = "http://localhost:${_ports[root]}"; |
| 282 if (path != null) url = "$url/$path"; | 487 if (path != null) url = "$url/$path"; |
| 283 return url; | 488 return url; |
| 284 } | 489 } |
| 490 |
| OLD | NEW |