Chromium Code Reviews| 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. Unlike typical unit tests, most pub | 5 /// Test infrastructure for testing pub. Unlike typical unit tests, most pub |
| 6 /// tests are integration tests that stage some stuff on the file system, run | 6 /// tests are integration tests that stage some stuff on the file system, run |
| 7 /// pub, and then validate the results. This library provides an API to build | 7 /// pub, and then validate the results. This library provides an API to build |
| 8 /// tests like that. | 8 /// tests like that. |
| 9 library test_pub; | 9 library test_pub; |
| 10 | 10 |
| 11 import 'dart:async'; | 11 import 'dart:async'; |
| 12 import 'dart:convert'; | 12 import 'dart:convert'; |
| 13 import 'dart:io'; | 13 import 'dart:io'; |
| 14 import 'dart:math'; | 14 import 'dart:math'; |
| 15 | 15 |
| 16 import 'package:http/testing.dart'; | 16 import 'package:http/testing.dart'; |
| 17 import 'package:path/path.dart' as path; | 17 import 'package:path/path.dart' as path; |
| 18 import 'package:scheduled_test/scheduled_process.dart'; | 18 import 'package:scheduled_test/scheduled_process.dart'; |
| 19 import 'package:scheduled_test/scheduled_server.dart'; | 19 import 'package:scheduled_test/scheduled_server.dart'; |
| 20 import 'package:scheduled_test/scheduled_stream.dart'; | |
| 20 import 'package:scheduled_test/scheduled_test.dart'; | 21 import 'package:scheduled_test/scheduled_test.dart'; |
| 21 import 'package:unittest/compact_vm_config.dart'; | 22 import 'package:unittest/compact_vm_config.dart'; |
| 22 import 'package:yaml/yaml.dart'; | 23 import 'package:yaml/yaml.dart'; |
| 23 | 24 |
| 24 import '../lib/src/entrypoint.dart'; | 25 import '../lib/src/entrypoint.dart'; |
| 25 import '../lib/src/exit_codes.dart' as exit_codes; | 26 import '../lib/src/exit_codes.dart' as exit_codes; |
| 26 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides | 27 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides |
| 27 // with the git descriptor method. Maybe we should try to clean up the top level | 28 // with the git descriptor method. Maybe we should try to clean up the top level |
| 28 // scope a bit? | 29 // scope a bit? |
| 29 import '../lib/src/git.dart' as gitlib; | 30 import '../lib/src/git.dart' as gitlib; |
| (...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 387 // Cannot pass both output and outputJson. | 388 // Cannot pass both output and outputJson. |
| 388 assert(output == null || outputJson == null); | 389 assert(output == null || outputJson == null); |
| 389 | 390 |
| 390 var pub = startPub(args: args, tokenEndpoint: tokenEndpoint); | 391 var pub = startPub(args: args, tokenEndpoint: tokenEndpoint); |
| 391 pub.shouldExit(exitCode); | 392 pub.shouldExit(exitCode); |
| 392 | 393 |
| 393 var failures = []; | 394 var failures = []; |
| 394 var stderr; | 395 var stderr; |
| 395 | 396 |
| 396 expect(Future.wait([ | 397 expect(Future.wait([ |
| 397 pub.remainingStdout(), | 398 pub.stdoutStream().toList(), |
| 398 pub.remainingStderr() | 399 pub.stderrStream().toList() |
| 399 ]).then((results) { | 400 ]).then((results) { |
| 400 stderr = results[1]; | 401 var stdout = results[0].join("\n"); |
| 402 stderr = results[1].join("\n"); | |
| 401 | 403 |
| 402 if (outputJson == null) { | 404 if (outputJson == null) { |
| 403 _validateOutput(failures, 'stdout', output, results[0]); | 405 _validateOutput(failures, 'stdout', output, stdout); |
| 404 return null; | 406 return null; |
| 405 } | 407 } |
| 406 | 408 |
| 407 // Allow the expected JSON to contain futures. | 409 // Allow the expected JSON to contain futures. |
| 408 return awaitObject(outputJson).then((resolved) { | 410 return awaitObject(outputJson).then((resolved) { |
| 409 _validateOutputJson(failures, 'stdout', resolved, results[0]); | 411 _validateOutputJson(failures, 'stdout', resolved, stdout); |
| 410 }); | 412 }); |
| 411 }).then((_) { | 413 }).then((_) { |
| 412 _validateOutput(failures, 'stderr', error, stderr); | 414 _validateOutput(failures, 'stderr', error, stderr); |
| 413 | 415 |
| 414 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); | 416 if (!failures.isEmpty) throw new TestFailure(failures.join('\n')); |
| 415 }), completes); | 417 }), completes); |
| 416 } | 418 } |
| 417 | 419 |
| 418 /// Like [startPub], but runs `pub lish` in particular with [server] used both | 420 /// Like [startPub], but runs `pub lish` in particular with [server] used both |
| 419 /// as the OAuth2 server (with "/token" as the token endpoint) and as the | 421 /// as the OAuth2 server (with "/token" as the token endpoint) and as the |
| 420 /// package server. | 422 /// package server. |
| 421 /// | 423 /// |
| 422 /// Any futures in [args] will be resolved before the process is started. | 424 /// Any futures in [args] will be resolved before the process is started. |
| 423 ScheduledProcess startPublish(ScheduledServer server, {List args}) { | 425 ScheduledProcess startPublish(ScheduledServer server, {List args}) { |
| 424 var tokenEndpoint = server.url.then((url) => | 426 var tokenEndpoint = server.url.then((url) => |
| 425 url.resolve('/token').toString()); | 427 url.resolve('/token').toString()); |
| 426 if (args == null) args = []; | 428 if (args == null) args = []; |
| 427 args = flatten(['lish', '--server', tokenEndpoint, args]); | 429 args = flatten(['lish', '--server', tokenEndpoint, args]); |
| 428 return startPub(args: args, tokenEndpoint: tokenEndpoint); | 430 return startPub(args: args, tokenEndpoint: tokenEndpoint); |
| 429 } | 431 } |
| 430 | 432 |
| 431 /// Handles the beginning confirmation process for uploading a packages. | 433 /// Handles the beginning confirmation process for uploading a packages. |
| 432 /// Ensures that the right output is shown and then enters "y" to confirm the | 434 /// Ensures that the right output is shown and then enters "y" to confirm the |
| 433 /// upload. | 435 /// upload. |
| 434 void confirmPublish(ScheduledProcess pub) { | 436 void confirmPublish(ScheduledProcess pub) { |
| 435 // TODO(rnystrom): This is overly specific and inflexible regarding different | 437 // TODO(rnystrom): This is overly specific and inflexible regarding different |
| 436 // test packages. Should validate this a little more loosely. | 438 // test packages. Should validate this a little more loosely. |
| 437 expect(pub.nextLine(), completion(startsWith( | 439 pub.stdout.expect(startsWith('Publishing test_pkg 1.0.0 to ')); |
| 438 'Publishing test_pkg 1.0.0 to '))); | 440 pub.stdout.expect(emitsString( |
| 439 expect(pub.nextLine(), completion(equals("|-- LICENSE"))); | 441 "|-- LICENSE\n" |
| 440 expect(pub.nextLine(), completion(equals("|-- lib"))); | 442 "|-- lib\n" |
| 441 expect(pub.nextLine(), completion(equals("| '-- test_pkg.dart"))); | 443 "| '-- test_pkg.dart\n" |
| 442 expect(pub.nextLine(), completion(equals("'-- pubspec.yaml"))); | 444 "'-- pubspec.yaml\n" |
| 443 expect(pub.nextLine(), completion(equals(""))); | 445 "\n" |
| 444 expect(pub.nextLine(), completion(equals('Looks great! Are you ready to ' | 446 "Looks great! Are you ready to upload your package (y/n)?")); |
|
Bob Nystrom
2014/02/14 17:55:02
Multi-line string?
nweiz
2014/02/18 22:01:10
Same response as before -- why here and not elsewh
Bob Nystrom
2014/02/18 22:12:59
Same response as before. It's already six lines of
| |
| 445 'upload your package (y/n)?'))); | |
| 446 | |
| 447 pub.writeLine("y"); | 447 pub.writeLine("y"); |
| 448 } | 448 } |
| 449 | 449 |
| 450 /// Starts a Pub process and returns a [ScheduledProcess] that supports | 450 /// Starts a Pub process and returns a [ScheduledProcess] that supports |
| 451 /// interaction with that process. | 451 /// interaction with that process. |
| 452 /// | 452 /// |
| 453 /// Any futures in [args] will be resolved before the process is started. | 453 /// Any futures in [args] will be resolved before the process is started. |
| 454 ScheduledProcess startPub({List args, Future<Uri> tokenEndpoint}) { | 454 ScheduledProcess startPub({List args, Future<Uri> tokenEndpoint}) { |
| 455 String pathInSandbox(String relPath) { | 455 String pathInSandbox(String relPath) { |
| 456 return path.join(path.absolute(sandboxDir), relPath); | 456 return path.join(path.absolute(sandboxDir), relPath); |
| (...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 501 return environment; | 501 return environment; |
| 502 }); | 502 }); |
| 503 | 503 |
| 504 return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, | 504 return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, |
| 505 workingDirectory: pathInSandbox(appPath), | 505 workingDirectory: pathInSandbox(appPath), |
| 506 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 506 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); |
| 507 } | 507 } |
| 508 | 508 |
| 509 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 509 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output |
| 510 /// and makes [nextLine], [nextErrLine], [remainingStdout], and | 510 /// and makes [nextLine], [nextErrLine], [remainingStdout], and |
| 511 /// [remainingStderr] work as though pub weren't running in verbose mode. | 511 /// [remainingStderr] work as though pub weren't running in verbose mode. |
|
Bob Nystrom
2014/02/14 17:55:02
Update docs.
nweiz
2014/02/18 22:01:10
Done.
| |
| 512 class PubProcess extends ScheduledProcess { | 512 class PubProcess extends ScheduledProcess { |
| 513 Stream<Pair<log.Level, String>> _log; | 513 Stream<Pair<log.Level, String>> _log; |
| 514 Stream<String> _stdout; | 514 Stream<String> _stdout; |
| 515 Stream<String> _stderr; | 515 Stream<String> _stderr; |
| 516 | 516 |
| 517 PubProcess.start(executable, arguments, | 517 PubProcess.start(executable, arguments, |
| 518 {workingDirectory, environment, String description, | 518 {workingDirectory, environment, String description, |
| 519 Encoding encoding: UTF8}) | 519 Encoding encoding: UTF8}) |
| 520 : super.start(executable, arguments, | 520 : super.start(executable, arguments, |
| 521 workingDirectory: workingDirectory, | 521 workingDirectory: workingDirectory, |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 567 } | 567 } |
| 568 | 568 |
| 569 var pair = tee(_stdout); | 569 var pair = tee(_stdout); |
| 570 _stdout = pair.first; | 570 _stdout = pair.first; |
| 571 return pair.last; | 571 return pair.last; |
| 572 } | 572 } |
| 573 | 573 |
| 574 Stream<String> stderrStream() { | 574 Stream<String> stderrStream() { |
| 575 if (_stderr == null) { | 575 if (_stderr == null) { |
| 576 _stderr = _logStream().expand((entry) { | 576 _stderr = _logStream().expand((entry) { |
| 577 if (entry.first != log.Level.ERROR && entry.first != log.Level.WARNING) { | 577 if (entry.first != log.Level.ERROR && entry.first != log.Level.WARNING) { |
|
Bob Nystrom
2014/02/14 17:55:02
How'd this long line sneak in?
nweiz
2014/02/18 22:01:10
Fixed.
| |
| 578 return []; | 578 return []; |
| 579 } | 579 } |
| 580 return [entry.last]; | 580 return [entry.last]; |
| 581 }); | 581 }); |
| 582 } | 582 } |
| 583 | 583 |
| 584 var pair = tee(_stderr); | 584 var pair = tee(_stderr); |
| 585 _stderr = pair.first; | 585 _stderr = pair.first; |
| 586 return pair.last; | 586 return pair.last; |
| 587 } | 587 } |
| (...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 859 bool matches(item, Map matchState) { | 859 bool matches(item, Map matchState) { |
| 860 if (item is! Pair) return false; | 860 if (item is! Pair) return false; |
| 861 return _firstMatcher.matches(item.first, matchState) && | 861 return _firstMatcher.matches(item.first, matchState) && |
| 862 _lastMatcher.matches(item.last, matchState); | 862 _lastMatcher.matches(item.last, matchState); |
| 863 } | 863 } |
| 864 | 864 |
| 865 Description describe(Description description) { | 865 Description describe(Description description) { |
| 866 description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 866 description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
| 867 } | 867 } |
| 868 } | 868 } |
| 869 | |
| 870 /// A [StreamMatcher] that matches multiple lines of output. | |
| 871 StreamMatcher emitsString(String output) => inOrder(output.split("\n")); | |
|
Bob Nystrom
2014/02/14 17:55:02
How about "emitsLines"?
nweiz
2014/02/18 22:01:10
I considered that name, but it felt weird that it
Bob Nystrom
2014/02/18 22:12:59
Yeah, I think it does. Before I saw the doc commen
nweiz
2014/02/18 22:28:02
Done.
| |
| OLD | NEW |