| 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; | 
| 11 | 11 | 
| 12 import 'dart:async'; | 12 import 'dart:async'; | 
| 13 import 'dart:convert'; | 13 import 'dart:convert'; | 
| 14 import 'dart:io'; | 14 import 'dart:io'; | 
| 15 import 'dart:math'; | 15 import 'dart:math'; | 
| 16 | 16 | 
|  | 17 import 'package:crypto/crypto.dart'; | 
| 17 import 'package:http/testing.dart'; | 18 import 'package:http/testing.dart'; | 
| 18 import 'package:path/path.dart' as p; | 19 import 'package:path/path.dart' as p; | 
| 19 import 'package:pub_semver/pub_semver.dart'; | 20 import 'package:pub_semver/pub_semver.dart'; | 
| 20 import 'package:scheduled_test/scheduled_process.dart'; | 21 import 'package:scheduled_test/scheduled_process.dart'; | 
| 21 import 'package:scheduled_test/scheduled_server.dart'; | 22 import 'package:scheduled_test/scheduled_server.dart'; | 
| 22 import 'package:scheduled_test/scheduled_stream.dart'; | 23 import 'package:scheduled_test/scheduled_stream.dart'; | 
| 23 import 'package:scheduled_test/scheduled_test.dart' hide fail; | 24 import 'package:scheduled_test/scheduled_test.dart' hide fail; | 
| 24 import 'package:shelf/shelf.dart' as shelf; | 25 import 'package:shelf/shelf.dart' as shelf; | 
| 25 import 'package:shelf/shelf_io.dart' as shelf_io; | 26 import 'package:shelf/shelf_io.dart' as shelf_io; | 
| 26 import 'package:unittest/compact_vm_config.dart'; | 27 import 'package:unittest/compact_vm_config.dart'; | 
| 27 import 'package:yaml/yaml.dart'; | 28 import 'package:yaml/yaml.dart'; | 
| 28 | 29 | 
| 29 import '../lib/src/entrypoint.dart'; | 30 import '../lib/src/entrypoint.dart'; | 
| 30 import '../lib/src/exit_codes.dart' as exit_codes; | 31 import '../lib/src/exit_codes.dart' as exit_codes; | 
| 31 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides | 32 // TODO(rnystrom): Using "gitlib" as the prefix here is ugly, but "git" collides | 
| 32 // with the git descriptor method. Maybe we should try to clean up the top level | 33 // with the git descriptor method. Maybe we should try to clean up the top level | 
| 33 // scope a bit? | 34 // scope a bit? | 
| 34 import '../lib/src/git.dart' as gitlib; | 35 import '../lib/src/git.dart' as gitlib; | 
| 35 import '../lib/src/http.dart'; | 36 import '../lib/src/http.dart'; | 
| 36 import '../lib/src/io.dart'; | 37 import '../lib/src/io.dart'; | 
| 37 import '../lib/src/lock_file.dart'; | 38 import '../lib/src/lock_file.dart'; | 
| 38 import '../lib/src/log.dart' as log; | 39 import '../lib/src/log.dart' as log; | 
| 39 import '../lib/src/package.dart'; | 40 import '../lib/src/package.dart'; | 
| 40 import '../lib/src/pubspec.dart'; | 41 import '../lib/src/pubspec.dart'; | 
|  | 42 import '../lib/src/sdk.dart' as sdk; | 
| 41 import '../lib/src/source/hosted.dart'; | 43 import '../lib/src/source/hosted.dart'; | 
| 42 import '../lib/src/source/path.dart'; | 44 import '../lib/src/source/path.dart'; | 
| 43 import '../lib/src/source_registry.dart'; | 45 import '../lib/src/source_registry.dart'; | 
| 44 import '../lib/src/system_cache.dart'; | 46 import '../lib/src/system_cache.dart'; | 
| 45 import '../lib/src/utils.dart'; | 47 import '../lib/src/utils.dart'; | 
| 46 import '../lib/src/validator.dart'; | 48 import '../lib/src/validator.dart'; | 
| 47 import 'descriptor.dart' as d; | 49 import 'descriptor.dart' as d; | 
| 48 import 'serve_packages.dart'; | 50 import 'serve_packages.dart'; | 
| 49 | 51 | 
| 50 export 'serve_packages.dart'; | 52 export 'serve_packages.dart'; | 
| (...skipping 461 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 512   // Find a Dart executable we can use to spawn. Use the same one that was | 514   // Find a Dart executable we can use to spawn. Use the same one that was | 
| 513   // used to run this script itself. | 515   // used to run this script itself. | 
| 514   var dartBin = Platform.executable; | 516   var dartBin = Platform.executable; | 
| 515 | 517 | 
| 516   // If the executable looks like a path, get its full path. That way we | 518   // If the executable looks like a path, get its full path. That way we | 
| 517   // can still find it when we spawn it with a different working directory. | 519   // can still find it when we spawn it with a different working directory. | 
| 518   if (dartBin.contains(Platform.pathSeparator)) { | 520   if (dartBin.contains(Platform.pathSeparator)) { | 
| 519     dartBin = p.absolute(dartBin); | 521     dartBin = p.absolute(dartBin); | 
| 520   } | 522   } | 
| 521 | 523 | 
| 522   // Always run pub from a snapshot. Since we require the SDK to be built, the | 524   // Always run pub from a snapshot. Using the snapshot makes running the tests | 
| 523   // snapshot should be there. Note that this *does* mean that the snapshot has | 525   // much faster, especially when multiple tests are run at once. | 
| 524   // to be manually updated when changing code before running the tests. | 526   var pubPath = p.absolute(p.join(pubRoot, '.pub/pub.test.snapshot')); | 
| 525   // Otherwise, you will test against stale data. |  | 
| 526   // |  | 
| 527   // Using the snapshot makes running the tests much faster, which is why we |  | 
| 528   // make this trade-off. |  | 
| 529   var pubPath = p.join(p.dirname(dartBin), 'snapshots/pub.dart.snapshot'); |  | 
| 530   var dartArgs = [pubPath, '--verbose']; | 527   var dartArgs = [pubPath, '--verbose']; | 
| 531   dartArgs.addAll(args); | 528   dartArgs.addAll(args); | 
| 532 | 529 | 
| 533   if (tokenEndpoint == null) tokenEndpoint = new Future.value(); | 530   if (tokenEndpoint == null) tokenEndpoint = new Future.value(); | 
| 534   var environmentFuture = tokenEndpoint | 531   var environmentFuture = tokenEndpoint | 
| 535       .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)) | 532       .then((tokenEndpoint) => getPubTestEnvironment(tokenEndpoint)) | 
| 536       .then((pubEnvironment) { | 533       .then((pubEnvironment) { | 
| 537     if (environment != null) pubEnvironment.addAll(environment); | 534     if (environment != null) pubEnvironment.addAll(environment); | 
| 538     return pubEnvironment; | 535     return pubEnvironment; | 
| 539   }); | 536   }); | 
| 540 | 537 | 
|  | 538   _ensureSnapshot(); | 
|  | 539 | 
| 541   return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, | 540   return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, | 
| 542       workingDirectory: _pathInSandbox(appPath), | 541       workingDirectory: _pathInSandbox(appPath), | 
| 543       description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 542       description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 
| 544 } | 543 } | 
| 545 | 544 | 
|  | 545 /// Ensure that a snapshot of the current pub source exists at | 
|  | 546 /// ".pub/pub.snapshot". | 
|  | 547 void _ensureSnapshot() { | 
|  | 548   ensureDir(p.join(pubRoot, '.pub')); | 
|  | 549 | 
|  | 550   var version = sdk.version.toString(); | 
|  | 551   var hash = _hashChanges(); | 
|  | 552 | 
|  | 553   var snapshotPath = p.join(pubRoot, '.pub', 'pub.test.snapshot'); | 
|  | 554   var hashPath = p.join(pubRoot, '.pub', 'pub.hash'); | 
|  | 555   var versionPath = p.join(pubRoot, '.pub', 'pub.version'); | 
|  | 556   if (fileExists(hashPath) && fileExists(versionPath)) { | 
|  | 557     var oldHash = readTextFile(hashPath); | 
|  | 558     var oldVersion = readTextFile(versionPath); | 
|  | 559 | 
|  | 560     if (oldHash == hash && oldVersion == version && fileExists(snapshotPath)) { | 
|  | 561       return; | 
|  | 562     } | 
|  | 563   } | 
|  | 564 | 
|  | 565   var dartSnapshot = runProcessSync(Platform.executable, [ | 
|  | 566     '--snapshot=$snapshotPath', | 
|  | 567     p.join(pubRoot, 'bin', 'pub.dart') | 
|  | 568   ]); | 
|  | 569   if (dartSnapshot.exitCode != 0) throw "Failed to run dart --snapshot."; | 
|  | 570 | 
|  | 571   writeTextFile(hashPath, hash); | 
|  | 572   writeTextFile(versionPath, version); | 
|  | 573 } | 
|  | 574 | 
|  | 575 /// Returns a hash that encapsulates the current state of the repo. | 
|  | 576 String _hashChanges() { | 
|  | 577   var hash = new SHA1(); | 
|  | 578 | 
|  | 579   // Include the current Git commit. | 
|  | 580   hash.add(UTF8.encode(gitlib.runSync(['rev-parse', 'HEAD']).first)); | 
|  | 581 | 
|  | 582   // Include the changes in lib and bin relative to the current Git commit. | 
|  | 583   var tracked = gitlib.runSync(['diff-index', '--patch', 'HEAD', 'lib', 'bin']); | 
|  | 584   for (var line in tracked) { | 
|  | 585     hash.add(UTF8.encode("$line\n")); | 
|  | 586   } | 
|  | 587 | 
|  | 588   // Include the full contents of non-ignored files in lib and bin that aren't | 
|  | 589   // tracked by Git. | 
|  | 590   var untracked = gitlib.runSync( | 
|  | 591       ['ls-files', '--others', '--exclude-standard', 'lib', 'bin']); | 
|  | 592   for (var path in untracked) { | 
|  | 593     hash.add(readBinaryFile(path)); | 
|  | 594   } | 
|  | 595 | 
|  | 596   return CryptoUtils.bytesToHex(hash.close()); | 
|  | 597 } | 
|  | 598 | 
| 546 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 599 /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 
| 547 /// and makes [stdout] and [stderr] work as though pub weren't running in | 600 /// and makes [stdout] and [stderr] work as though pub weren't running in | 
| 548 /// verbose mode. | 601 /// verbose mode. | 
| 549 class PubProcess extends ScheduledProcess { | 602 class PubProcess extends ScheduledProcess { | 
| 550   Stream<Pair<log.Level, String>> _log; | 603   Stream<Pair<log.Level, String>> _log; | 
| 551   Stream<String> _stdout; | 604   Stream<String> _stdout; | 
| 552   Stream<String> _stderr; | 605   Stream<String> _stderr; | 
| 553 | 606 | 
| 554   PubProcess.start(executable, arguments, | 607   PubProcess.start(executable, arguments, | 
| 555       {workingDirectory, environment, String description, | 608       {workingDirectory, environment, String description, | 
| (...skipping 411 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 967         _lastMatcher.matches(item.last, matchState); | 1020         _lastMatcher.matches(item.last, matchState); | 
| 968   } | 1021   } | 
| 969 | 1022 | 
| 970   Description describe(Description description) { | 1023   Description describe(Description description) { | 
| 971     return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 1024     return description.addAll("(", ", ", ")", [_firstMatcher, _lastMatcher]); | 
| 972   } | 1025   } | 
| 973 } | 1026 } | 
| 974 | 1027 | 
| 975 /// A [StreamMatcher] that matches multiple lines of output. | 1028 /// A [StreamMatcher] that matches multiple lines of output. | 
| 976 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 1029 StreamMatcher emitsLines(String output) => inOrder(output.split("\n")); | 
| OLD | NEW | 
|---|