OLD | NEW |
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS 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 /// Test infrastructure for testing pub. | 5 /// Test infrastructure for testing pub. |
6 /// | 6 /// |
7 /// Unlike typical unit tests, most pub tests are integration tests that stage | 7 /// Unlike typical unit tests, most pub tests are integration tests that stage |
8 /// some stuff on the file system, run pub, and then validate the results. This | 8 /// some stuff on the file system, run pub, and then validate the results. This |
9 /// library provides an API to build tests like that. | 9 /// library provides an API to build tests like that. |
10 library test_pub; | 10 library test_pub; |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
135 } | 135 } |
136 | 136 |
137 for (var version in validVersions) { | 137 for (var version in validVersions) { |
138 group("with barback $version", () { | 138 group("with barback $version", () { |
139 setUp(() { | 139 setUp(() { |
140 _packageOverrides = {}; | 140 _packageOverrides = {}; |
141 _packageOverrides['barback'] = _barbackVersions[version]; | 141 _packageOverrides['barback'] = _barbackVersions[version]; |
142 _barbackDeps.forEach((constraint, deps) { | 142 _barbackDeps.forEach((constraint, deps) { |
143 if (!constraint.allows(version)) return; | 143 if (!constraint.allows(version)) return; |
144 deps.forEach((packageName, version) { | 144 deps.forEach((packageName, version) { |
145 _packageOverrides[packageName] = p.join( | 145 _packageOverrides[packageName] = |
146 repoRoot, 'third_party', 'pkg', '$packageName-$version'); | 146 p.join(repoRoot, 'third_party', 'pkg', '$packageName-$version'); |
147 }); | 147 }); |
148 }); | 148 }); |
149 | 149 |
150 currentSchedule.onComplete.schedule(() { | 150 currentSchedule.onComplete.schedule(() { |
151 _packageOverrides = null; | 151 _packageOverrides = null; |
152 }); | 152 }); |
153 }); | 153 }); |
154 | 154 |
155 callback(); | 155 callback(); |
156 }); | 156 }); |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
189 | 189 |
190 _hasServer = true; | 190 _hasServer = true; |
191 | 191 |
192 schedule(() { | 192 schedule(() { |
193 return _closeServer().then((_) { | 193 return _closeServer().then((_) { |
194 return shelf_io.serve((request) { | 194 return shelf_io.serve((request) { |
195 currentSchedule.heartbeat(); | 195 currentSchedule.heartbeat(); |
196 var path = p.posix.fromUri(request.url.path.replaceFirst("/", "")); | 196 var path = p.posix.fromUri(request.url.path.replaceFirst("/", "")); |
197 _requestedPaths.add(path); | 197 _requestedPaths.add(path); |
198 | 198 |
199 return validateStream(baseDir.load(path)) | 199 return validateStream( |
200 .then((stream) => new shelf.Response.ok(stream)) | 200 baseDir.load( |
201 .catchError((error) { | 201 path)).then((stream) => new shelf.Response.ok(stream)).catchErro
r((error) { |
202 return new shelf.Response.notFound('File "$path" not found.'); | 202 return new shelf.Response.notFound('File "$path" not found.'); |
203 }); | 203 }); |
204 }, 'localhost', 0).then((server) { | 204 }, 'localhost', 0).then((server) { |
205 _server = server; | 205 _server = server; |
206 _portCompleter.complete(_server.port); | 206 _portCompleter.complete(_server.port); |
207 currentSchedule.onComplete.schedule(_closeServer); | 207 currentSchedule.onComplete.schedule(_closeServer); |
208 }); | 208 }); |
209 }); | 209 }); |
210 }, 'starting a server serving:\n${baseDir.describe()}'); | 210 }, 'starting a server serving:\n${baseDir.describe()}'); |
211 } | 211 } |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
243 /// The path of the packages directory in the mock app used for tests, relative | 243 /// The path of the packages directory in the mock app used for tests, relative |
244 /// to the sandbox directory. | 244 /// to the sandbox directory. |
245 final String packagesPath = "$appPath/packages"; | 245 final String packagesPath = "$appPath/packages"; |
246 | 246 |
247 /// Set to true when the current batch of scheduled events should be aborted. | 247 /// Set to true when the current batch of scheduled events should be aborted. |
248 bool _abortScheduled = false; | 248 bool _abortScheduled = false; |
249 | 249 |
250 /// Enum identifying a pub command that can be run with a well-defined success | 250 /// Enum identifying a pub command that can be run with a well-defined success |
251 /// output. | 251 /// output. |
252 class RunCommand { | 252 class RunCommand { |
253 static final get = new RunCommand('get', new RegExp( | 253 static final get = new RunCommand( |
254 r'Got dependencies!|Changed \d+ dependenc(y|ies)!')); | 254 'get', |
255 static final upgrade = new RunCommand('upgrade', new RegExp( | 255 new RegExp(r'Got dependencies!|Changed \d+ dependenc(y|ies)!')); |
256 r'(No dependencies changed\.|Changed \d+ dependenc(y|ies)!)$')); | 256 static final upgrade = new RunCommand( |
257 static final downgrade = new RunCommand('downgrade', new RegExp( | 257 'upgrade', |
258 r'(No dependencies changed\.|Changed \d+ dependenc(y|ies)!)$')); | 258 new RegExp(r'(No dependencies changed\.|Changed \d+ dependenc(y|ies)!)$'))
; |
| 259 static final downgrade = new RunCommand( |
| 260 'downgrade', |
| 261 new RegExp(r'(No dependencies changed\.|Changed \d+ dependenc(y|ies)!)$'))
; |
259 | 262 |
260 final String name; | 263 final String name; |
261 final RegExp success; | 264 final RegExp success; |
262 RunCommand(this.name, this.success); | 265 RunCommand(this.name, this.success); |
263 } | 266 } |
264 | 267 |
265 /// Runs the tests defined within [callback] using both pub get and pub upgrade. | 268 /// Runs the tests defined within [callback] using both pub get and pub upgrade. |
266 /// | 269 /// |
267 /// Many tests validate behavior that is the same between pub get and | 270 /// Many tests validate behavior that is the same between pub get and |
268 /// upgrade have the same behavior. Instead of duplicating those tests, this | 271 /// upgrade have the same behavior. Instead of duplicating those tests, this |
269 /// takes a callback that defines get/upgrade agnostic tests and runs them | 272 /// takes a callback that defines get/upgrade agnostic tests and runs them |
270 /// with both commands. | 273 /// with both commands. |
271 void forBothPubGetAndUpgrade(void callback(RunCommand command)) { | 274 void forBothPubGetAndUpgrade(void callback(RunCommand command)) { |
272 group(RunCommand.get.name, () => callback(RunCommand.get)); | 275 group(RunCommand.get.name, () => callback(RunCommand.get)); |
273 group(RunCommand.upgrade.name, () => callback(RunCommand.upgrade)); | 276 group(RunCommand.upgrade.name, () => callback(RunCommand.upgrade)); |
274 } | 277 } |
275 | 278 |
276 /// Schedules an invocation of pub [command] and validates that it completes | 279 /// Schedules an invocation of pub [command] and validates that it completes |
277 /// in an expected way. | 280 /// in an expected way. |
278 /// | 281 /// |
279 /// By default, this validates that the command completes successfully and | 282 /// By default, this validates that the command completes successfully and |
280 /// understands the normal output of a successful pub command. If [warning] is | 283 /// understands the normal output of a successful pub command. If [warning] is |
281 /// given, it expects the command to complete successfully *and* print | 284 /// given, it expects the command to complete successfully *and* print |
282 /// [warning] to stderr. If [error] is given, it expects the command to *only* | 285 /// [warning] to stderr. If [error] is given, it expects the command to *only* |
283 /// print [error] to stderr. [output], [error], and [warning] may be strings, | 286 /// print [error] to stderr. [output], [error], and [warning] may be strings, |
284 /// [RegExp]s, or [Matcher]s. | 287 /// [RegExp]s, or [Matcher]s. |
285 /// | 288 /// |
286 /// If [exitCode] is given, expects the command to exit with that code. | 289 /// If [exitCode] is given, expects the command to exit with that code. |
287 // TODO(rnystrom): Clean up other tests to call this when possible. | 290 // TODO(rnystrom): Clean up other tests to call this when possible. |
288 void pubCommand(RunCommand command, | 291 void pubCommand(RunCommand command, {Iterable<String> args, output, error, |
289 {Iterable<String> args, output, error, warning, int exitCode}) { | 292 warning, int exitCode}) { |
290 if (error != null && warning != null) { | 293 if (error != null && warning != null) { |
291 throw new ArgumentError("Cannot pass both 'error' and 'warning'."); | 294 throw new ArgumentError("Cannot pass both 'error' and 'warning'."); |
292 } | 295 } |
293 | 296 |
294 var allArgs = [command.name]; | 297 var allArgs = [command.name]; |
295 if (args != null) allArgs.addAll(args); | 298 if (args != null) allArgs.addAll(args); |
296 | 299 |
297 if (output == null) output = command.success; | 300 if (output == null) output = command.success; |
298 | 301 |
299 if (error != null && exitCode == null) exitCode = 1; | 302 if (error != null && exitCode == null) exitCode = 1; |
300 | 303 |
301 // No success output on an error. | 304 // No success output on an error. |
302 if (error != null) output = null; | 305 if (error != null) output = null; |
303 if (warning != null) error = warning; | 306 if (warning != null) error = warning; |
304 | 307 |
305 schedulePub(args: allArgs, output: output, error: error, exitCode: exitCode); | 308 schedulePub(args: allArgs, output: output, error: error, exitCode: exitCode); |
306 } | 309 } |
307 | 310 |
308 void pubGet({Iterable<String> args, output, error, warning, int exitCode}) { | 311 void pubGet({Iterable<String> args, output, error, warning, int exitCode}) { |
309 pubCommand(RunCommand.get, args: args, output: output, error: error, | 312 pubCommand( |
310 warning: warning, exitCode: exitCode); | 313 RunCommand.get, |
| 314 args: args, |
| 315 output: output, |
| 316 error: error, |
| 317 warning: warning, |
| 318 exitCode: exitCode); |
311 } | 319 } |
312 | 320 |
313 void pubUpgrade({Iterable<String> args, output, error, warning, int exitCode}) { | 321 void pubUpgrade({Iterable<String> args, output, error, warning, int exitCode}) { |
314 pubCommand(RunCommand.upgrade, args: args, output: output, error: error, | 322 pubCommand( |
315 warning: warning, exitCode: exitCode); | 323 RunCommand.upgrade, |
| 324 args: args, |
| 325 output: output, |
| 326 error: error, |
| 327 warning: warning, |
| 328 exitCode: exitCode); |
316 } | 329 } |
317 | 330 |
318 void pubDowngrade({Iterable<String> args, output, error, warning, | 331 void pubDowngrade({Iterable<String> args, output, error, warning, int exitCode}) |
319 int exitCode}) { | 332 { |
320 pubCommand(RunCommand.downgrade, args: args, output: output, error: error, | 333 pubCommand( |
321 warning: warning, exitCode: exitCode); | 334 RunCommand.downgrade, |
| 335 args: args, |
| 336 output: output, |
| 337 error: error, |
| 338 warning: warning, |
| 339 exitCode: exitCode); |
322 } | 340 } |
323 | 341 |
324 /// Schedules starting the "pub [global] run" process and validates the | 342 /// Schedules starting the "pub [global] run" process and validates the |
325 /// expected startup output. | 343 /// expected startup output. |
326 /// | 344 /// |
327 /// If [global] is `true`, this invokes "pub global run", otherwise it does | 345 /// If [global] is `true`, this invokes "pub global run", otherwise it does |
328 /// "pub run". | 346 /// "pub run". |
329 /// | 347 /// |
330 /// Returns the `pub run` process. | 348 /// Returns the `pub run` process. |
331 ScheduledProcess pubRun({bool global: false, Iterable<String> args}) { | 349 ScheduledProcess pubRun({bool global: false, Iterable<String> args}) { |
332 var pubArgs = global ? ["global", "run"] : ["run"]; | 350 var pubArgs = global ? ["global", "run"] : ["run"]; |
333 pubArgs.addAll(args); | 351 pubArgs.addAll(args); |
334 var pub = startPub(args: pubArgs); | 352 var pub = startPub(args: pubArgs); |
335 | 353 |
336 // Loading sources and transformers isn't normally printed, but the pub test | 354 // Loading sources and transformers isn't normally printed, but the pub test |
337 // infrastructure runs pub in verbose mode, which enables this. | 355 // infrastructure runs pub in verbose mode, which enables this. |
338 pub.stdout.expect(consumeWhile(startsWith("Loading"))); | 356 pub.stdout.expect(consumeWhile(startsWith("Loading"))); |
339 | 357 |
340 return pub; | 358 return pub; |
341 } | 359 } |
342 | 360 |
343 /// Defines an integration test. | 361 /// Defines an integration test. |
344 /// | 362 /// |
345 /// The [body] should schedule a series of operations which will be run | 363 /// The [body] should schedule a series of operations which will be run |
346 /// asynchronously. | 364 /// asynchronously. |
347 void integration(String description, void body()) => | 365 void integration(String description, void body()) => |
348 _integration(description, body, test); | 366 _integration(description, body, test); |
349 | 367 |
350 /// Like [integration], but causes only this test to run. | 368 /// Like [integration], but causes only this test to run. |
351 void solo_integration(String description, void body()) => | 369 void solo_integration(String description, void body()) => |
352 _integration(description, body, solo_test); | 370 _integration(description, body, solo_test); |
353 | 371 |
354 void _integration(String description, void body(), [Function testFn]) { | 372 void _integration(String description, void body(), [Function testFn]) { |
355 testFn(description, () { | 373 testFn(description, () { |
356 // TODO(nweiz): remove this when issue 15362 is fixed. | 374 // TODO(nweiz): remove this when issue 15362 is fixed. |
357 currentSchedule.timeout *= 2; | 375 currentSchedule.timeout *= 2; |
358 | 376 |
359 // The windows bots are very slow, so we increase the default timeout. | 377 // The windows bots are very slow, so we increase the default timeout. |
360 if (Platform.operatingSystem == "windows") { | 378 if (Platform.operatingSystem == "windows") { |
361 currentSchedule.timeout *= 2; | 379 currentSchedule.timeout *= 2; |
362 } | 380 } |
363 | 381 |
364 _sandboxDir = createSystemTempDir(); | 382 _sandboxDir = createSystemTempDir(); |
365 d.defaultRoot = sandboxDir; | 383 d.defaultRoot = sandboxDir; |
366 currentSchedule.onComplete.schedule(() => deleteEntry(_sandboxDir), | 384 currentSchedule.onComplete.schedule( |
| 385 () => deleteEntry(_sandboxDir), |
367 'deleting the sandbox directory'); | 386 'deleting the sandbox directory'); |
368 | 387 |
369 // Schedule the test. | 388 // Schedule the test. |
370 body(); | 389 body(); |
371 }); | 390 }); |
372 } | 391 } |
373 | 392 |
374 /// Get the path to the root "pub/test" directory containing the pub | 393 /// Get the path to the root "pub/test" directory containing the pub |
375 /// tests. | 394 /// tests. |
376 String get testDirectory => | 395 String get testDirectory => p.absolute(p.dirname(libraryPath('test_pub'))); |
377 p.absolute(p.dirname(libraryPath('test_pub'))); | |
378 | 396 |
379 /// Schedules renaming (moving) the directory at [from] to [to], both of which | 397 /// Schedules renaming (moving) the directory at [from] to [to], both of which |
380 /// are assumed to be relative to [sandboxDir]. | 398 /// are assumed to be relative to [sandboxDir]. |
381 void scheduleRename(String from, String to) { | 399 void scheduleRename(String from, String to) { |
382 schedule( | 400 schedule( |
383 () => renameDir( | 401 () => renameDir(p.join(sandboxDir, from), p.join(sandboxDir, to)), |
384 p.join(sandboxDir, from), | |
385 p.join(sandboxDir, to)), | |
386 'renaming $from to $to'); | 402 'renaming $from to $to'); |
387 } | 403 } |
388 | 404 |
389 /// Schedules creating a symlink at path [symlink] that points to [target], | 405 /// Schedules creating a symlink at path [symlink] that points to [target], |
390 /// both of which are assumed to be relative to [sandboxDir]. | 406 /// both of which are assumed to be relative to [sandboxDir]. |
391 void scheduleSymlink(String target, String symlink) { | 407 void scheduleSymlink(String target, String symlink) { |
392 schedule( | 408 schedule( |
393 () => createSymlink( | 409 () => createSymlink(p.join(sandboxDir, target), p.join(sandboxDir, symlink
)), |
394 p.join(sandboxDir, target), | |
395 p.join(sandboxDir, symlink)), | |
396 'symlinking $target to $symlink'); | 410 'symlinking $target to $symlink'); |
397 } | 411 } |
398 | 412 |
399 /// Schedules a call to the Pub command-line utility. | 413 /// Schedules a call to the Pub command-line utility. |
400 /// | 414 /// |
401 /// Runs Pub with [args] and validates that its results match [output] (or | 415 /// Runs Pub with [args] and validates that its results match [output] (or |
402 /// [outputJson]), [error], and [exitCode]. | 416 /// [outputJson]), [error], and [exitCode]. |
403 /// | 417 /// |
404 /// [output] and [error] can be [String]s, [RegExp]s, or [Matcher]s. | 418 /// [output] and [error] can be [String]s, [RegExp]s, or [Matcher]s. |
405 /// | 419 /// |
406 /// If [outputJson] is given, validates that pub outputs stringified JSON | 420 /// If [outputJson] is given, validates that pub outputs stringified JSON |
407 /// matching that object, which can be a literal JSON object or any other | 421 /// matching that object, which can be a literal JSON object or any other |
408 /// [Matcher]. | 422 /// [Matcher]. |
409 /// | 423 /// |
410 /// If [environment] is given, any keys in it will override the environment | 424 /// If [environment] is given, any keys in it will override the environment |
411 /// variables passed to the spawned process. | 425 /// variables passed to the spawned process. |
412 void schedulePub({List args, output, error, outputJson, | 426 void schedulePub({List args, output, error, outputJson, int exitCode: |
413 int exitCode: exit_codes.SUCCESS, Map<String, String> environment}) { | 427 exit_codes.SUCCESS, Map<String, String> environment}) { |
414 // Cannot pass both output and outputJson. | 428 // Cannot pass both output and outputJson. |
415 assert(output == null || outputJson == null); | 429 assert(output == null || outputJson == null); |
416 | 430 |
417 var pub = startPub(args: args, environment: environment); | 431 var pub = startPub(args: args, environment: environment); |
418 pub.shouldExit(exitCode); | 432 pub.shouldExit(exitCode); |
419 | 433 |
420 var failures = []; | 434 var failures = []; |
421 var stderr; | 435 var stderr; |
422 | 436 |
423 expect(Future.wait([ | 437 expect( |
424 pub.stdoutStream().toList(), | 438 Future.wait( |
425 pub.stderrStream().toList() | 439 [pub.stdoutStream().toList(), pub.stderrStream().toList()]).then((resu
lts) { |
426 ]).then((results) { | |
427 var stdout = results[0].join("\n"); | 440 var stdout = results[0].join("\n"); |
428 stderr = results[1].join("\n"); | 441 stderr = results[1].join("\n"); |
429 | 442 |
430 if (outputJson == null) { | 443 if (outputJson == null) { |
431 _validateOutput(failures, 'stdout', output, stdout); | 444 _validateOutput(failures, 'stdout', output, stdout); |
432 return null; | 445 return null; |
433 } | 446 } |
434 | 447 |
435 // Allow the expected JSON to contain futures. | 448 // Allow the expected JSON to contain futures. |
436 return awaitObject(outputJson).then((resolved) { | 449 return awaitObject(outputJson).then((resolved) { |
437 _validateOutputJson(failures, 'stdout', resolved, stdout); | 450 _validateOutputJson(failures, 'stdout', resolved, stdout); |
438 }); | 451 }); |
439 }).then((_) { | 452 }).then((_) { |
440 _validateOutput(failures, 'stderr', error, stderr); | 453 _validateOutput(failures, 'stderr', error, stderr); |
441 | 454 |
442 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); | 455 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); |
443 }), completes); | 456 }), completes); |
444 } | 457 } |
445 | 458 |
446 /// Like [startPub], but runs `pub lish` in particular with [server] used both | 459 /// Like [startPub], but runs `pub lish` in particular with [server] used both |
447 /// as the OAuth2 server (with "/token" as the token endpoint) and as the | 460 /// as the OAuth2 server (with "/token" as the token endpoint) and as the |
448 /// package server. | 461 /// package server. |
449 /// | 462 /// |
450 /// Any futures in [args] will be resolved before the process is started. | 463 /// Any futures in [args] will be resolved before the process is started. |
451 ScheduledProcess startPublish(ScheduledServer server, {List args}) { | 464 ScheduledProcess startPublish(ScheduledServer server, {List args}) { |
452 var tokenEndpoint = server.url.then((url) => | 465 var tokenEndpoint = |
453 url.resolve('/token').toString()); | 466 server.url.then((url) => url.resolve('/token').toString()); |
454 if (args == null) args = []; | 467 if (args == null) args = []; |
455 args = flatten(['lish', '--server', tokenEndpoint, args]); | 468 args = flatten(['lish', '--server', tokenEndpoint, args]); |
456 return startPub(args: args, tokenEndpoint: tokenEndpoint); | 469 return startPub(args: args, tokenEndpoint: tokenEndpoint); |
457 } | 470 } |
458 | 471 |
459 /// Handles the beginning confirmation process for uploading a packages. | 472 /// Handles the beginning confirmation process for uploading a packages. |
460 /// | 473 /// |
461 /// Ensures that the right output is shown and then enters "y" to confirm the | 474 /// Ensures that the right output is shown and then enters "y" to confirm the |
462 /// upload. | 475 /// upload. |
463 void confirmPublish(ScheduledProcess pub) { | 476 void confirmPublish(ScheduledProcess pub) { |
464 // TODO(rnystrom): This is overly specific and inflexible regarding different | 477 // TODO(rnystrom): This is overly specific and inflexible regarding different |
465 // test packages. Should validate this a little more loosely. | 478 // test packages. Should validate this a little more loosely. |
466 pub.stdout.expect(startsWith('Publishing test_pkg 1.0.0 to ')); | 479 pub.stdout.expect(startsWith('Publishing test_pkg 1.0.0 to ')); |
467 pub.stdout.expect(emitsLines( | 480 pub.stdout.expect( |
468 "|-- LICENSE\n" | 481 emitsLines( |
469 "|-- lib\n" | 482 "|-- LICENSE\n" "|-- lib\n" "| '-- test_pkg.dart\n" "'-- pubspec.yam
l\n" "\n" |
470 "| '-- test_pkg.dart\n" | 483 "Looks great! Are you ready to upload your package (y/n)?")); |
471 "'-- pubspec.yaml\n" | |
472 "\n" | |
473 "Looks great! Are you ready to upload your package (y/n)?")); | |
474 pub.writeLine("y"); | 484 pub.writeLine("y"); |
475 } | 485 } |
476 | 486 |
477 /// Gets the absolute path to [relPath], which is a relative path in the test | 487 /// Gets the absolute path to [relPath], which is a relative path in the test |
478 /// sandbox. | 488 /// sandbox. |
479 String _pathInSandbox(String relPath) { | 489 String _pathInSandbox(String relPath) { |
480 return p.join(p.absolute(sandboxDir), relPath); | 490 return p.join(p.absolute(sandboxDir), relPath); |
481 } | 491 } |
482 | 492 |
483 /// Gets the environment variables used to run pub in a test context. | 493 /// Gets the environment variables used to run pub in a test context. |
484 Future<Map> getPubTestEnvironment([String tokenEndpoint]) async { | 494 Future<Map> getPubTestEnvironment([String tokenEndpoint]) { |
485 var environment = {}; | 495 final completer0 = new Completer(); |
486 environment['_PUB_TESTING'] = 'true'; | 496 scheduleMicrotask(() { |
487 environment['PUB_CACHE'] = _pathInSandbox(cachePath); | 497 try { |
488 | 498 var environment = {}; |
489 // Ensure a known SDK version is set for the tests that rely on that. | 499 environment['_PUB_TESTING'] = 'true'; |
490 environment['_PUB_TEST_SDK_VERSION'] = "0.1.2+3"; | 500 environment['PUB_CACHE'] = _pathInSandbox(cachePath); |
491 | 501 environment['_PUB_TEST_SDK_VERSION'] = "0.1.2+3"; |
492 if (tokenEndpoint != null) { | 502 join0() { |
493 environment['_PUB_TEST_TOKEN_ENDPOINT'] = tokenEndpoint.toString(); | 503 join1() { |
494 } | 504 completer0.complete(environment); |
495 | 505 } |
496 if (_hasServer) { | 506 if (_hasServer) { |
497 return port.then((p) { | 507 completer0.complete(port.then(((p) { |
498 environment['PUB_HOSTED_URL'] = "http://localhost:$p"; | 508 environment['PUB_HOSTED_URL'] = "http://localhost:$p"; |
499 return environment; | 509 return environment; |
500 }); | 510 }))); |
501 } | 511 } else { |
502 | 512 join1(); |
503 return environment; | 513 } |
| 514 } |
| 515 if (tokenEndpoint != null) { |
| 516 environment['_PUB_TEST_TOKEN_ENDPOINT'] = tokenEndpoint.toString(); |
| 517 join0(); |
| 518 } else { |
| 519 join0(); |
| 520 } |
| 521 } catch (e, s) { |
| 522 completer0.completeError(e, s); |
| 523 } |
| 524 }); |
| 525 return completer0.future; |
504 } | 526 } |
505 | 527 |
506 /// Starts a Pub process and returns a [ScheduledProcess] that supports | 528 /// Starts a Pub process and returns a [ScheduledProcess] that supports |
507 /// interaction with that process. | 529 /// interaction with that process. |
508 /// | 530 /// |
509 /// Any futures in [args] will be resolved before the process is started. | 531 /// Any futures in [args] will be resolved before the process is started. |
510 /// | 532 /// |
511 /// If [environment] is given, any keys in it will override the environment | 533 /// If [environment] is given, any keys in it will override the environment |
512 /// variables passed to the spawned process. | 534 /// variables passed to the spawned process. |
513 ScheduledProcess startPub({List args, Future<String> tokenEndpoint, | 535 ScheduledProcess startPub({List args, Future<String> tokenEndpoint, Map<String, |
514 Map<String, String> environment}) { | 536 String> environment}) { |
515 ensureDir(_pathInSandbox(appPath)); | 537 ensureDir(_pathInSandbox(appPath)); |
516 | 538 |
517 // Find a Dart executable we can use to spawn. Use the same one that was | 539 // Find a Dart executable we can use to spawn. Use the same one that was |
518 // used to run this script itself. | 540 // used to run this script itself. |
519 var dartBin = Platform.executable; | 541 var dartBin = Platform.executable; |
520 | 542 |
521 // If the executable looks like a path, get its full path. That way we | 543 // If the executable looks like a path, get its full path. That way we |
522 // can still find it when we spawn it with a different working directory. | 544 // can still find it when we spawn it with a different working directory. |
523 if (dartBin.contains(Platform.pathSeparator)) { | 545 if (dartBin.contains(Platform.pathSeparator)) { |
524 dartBin = p.absolute(dartBin); | 546 dartBin = p.absolute(dartBin); |
525 } | 547 } |
526 | 548 |
527 // Always run pub from a snapshot. Since we require the SDK to be built, the | 549 // Always run pub from a snapshot. Since we require the SDK to be built, the |
528 // snapshot should be there. Note that this *does* mean that the snapshot has | 550 // snapshot should be there. Note that this *does* mean that the snapshot has |
529 // to be manually updated when changing code before running the tests. | 551 // to be manually updated when changing code before running the tests. |
530 // Otherwise, you will test against stale data. | 552 // Otherwise, you will test against stale data. |
531 // | 553 // |
532 // Using the snapshot makes running the tests much faster, which is why we | 554 // Using the snapshot makes running the tests much faster, which is why we |
533 // make this trade-off. | 555 // make this trade-off. |
534 var pubPath = p.join(p.dirname(dartBin), 'snapshots/pub.dart.snapshot'); | 556 var pubPath = p.join(p.dirname(dartBin), 'snapshots/pub.dart.snapshot'); |
535 var dartArgs = [pubPath, '--verbose']; | 557 var dartArgs = [pubPath, '--verbose']; |
536 dartArgs.addAll(args); | 558 dartArgs.addAll(args); |
537 | 559 |
538 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); | 560 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); |
539 var environmentFuture = tokenEndpoint | 561 var environmentFuture = tokenEndpoint.then( |
540 .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)) | 562 (tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)).then((pubEnvironm
ent) { |
541 .then((pubEnvironment) { | |
542 if (environment != null) pubEnvironment.addAll(environment); | 563 if (environment != null) pubEnvironment.addAll(environment); |
543 return pubEnvironment; | 564 return pubEnvironment; |
544 }); | 565 }); |
545 | 566 |
546 return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, | 567 return new PubProcess.start( |
| 568 dartBin, |
| 569 dartArgs, |
| 570 environment: environmentFuture, |
547 workingDirectory: _pathInSandbox(appPath), | 571 workingDirectory: _pathInSandbox(appPath), |
548 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 572 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); |
549 } | 573 } |
550 | 574 |
551 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 575 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output |
552 /// and makes [stdout] and [stderr] work as though pub weren't running in | 576 /// and makes [stdout] and [stderr] work as though pub weren't running in |
553 /// verbose mode. | 577 /// verbose mode. |
554 class PubProcess extends ScheduledProcess { | 578 class PubProcess extends ScheduledProcess { |
555 Stream<Pair<log.Level, String>> _log; | 579 Stream<Pair<log.Level, String>> _log; |
556 Stream<String> _stdout; | 580 Stream<String> _stdout; |
557 Stream<String> _stderr; | 581 Stream<String> _stderr; |
558 | 582 |
559 PubProcess.start(executable, arguments, | 583 PubProcess.start(executable, arguments, {workingDirectory, environment, |
560 {workingDirectory, environment, String description, | 584 String description, Encoding encoding: UTF8}) |
561 Encoding encoding: UTF8}) | 585 : super.start( |
562 : super.start(executable, arguments, | 586 executable, |
563 workingDirectory: workingDirectory, | 587 arguments, |
564 environment: environment, | 588 workingDirectory: workingDirectory, |
565 description: description, | 589 environment: environment, |
566 encoding: encoding); | 590 description: description, |
| 591 encoding: encoding); |
567 | 592 |
568 Stream<Pair<log.Level, String>> _logStream() { | 593 Stream<Pair<log.Level, String>> _logStream() { |
569 if (_log == null) { | 594 if (_log == null) { |
570 _log = mergeStreams( | 595 _log = mergeStreams( |
571 _outputToLog(super.stdoutStream(), log.Level.MESSAGE), | 596 _outputToLog(super.stdoutStream(), log.Level.MESSAGE), |
572 _outputToLog(super.stderrStream(), log.Level.ERROR)); | 597 _outputToLog(super.stderrStream(), log.Level.ERROR)); |
573 } | 598 } |
574 | 599 |
575 var pair = tee(_log); | 600 var pair = tee(_log); |
576 _log = pair.first; | 601 _log = pair.first; |
577 return pair.last; | 602 return pair.last; |
578 } | 603 } |
579 | 604 |
580 final _logLineRegExp = new RegExp(r"^([A-Z ]{4})[:|] (.*)$"); | 605 final _logLineRegExp = new RegExp(r"^([A-Z ]{4})[:|] (.*)$"); |
581 final _logLevels = [ | 606 final _logLevels = [ |
582 log.Level.ERROR, log.Level.WARNING, log.Level.MESSAGE, log.Level.IO, | 607 log.Level.ERROR, |
583 log.Level.SOLVER, log.Level.FINE | 608 log.Level.WARNING, |
584 ].fold(<String, log.Level>{}, (levels, level) { | 609 log.Level.MESSAGE, |
| 610 log.Level.IO, |
| 611 log.Level.SOLVER, |
| 612 log.Level.FINE].fold(<String, log.Level>{}, (levels, level) { |
585 levels[level.name] = level; | 613 levels[level.name] = level; |
586 return levels; | 614 return levels; |
587 }); | 615 }); |
588 | 616 |
589 Stream<Pair<log.Level, String>> _outputToLog(Stream<String> stream, | 617 Stream<Pair<log.Level, String>> _outputToLog(Stream<String> stream, |
590 log.Level defaultLevel) { | 618 log.Level defaultLevel) { |
591 var lastLevel; | 619 var lastLevel; |
592 return stream.map((line) { | 620 return stream.map((line) { |
593 var match = _logLineRegExp.firstMatch(line); | 621 var match = _logLineRegExp.firstMatch(line); |
594 if (match == null) return new Pair<log.Level, String>(defaultLevel, line); | 622 if (match == null) return new Pair<log.Level, String>(defaultLevel, line); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
651 } | 679 } |
652 } | 680 } |
653 | 681 |
654 /// Schedules activating a global package [package] without running | 682 /// Schedules activating a global package [package] without running |
655 /// "pub global activate". | 683 /// "pub global activate". |
656 /// | 684 /// |
657 /// This is useful because global packages must be hosted, but the test hosted | 685 /// This is useful because global packages must be hosted, but the test hosted |
658 /// server doesn't serve barback. The other parameters here follow | 686 /// server doesn't serve barback. The other parameters here follow |
659 /// [createLockFile]. | 687 /// [createLockFile]. |
660 void makeGlobalPackage(String package, String version, | 688 void makeGlobalPackage(String package, String version, |
661 Iterable<d.Descriptor> contents, {Iterable<String> pkg, | 689 Iterable<d.Descriptor> contents, {Iterable<String> pkg, Map<String, |
662 Map<String, String> hosted}) { | 690 String> hosted}) { |
663 // Start the server so we know what port to use in the cache directory name. | 691 // Start the server so we know what port to use in the cache directory name. |
664 serveNoPackages(); | 692 serveNoPackages(); |
665 | 693 |
666 // Create the package in the hosted cache. | 694 // Create the package in the hosted cache. |
667 d.hostedCache([ | 695 d.hostedCache([d.dir("$package-$version", contents)]).create(); |
668 d.dir("$package-$version", contents) | |
669 ]).create(); | |
670 | 696 |
671 var lockFile = _createLockFile(pkg: pkg, hosted: hosted); | 697 var lockFile = _createLockFile(pkg: pkg, hosted: hosted); |
672 | 698 |
673 // Add the root package to the lockfile. | 699 // Add the root package to the lockfile. |
674 var id = new PackageId(package, "hosted", new Version.parse(version), | 700 var id = |
675 package); | 701 new PackageId(package, "hosted", new Version.parse(version), package); |
676 lockFile.packages[package] = id; | 702 lockFile.packages[package] = id; |
677 | 703 |
678 // Write the lockfile to the global cache. | 704 // Write the lockfile to the global cache. |
679 var sources = new SourceRegistry(); | 705 var sources = new SourceRegistry(); |
680 sources.register(new HostedSource()); | 706 sources.register(new HostedSource()); |
681 sources.register(new PathSource()); | 707 sources.register(new PathSource()); |
682 | 708 |
683 d.dir(cachePath, [ | 709 d.dir( |
684 d.dir("global_packages", [ | 710 cachePath, |
685 d.file("$package.lock", lockFile.serialize(null, sources)) | 711 [ |
686 ]) | 712 d.dir( |
687 ]).create(); | 713 "global_packages", |
| 714 [d.file("$package.lock", lockFile.serialize(null, sources))])]).cr
eate(); |
688 } | 715 } |
689 | 716 |
690 /// Creates a lock file for [package] without running `pub get`. | 717 /// Creates a lock file for [package] without running `pub get`. |
691 /// | 718 /// |
692 /// [sandbox] is a list of path dependencies to be found in the sandbox | 719 /// [sandbox] is a list of path dependencies to be found in the sandbox |
693 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; | 720 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; |
694 /// each package listed here and all its dependencies will be linked to the | 721 /// each package listed here and all its dependencies will be linked to the |
695 /// version in the Dart repo. | 722 /// version in the Dart repo. |
696 /// | 723 /// |
697 /// [hosted] is a list of package names to version strings for dependencies on | 724 /// [hosted] is a list of package names to version strings for dependencies on |
698 /// hosted packages. | 725 /// hosted packages. |
699 void createLockFile(String package, {Iterable<String> sandbox, | 726 void createLockFile(String package, {Iterable<String> sandbox, |
700 Iterable<String> pkg, Map<String, String> hosted}) { | 727 Iterable<String> pkg, Map<String, String> hosted}) { |
701 var lockFile = _createLockFile(sandbox: sandbox, pkg: pkg, hosted: hosted); | 728 var lockFile = _createLockFile(sandbox: sandbox, pkg: pkg, hosted: hosted); |
702 | 729 |
703 var sources = new SourceRegistry(); | 730 var sources = new SourceRegistry(); |
704 sources.register(new HostedSource()); | 731 sources.register(new HostedSource()); |
705 sources.register(new PathSource()); | 732 sources.register(new PathSource()); |
706 | 733 |
707 d.file(p.join(package, 'pubspec.lock'), | 734 d.file( |
| 735 p.join(package, 'pubspec.lock'), |
708 lockFile.serialize(null, sources)).create(); | 736 lockFile.serialize(null, sources)).create(); |
709 } | 737 } |
710 | 738 |
711 /// Creates a lock file for [package] without running `pub get`. | 739 /// Creates a lock file for [package] without running `pub get`. |
712 /// | 740 /// |
713 /// [sandbox] is a list of path dependencies to be found in the sandbox | 741 /// [sandbox] is a list of path dependencies to be found in the sandbox |
714 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; | 742 /// directory. [pkg] is a list of packages in the Dart repo's "pkg" directory; |
715 /// each package listed here and all its dependencies will be linked to the | 743 /// each package listed here and all its dependencies will be linked to the |
716 /// version in the Dart repo. | 744 /// version in the Dart repo. |
717 /// | 745 /// |
718 /// [hosted] is a list of package names to version strings for dependencies on | 746 /// [hosted] is a list of package names to version strings for dependencies on |
719 /// hosted packages. | 747 /// hosted packages. |
720 LockFile _createLockFile({Iterable<String> sandbox, | 748 LockFile _createLockFile({Iterable<String> sandbox, Iterable<String> pkg, |
721 Iterable<String> pkg, Map<String, String> hosted}) { | 749 Map<String, String> hosted}) { |
722 var dependencies = {}; | 750 var dependencies = {}; |
723 | 751 |
724 if (sandbox != null) { | 752 if (sandbox != null) { |
725 for (var package in sandbox) { | 753 for (var package in sandbox) { |
726 dependencies[package] = '../$package'; | 754 dependencies[package] = '../$package'; |
727 } | 755 } |
728 } | 756 } |
729 | 757 |
730 if (pkg != null) { | 758 if (pkg != null) { |
731 _addPackage(String package) { | 759 _addPackage(String package) { |
732 if (dependencies.containsKey(package)) return; | 760 if (dependencies.containsKey(package)) return; |
733 | 761 |
734 var path; | 762 var path; |
735 if (package == 'barback' && _packageOverrides == null) { | 763 if (package == 'barback' && _packageOverrides == null) { |
736 throw new StateError("createLockFile() can only create a lock file " | 764 throw new StateError( |
737 "with a barback dependency within a withBarbackVersions() " | 765 "createLockFile() can only create a lock file " |
738 "block."); | 766 "with a barback dependency within a withBarbackVersions() " "blo
ck."); |
739 } | 767 } |
740 | 768 |
741 if (_packageOverrides.containsKey(package)) { | 769 if (_packageOverrides.containsKey(package)) { |
742 path = _packageOverrides[package]; | 770 path = _packageOverrides[package]; |
743 } else { | 771 } else { |
744 path = packagePath(package); | 772 path = packagePath(package); |
745 } | 773 } |
746 | 774 |
747 dependencies[package] = path; | 775 dependencies[package] = path; |
748 var pubspec = loadYaml( | 776 var pubspec = loadYaml(readTextFile(p.join(path, 'pubspec.yaml'))); |
749 readTextFile(p.join(path, 'pubspec.yaml'))); | |
750 var packageDeps = pubspec['dependencies']; | 777 var packageDeps = pubspec['dependencies']; |
751 if (packageDeps == null) return; | 778 if (packageDeps == null) return; |
752 packageDeps.keys.forEach(_addPackage); | 779 packageDeps.keys.forEach(_addPackage); |
753 } | 780 } |
754 | 781 |
755 pkg.forEach(_addPackage); | 782 pkg.forEach(_addPackage); |
756 } | 783 } |
757 | 784 |
758 var lockFile = new LockFile.empty(); | 785 var lockFile = new LockFile.empty(); |
759 dependencies.forEach((name, dependencyPath) { | 786 dependencies.forEach((name, dependencyPath) { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 "homepage": "http://pub.dartlang.org", | 829 "homepage": "http://pub.dartlang.org", |
803 "description": "A package, I guess." | 830 "description": "A package, I guess." |
804 }; | 831 }; |
805 | 832 |
806 if (dependencies != null) package["dependencies"] = dependencies; | 833 if (dependencies != null) package["dependencies"] = dependencies; |
807 | 834 |
808 return package; | 835 return package; |
809 } | 836 } |
810 | 837 |
811 /// Resolves [target] relative to the path to pub's `test/asset` directory. | 838 /// Resolves [target] relative to the path to pub's `test/asset` directory. |
812 String testAssetPath(String target) => | 839 String testAssetPath(String target) { |
813 p.join(p.dirname(libraryPath('test_pub')), 'asset', target); | 840 var libPath = libraryPath('test_pub'); |
| 841 |
| 842 // We are running from the generated directory, but non-dart assets are only |
| 843 // in the canonical directory. |
| 844 // TODO(rnystrom): Remove this when #104 is fixed. |
| 845 libPath = libPath.replaceAll('pub_generated', 'pub'); |
| 846 |
| 847 return p.join(p.dirname(libPath), 'asset', target); |
| 848 } |
814 | 849 |
815 /// Returns a Map in the format used by the pub.dartlang.org API to represent a | 850 /// Returns a Map in the format used by the pub.dartlang.org API to represent a |
816 /// package version. | 851 /// package version. |
817 /// | 852 /// |
818 /// [pubspec] is the parsed pubspec of the package version. If [full] is true, | 853 /// [pubspec] is the parsed pubspec of the package version. If [full] is true, |
819 /// this returns the complete map, including metadata that's only included when | 854 /// this returns the complete map, including metadata that's only included when |
820 /// requesting the package version directly. | 855 /// requesting the package version directly. |
821 Map packageVersionApiMap(Map pubspec, {bool full: false}) { | 856 Map packageVersionApiMap(Map pubspec, {bool full: false}) { |
822 var name = pubspec['name']; | 857 var name = pubspec['name']; |
823 var version = pubspec['version']; | 858 var version = pubspec['version']; |
824 var map = { | 859 var map = { |
825 'pubspec': pubspec, | 860 'pubspec': pubspec, |
826 'version': version, | 861 'version': version, |
827 'url': '/api/packages/$name/versions/$version', | 862 'url': '/api/packages/$name/versions/$version', |
828 'archive_url': '/packages/$name/versions/$version.tar.gz', | 863 'archive_url': '/packages/$name/versions/$version.tar.gz', |
829 'new_dartdoc_url': '/api/packages/$name/versions/$version' | 864 'new_dartdoc_url': '/api/packages/$name/versions/$version' '/new_dartdoc', |
830 '/new_dartdoc', | |
831 'package_url': '/api/packages/$name' | 865 'package_url': '/api/packages/$name' |
832 }; | 866 }; |
833 | 867 |
834 if (full) { | 868 if (full) { |
835 map.addAll({ | 869 map.addAll({ |
836 'downloads': 0, | 870 'downloads': 0, |
837 'created': '2012-09-25T18:38:28.685260', | 871 'created': '2012-09-25T18:38:28.685260', |
838 'libraries': ['$name.dart'], | 872 'libraries': ['$name.dart'], |
839 'uploader': ['nweiz@google.com'] | 873 'uploader': ['nweiz@google.com'] |
840 }); | 874 }); |
841 } | 875 } |
842 | 876 |
843 return map; | 877 return map; |
844 } | 878 } |
845 | 879 |
846 /// Returns the name of the shell script for a binstub named [name]. | 880 /// Returns the name of the shell script for a binstub named [name]. |
847 /// | 881 /// |
848 /// Adds a ".bat" extension on Windows. | 882 /// Adds a ".bat" extension on Windows. |
849 String binStubName(String name) => Platform.isWindows ? '$name.bat' : name; | 883 String binStubName(String name) => Platform.isWindows ? '$name.bat' : name; |
850 | 884 |
851 /// Compares the [actual] output from running pub with [expected]. | 885 /// Compares the [actual] output from running pub with [expected]. |
852 /// | 886 /// |
853 /// If [expected] is a [String], ignores leading and trailing whitespace | 887 /// If [expected] is a [String], ignores leading and trailing whitespace |
854 /// differences and tries to report the offending difference in a nice way. | 888 /// differences and tries to report the offending difference in a nice way. |
855 /// | 889 /// |
856 /// If it's a [RegExp] or [Matcher], just reports whether the output matches. | 890 /// If it's a [RegExp] or [Matcher], just reports whether the output matches. |
857 void _validateOutput(List<String> failures, String pipe, expected, | 891 void _validateOutput(List<String> failures, String pipe, expected, |
858 String actual) { | 892 String actual) { |
859 if (expected == null) return; | 893 if (expected == null) return; |
860 | 894 |
861 if (expected is String) { | 895 if (expected is String) { |
862 _validateOutputString(failures, pipe, expected, actual); | 896 _validateOutputString(failures, pipe, expected, actual); |
863 } else { | 897 } else { |
864 if (expected is RegExp) expected = matches(expected); | 898 if (expected is RegExp) expected = matches(expected); |
865 expect(actual, expected); | 899 expect(actual, expected); |
866 } | 900 } |
867 } | 901 } |
868 | 902 |
869 void _validateOutputString(List<String> failures, String pipe, | 903 void _validateOutputString(List<String> failures, String pipe, String expected, |
870 String expected, String actual) { | 904 String actual) { |
871 var actualLines = actual.split("\n"); | 905 var actualLines = actual.split("\n"); |
872 var expectedLines = expected.split("\n"); | 906 var expectedLines = expected.split("\n"); |
873 | 907 |
874 // Strip off the last line. This lets us have expected multiline strings | 908 // Strip off the last line. This lets us have expected multiline strings |
875 // where the closing ''' is on its own line. It also fixes '' expected output | 909 // where the closing ''' is on its own line. It also fixes '' expected output |
876 // to expect zero lines of output, not a single empty line. | 910 // to expect zero lines of output, not a single empty line. |
877 if (expectedLines.last.trim() == '') { | 911 if (expectedLines.last.trim() == '') { |
878 expectedLines.removeLast(); | 912 expectedLines.removeLast(); |
879 } | 913 } |
880 | 914 |
(...skipping 30 matching lines...) Expand all Loading... |
911 if (failed) { | 945 if (failed) { |
912 failures.add('Expected $pipe:'); | 946 failures.add('Expected $pipe:'); |
913 failures.addAll(expectedLines.map((line) => '| $line')); | 947 failures.addAll(expectedLines.map((line) => '| $line')); |
914 failures.add('Got:'); | 948 failures.add('Got:'); |
915 failures.addAll(results); | 949 failures.addAll(results); |
916 } | 950 } |
917 } | 951 } |
918 | 952 |
919 /// Validates that [actualText] is a string of JSON that matches [expected], | 953 /// Validates that [actualText] is a string of JSON that matches [expected], |
920 /// which may be a literal JSON object, or any other [Matcher]. | 954 /// which may be a literal JSON object, or any other [Matcher]. |
921 void _validateOutputJson(List<String> failures, String pipe, | 955 void _validateOutputJson(List<String> failures, String pipe, expected, |
922 expected, String actualText) { | 956 String actualText) { |
923 var actual; | 957 var actual; |
924 try { | 958 try { |
925 actual = JSON.decode(actualText); | 959 actual = JSON.decode(actualText); |
926 } on FormatException catch(error) { | 960 } on FormatException catch (error) { |
927 failures.add('Expected $pipe JSON:'); | 961 failures.add('Expected $pipe JSON:'); |
928 failures.add(expected); | 962 failures.add(expected); |
929 failures.add('Got invalid JSON:'); | 963 failures.add('Got invalid JSON:'); |
930 failures.add(actualText); | 964 failures.add(actualText); |
931 } | 965 } |
932 | 966 |
933 // Match against the expectation. | 967 // Match against the expectation. |
934 expect(actual, expected); | 968 expect(actual, expected); |
935 } | 969 } |
936 | 970 |
937 /// A function that creates a [Validator] subclass. | 971 /// A function that creates a [Validator] subclass. |
938 typedef Validator ValidatorCreator(Entrypoint entrypoint); | 972 typedef Validator ValidatorCreator(Entrypoint entrypoint); |
939 | 973 |
940 /// Schedules a single [Validator] to run on the [appPath]. | 974 /// Schedules a single [Validator] to run on the [appPath]. |
941 /// | 975 /// |
942 /// Returns a scheduled Future that contains the errors and warnings produced | 976 /// Returns a scheduled Future that contains the errors and warnings produced |
943 /// by that validator. | 977 /// by that validator. |
944 Future<Pair<List<String>, List<String>>> schedulePackageValidation( | 978 Future<Pair<List<String>, List<String>>> |
945 ValidatorCreator fn) { | 979 schedulePackageValidation(ValidatorCreator fn) { |
946 return schedule(() { | 980 return schedule(() { |
947 var cache = new SystemCache.withSources( | 981 var cache = |
948 rootDir: p.join(sandboxDir, cachePath)); | 982 new SystemCache.withSources(rootDir: p.join(sandboxDir, cachePath)); |
949 | 983 |
950 return new Future.sync(() { | 984 return new Future.sync(() { |
951 var validator = fn(new Entrypoint(p.join(sandboxDir, appPath), cache)); | 985 var validator = fn(new Entrypoint(p.join(sandboxDir, appPath), cache)); |
952 return validator.validate().then((_) { | 986 return validator.validate().then((_) { |
953 return new Pair(validator.errors, validator.warnings); | 987 return new Pair(validator.errors, validator.warnings); |
954 }); | 988 }); |
955 }); | 989 }); |
956 }, "validating package"); | 990 }, "validating package"); |
957 } | 991 } |
958 | 992 |
959 /// A matcher that matches a Pair. | 993 /// A matcher that matches a Pair. |
960 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => | 994 Matcher pairOf(Matcher firstMatcher, Matcher lastMatcher) => |
961 new _PairMatcher(firstMatcher, lastMatcher); | 995 new _PairMatcher(firstMatcher, lastMatcher); |
962 | 996 |
963 class _PairMatcher extends Matcher { | 997 class _PairMatcher extends Matcher { |
964 final Matcher _firstMatcher; | 998 final Matcher _firstMatcher; |
965 final Matcher _lastMatcher; | 999 final Matcher _lastMatcher; |
966 | 1000 |
967 _PairMatcher(this._firstMatcher, this._lastMatcher); | 1001 _PairMatcher(this._firstMatcher, this._lastMatcher); |
968 | 1002 |
969 bool matches(item, Map matchState) { | 1003 bool matches(item, Map matchState) { |
970 if (item is! Pair) return false; | 1004 if (item is! Pair) return false; |
971 return _firstMatcher.matches(item.first, matchState) && | 1005 return _firstMatcher.matches(item.first, matchState) && |
972 _lastMatcher.matches(item.last, matchState); | 1006 _lastMatcher.matches(item.last, matchState); |
973 } | 1007 } |
974 | 1008 |
975 Description describe(Description description) { | 1009 Description describe(Description description) { |
976 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 1010 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
977 } | 1011 } |
978 } | 1012 } |
979 | 1013 |
980 /// A [StreamMatcher] that matches multiple lines of output. | 1014 /// A [StreamMatcher] that matches multiple lines of output. |
981 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 1015 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); |
OLD | NEW |