| Index: test/test_pub.dart | 
| diff --git a/test/test_pub.dart b/test/test_pub.dart | 
| index b0ec23f6f0fddbe4eef162d867b89052d5dfcdba..fb7164a59f96ee41333d859f8dddcb4e21425069 100644 | 
| --- a/test/test_pub.dart | 
| +++ b/test/test_pub.dart | 
| @@ -14,6 +14,7 @@ import 'dart:convert'; | 
| import 'dart:io'; | 
| import 'dart:math'; | 
|  | 
| +import 'package:crypto/crypto.dart'; | 
| import 'package:http/testing.dart'; | 
| import 'package:path/path.dart' as p; | 
| import 'package:pub_semver/pub_semver.dart'; | 
| @@ -38,6 +39,7 @@ import '../lib/src/lock_file.dart'; | 
| import '../lib/src/log.dart' as log; | 
| import '../lib/src/package.dart'; | 
| import '../lib/src/pubspec.dart'; | 
| +import '../lib/src/sdk.dart' as sdk; | 
| import '../lib/src/source/hosted.dart'; | 
| import '../lib/src/source/path.dart'; | 
| import '../lib/src/source_registry.dart'; | 
| @@ -519,14 +521,9 @@ ScheduledProcess startPub({List args, Future<String> tokenEndpoint, | 
| dartBin = p.absolute(dartBin); | 
| } | 
|  | 
| -  // Always run pub from a snapshot. Since we require the SDK to be built, the | 
| -  // snapshot should be there. Note that this *does* mean that the snapshot has | 
| -  // to be manually updated when changing code before running the tests. | 
| -  // Otherwise, you will test against stale data. | 
| -  // | 
| -  // Using the snapshot makes running the tests much faster, which is why we | 
| -  // make this trade-off. | 
| -  var pubPath = p.join(p.dirname(dartBin), 'snapshots/pub.dart.snapshot'); | 
| +  // Always run pub from a snapshot. Using the snapshot makes running the tests | 
| +  // much faster, especially when multiple tests are run at once. | 
| +  var pubPath = p.absolute(p.join(pubRoot, '.pub/pub.test.snapshot')); | 
| var dartArgs = [pubPath, '--verbose']; | 
| dartArgs.addAll(args); | 
|  | 
| @@ -538,11 +535,67 @@ ScheduledProcess startPub({List args, Future<String> tokenEndpoint, | 
| return pubEnvironment; | 
| }); | 
|  | 
| +  _ensureSnapshot(); | 
| + | 
| return new PubProcess.start(dartBin, dartArgs, environment: environmentFuture, | 
| workingDirectory: _pathInSandbox(appPath), | 
| description: args.isEmpty ? 'pub' : 'pub ${args.first}'); | 
| } | 
|  | 
| +/// Ensure that a snapshot of the current pub source exists at | 
| +/// ".pub/pub.snapshot". | 
| +void _ensureSnapshot() { | 
| +  ensureDir(p.join(pubRoot, '.pub')); | 
| + | 
| +  var version = sdk.version.toString(); | 
| +  var hash = _hashChanges(); | 
| + | 
| +  var snapshotPath = p.join(pubRoot, '.pub', 'pub.test.snapshot'); | 
| +  var hashPath = p.join(pubRoot, '.pub', 'pub.hash'); | 
| +  var versionPath = p.join(pubRoot, '.pub', 'pub.version'); | 
| +  if (fileExists(hashPath) && fileExists(versionPath)) { | 
| +    var oldHash = readTextFile(hashPath); | 
| +    var oldVersion = readTextFile(versionPath); | 
| + | 
| +    if (oldHash == hash && oldVersion == version && fileExists(snapshotPath)) { | 
| +      return; | 
| +    } | 
| +  } | 
| + | 
| +  var dartSnapshot = runProcessSync(Platform.executable, [ | 
| +    '--snapshot=$snapshotPath', | 
| +    p.join(pubRoot, 'bin', 'pub.dart') | 
| +  ]); | 
| +  if (dartSnapshot.exitCode != 0) throw "Failed to run dart --snapshot."; | 
| + | 
| +  writeTextFile(hashPath, hash); | 
| +  writeTextFile(versionPath, version); | 
| +} | 
| + | 
| +/// Returns a hash that encapsulates the current state of the repo. | 
| +String _hashChanges() { | 
| +  var hash = new SHA1(); | 
| + | 
| +  // Include the current Git commit. | 
| +  hash.add(UTF8.encode(gitlib.runSync(['rev-parse', 'HEAD']).first)); | 
| + | 
| +  // Include the changes in lib and bin relative to the current Git commit. | 
| +  var tracked = gitlib.runSync(['diff-index', '--patch', 'HEAD', 'lib', 'bin']); | 
| +  for (var line in tracked) { | 
| +    hash.add(UTF8.encode("$line\n")); | 
| +  } | 
| + | 
| +  // Include the full contents of non-ignored files in lib and bin that aren't | 
| +  // tracked by Git. | 
| +  var untracked = gitlib.runSync( | 
| +      ['ls-files', '--others', '--exclude-standard', 'lib', 'bin']); | 
| +  for (var path in untracked) { | 
| +    hash.add(readBinaryFile(path)); | 
| +  } | 
| + | 
| +  return CryptoUtils.bytesToHex(hash.close()); | 
| +} | 
| + | 
| /// A subclass of [ScheduledProcess] that parses pub's verbose logging output | 
| /// and makes [stdout] and [stderr] work as though pub weren't running in | 
| /// verbose mode. | 
|  |