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 514 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
525 // Find a Dart executable we can use to spawn. Use the same one that was | 525 // Find a Dart executable we can use to spawn. Use the same one that was |
526 // used to run this script itself. | 526 // used to run this script itself. |
527 var dartBin = Platform.executable; | 527 var dartBin = Platform.executable; |
528 | 528 |
529 // If the executable looks like a path, get its full path. That way we | 529 // If the executable looks like a path, get its full path. That way we |
530 // can still find it when we spawn it with a different working directory. | 530 // can still find it when we spawn it with a different working directory. |
531 if (dartBin.contains(Platform.pathSeparator)) { | 531 if (dartBin.contains(Platform.pathSeparator)) { |
532 dartBin = p.absolute(dartBin); | 532 dartBin = p.absolute(dartBin); |
533 } | 533 } |
534 | 534 |
535 // Always run pub from a snapshot. Using the snapshot makes running the tests | 535 // If there's a snapshot available, use it. The user is responsible for |
536 // much faster, especially when multiple tests are run at once. | 536 // ensuring this is up-to-date.. |
537 var pubPath = p.absolute(p.join(pubRoot, '.pub/pub.test.snapshot')); | 537 // |
| 538 // TODO(nweiz): When the test runner supports plugins, create one to |
| 539 // auto-generate the snapshot before each run. |
| 540 var pubPath = p.absolute(p.join(pubRoot, 'bin/pub.dart')); |
| 541 if (fileExists('$pubPath.snapshot')) pubPath += '.snapshot'; |
| 542 |
538 var dartArgs = [pubPath, '--verbose']; | 543 var dartArgs = [pubPath, '--verbose']; |
539 dartArgs.addAll(args); | 544 dartArgs.addAll(args); |
540 | 545 |
541 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); | 546 if (tokenEndpoint == null) tokenEndpoint = new Future.value(); |
542 var environmentFuture = tokenEndpoint | 547 var environmentFuture = tokenEndpoint |
543 .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)) | 548 .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)) |
544 .then((pubEnvironment) { | 549 .then((pubEnvironment) { |
545 if (environment != null) pubEnvironment.addAll(environment); | 550 if (environment != null) pubEnvironment.addAll(environment); |
546 return pubEnvironment; | 551 return pubEnvironment; |
547 }); | 552 }); |
548 | 553 |
549 _ensureSnapshot(); | |
550 | |
551 return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, | 554 return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, |
552 workingDirectory: _pathInSandbox(appPath), | 555 workingDirectory: _pathInSandbox(appPath), |
553 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 556 description: args.isEmpty ? 'pub' : 'pub ${args.first}'); |
554 } | 557 } |
555 | 558 |
556 /// Ensure that a snapshot of the current pub source exists at | |
557 /// ".pub/pub.snapshot". | |
558 void _ensureSnapshot() { | |
559 ensureDir(p.join(pubRoot, '.pub')); | |
560 | |
561 var version = sdk.version.toString(); | |
562 var pubHash = _hashChanges(); | |
563 var dartHash = runningFromDartRepo ? _hashExecutable() : null; | |
564 | |
565 var snapshotPath = p.join(pubRoot, '.pub', 'pub.test.snapshot'); | |
566 var pubHashPath = p.join(pubRoot, '.pub', 'pub.hash'); | |
567 var dartHashPath = p.join(pubRoot, '.pub', 'dart.hash'); | |
568 var versionPath = p.join(pubRoot, '.pub', 'pub.version'); | |
569 if (fileExists(pubHashPath) && fileExists(versionPath) && | |
570 (!runningFromDartRepo || fileExists(dartHashPath))) { | |
571 var oldPubHash = readTextFile(pubHashPath); | |
572 var oldDartHash = runningFromDartRepo ? readTextFile(dartHashPath) : null; | |
573 var oldVersion = readTextFile(versionPath); | |
574 | |
575 if (oldPubHash == pubHash && oldDartHash == dartHash && | |
576 oldVersion == version && fileExists(snapshotPath)) { | |
577 return; | |
578 } | |
579 } | |
580 | |
581 var args = ['--snapshot=$snapshotPath']; | |
582 if (Platform.packageRoot.isNotEmpty) { | |
583 args.add('--package-root=${Platform.packageRoot}'); | |
584 } | |
585 args.add(p.join(pubRoot, 'bin', 'pub.dart')); | |
586 | |
587 var dartSnapshot = runProcessSync(Platform.executable, args); | |
588 if (dartSnapshot.exitCode != 0) throw "Failed to run dart --snapshot."; | |
589 | |
590 writeTextFile(pubHashPath, pubHash); | |
591 if (runningFromDartRepo) writeTextFile(dartHashPath, dartHash); | |
592 writeTextFile(versionPath, version); | |
593 } | |
594 | |
595 /// Returns a hash that encapsulates the current state of the repo. | |
596 String _hashChanges() { | |
597 var hash = new SHA1(); | |
598 | |
599 // Include the current Git commit. | |
600 hash.add(UTF8.encode( | |
601 gitlib.runSync(['rev-parse', 'HEAD'], workingDir: pubRoot).first)); | |
602 | |
603 // Include the changes in lib and bin relative to the current Git commit. | |
604 var tracked = gitlib.runSync(['diff-index', '--patch', 'HEAD', 'lib', 'bin'], | |
605 workingDir: pubRoot); | |
606 for (var line in tracked) { | |
607 hash.add(UTF8.encode("$line\n")); | |
608 } | |
609 | |
610 // Include the full contents of non-ignored files in lib and bin that aren't | |
611 // tracked by Git. | |
612 var untracked = gitlib.runSync( | |
613 ['ls-files', '--others', '--exclude-standard', 'lib', 'bin'], | |
614 workingDir: pubRoot); | |
615 for (var path in untracked) { | |
616 hash.add(readBinaryFile(path)); | |
617 } | |
618 | |
619 return CryptoUtils.bytesToHex(hash.close()); | |
620 } | |
621 | |
622 /// Return a SHA1 hash of the Dart executable used to run this script. | |
623 /// | |
624 /// This is used when running within the Dart repo to ensure that the snapshot | |
625 /// is invalidated when the executable changes. | |
626 String _hashExecutable() { | |
627 var hash = new SHA1(); | |
628 hash.add(new File(Platform.executable).readAsBytesSync()); | |
629 return CryptoUtils.bytesToHex(hash.close()); | |
630 } | |
631 | |
632 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 559 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output |
633 /// and makes [stdout] and [stderr] work as though pub weren't running in | 560 /// and makes [stdout] and [stderr] work as though pub weren't running in |
634 /// verbose mode. | 561 /// verbose mode. |
635 class PubProcess extends ScheduledProcess { | 562 class PubProcess extends ScheduledProcess { |
636 Stream<Pair<log.Level, String>> _log; | 563 Stream<Pair<log.Level, String>> _log; |
637 Stream<String> _stdout; | 564 Stream<String> _stdout; |
638 Stream<String> _stderr; | 565 Stream<String> _stderr; |
639 | 566 |
640 PubProcess.start(executable, arguments, | 567 PubProcess.start(executable, arguments, |
641 {workingDirectory, environment, String description, | 568 {workingDirectory, environment, String description, |
(...skipping 423 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1065 _lastMatcher.matches(item.last, matchState); | 992 _lastMatcher.matches(item.last, matchState); |
1066 } | 993 } |
1067 | 994 |
1068 Description describe(Description description) { | 995 Description describe(Description description) { |
1069 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 996 return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); |
1070 } | 997 } |
1071 } | 998 } |
1072 | 999 |
1073 /// A [StreamMatcher] that matches multiple lines of output. | 1000 /// A [StreamMatcher] that matches multiple lines of output. |
1074 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 1001 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); |
OLD | NEW |