diff --git a/lib/pub.dart b/lib/pub.dart
index b2fed29..5b3054f 100644
--- a/lib/pub.dart
+++ b/lib/pub.dart
@@ -6,7 +6,11 @@
 import 'src/command_runner.dart';
 import 'src/pub_embeddable_command.dart';
 export 'src/executable.dart'
-    show getExecutableForCommand, CommandResolutionFailedException;
+    show
+        getExecutableForCommand,
+        CommandResolutionFailedException,
+        CommandResolutionIssue,
+        DartExecutableWithPackageConfig;
 export 'src/pub_embeddable_command.dart' show PubAnalytics;
 
 /// Returns a [Command] for pub functionality that can be used by an embedding
diff --git a/lib/src/command/add.dart b/lib/src/command/add.dart
index b481fe4..09cc426 100644
--- a/lib/src/command/add.dart
+++ b/lib/src/command/add.dart
@@ -389,8 +389,13 @@
         pubspecInformation = {'hosted': hostInfo};
       }
 
-      packageRange = PackageRange(packageName, cache.sources['hosted'],
-          constraint ?? VersionConstraint.any, hostInfo ?? packageName);
+      packageRange = cache.hosted.source
+          .parseRef(
+            packageName,
+            hostInfo,
+            languageVersion: entrypoint.root.pubspec.languageVersion,
+          )
+          .withConstraint(constraint ?? VersionConstraint.any);
     }
 
     if (pubspecInformation is Map && constraint != null) {
diff --git a/lib/src/dart.dart b/lib/src/dart.dart
index e498058..97219dc 100644
--- a/lib/src/dart.dart
+++ b/lib/src/dart.dart
@@ -7,8 +7,7 @@
 import 'dart:io';
 
 import 'package:analyzer/dart/analysis/analysis_context.dart';
-import 'package:analyzer/dart/analysis/context_builder.dart';
-import 'package:analyzer/dart/analysis/context_locator.dart';
+import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
 import 'package:analyzer/dart/analysis/session.dart';
 import 'package:analyzer/dart/ast/ast.dart';
@@ -67,21 +66,22 @@
       modificationStamp: 0,
     );
 
+    var contextCollection = AnalysisContextCollection(
+      includedPaths: [path],
+      resourceProvider: resourceProvider,
+      sdkPath: getSdkPath(),
+    );
+
     // Add new contexts for the given path.
-    var contextLocator = ContextLocator(resourceProvider: resourceProvider);
-    var roots = contextLocator.locateRoots(includedPaths: [path]);
-    for (var root in roots) {
-      var contextRootPath = root.root.path;
+    for (var analysisContext in contextCollection.contexts) {
+      var contextRootPath = analysisContext.contextRoot.root.path;
 
       // If there is already a context for this context root path, keep it.
       if (_contexts.containsKey(contextRootPath)) {
         continue;
       }
 
-      var contextBuilder = ContextBuilder();
-      var context = contextBuilder.createContext(
-          contextRoot: root, sdkPath: getSdkPath());
-      _contexts[contextRootPath] = context;
+      _contexts[contextRootPath] = analysisContext;
     }
   }
 
@@ -94,7 +94,7 @@
   /// Throws [AnalyzerErrorGroup] is the file has parsing errors.
   CompilationUnit parse(String path) {
     path = p.normalize(p.absolute(path));
-    var parseResult = _getExistingSession(path).getParsedUnit2(path);
+    var parseResult = _getExistingSession(path).getParsedUnit(path);
     if (parseResult is ParsedUnitResult) {
       if (parseResult.errors.isNotEmpty) {
         throw AnalyzerErrorGroup(parseResult.errors);
diff --git a/lib/src/executable.dart b/lib/src/executable.dart
index 8c7be58..2112acc 100644
--- a/lib/src/executable.dart
+++ b/lib/src/executable.dart
@@ -7,6 +7,7 @@
 import 'dart:isolate';
 
 import 'package:args/args.dart';
+import 'package:meta/meta.dart';
 import 'package:path/path.dart' as p;
 
 import 'entrypoint.dart';
@@ -202,7 +203,23 @@
   }
 }
 
-/// Returns the path to dart program/snapshot to invoke for running [descriptor]
+/// The result of a `getExecutableForCommand` command resolution.
+@sealed
+class DartExecutableWithPackageConfig {
+  /// Can be a .dart file or a incremental snapshot.
+  final String executable;
+
+  /// The package_config.json to run [executable] with. Or <null> if the VM
+  /// should find it according to the standard rules.
+  final String? packageConfig;
+
+  DartExecutableWithPackageConfig({
+    required this.executable,
+    required this.packageConfig,
+  });
+}
+
+/// Returns the dart program/snapshot to invoke for running [descriptor]
 /// resolved according to the package configuration of the package at [root]
 /// (defaulting to the current working directory). Using the pub-cache at
 /// [pubCacheDir] (defaulting to the default pub cache).
@@ -237,7 +254,7 @@
 /// * `` and `:` both resolves to `<current>:bin/<current>.dart` or
 ///   `bin/<current>:main.dart`.
 ///
-/// If that doesn't resolve as an existing file throw an exception.
+/// If that doesn't resolve as an existing file, throw an exception.
 ///
 /// ## Snapshotting
 ///
@@ -250,7 +267,7 @@
 ///
 /// Throws an [CommandResolutionFailedException] if the command is not found or
 /// if the entrypoint is not up to date (requires `pub get`) and a `pub get`.
-Future<String> getExecutableForCommand(
+Future<DartExecutableWithPackageConfig> getExecutableForCommand(
   String descriptor, {
   bool allowSnapshot = true,
   String? root,
@@ -270,71 +287,127 @@
   }
 
   final asDirectFile = p.join(root, asPath);
-  if (fileExists(asDirectFile)) return p.relative(asDirectFile, from: root);
-  if (!fileExists(p.join(root, 'pubspec.yaml'))) {
-    throw CommandResolutionFailedException('Could not find file `$descriptor`');
+  if (fileExists(asDirectFile)) {
+    return DartExecutableWithPackageConfig(
+      executable: p.relative(asDirectFile, from: root),
+      packageConfig: null,
+    );
   }
+  if (!fileExists(p.join(root, 'pubspec.yaml'))) {
+    throw CommandResolutionFailedException._(
+        'Could not find file `$descriptor`',
+        CommandResolutionIssue.fileNotFound);
+  }
+  final entrypoint = Entrypoint(root, SystemCache(rootDir: pubCacheDir));
   try {
-    final entrypoint = Entrypoint(root, SystemCache(rootDir: pubCacheDir));
+    // TODO(sigurdm): it would be nicer with a 'isUpToDate' function.
+    entrypoint.assertUpToDate();
+  } on DataException {
     try {
-      // TODO(sigurdm): it would be nicer with a 'isUpToDate' function.
-      entrypoint.assertUpToDate();
-    } on DataException {
       await warningsOnlyUnlessTerminal(
         () => entrypoint.acquireDependencies(
           SolveType.GET,
           analytics: analytics,
         ),
       );
+    } on ApplicationException catch (e) {
+      throw CommandResolutionFailedException._(
+          e.toString(), CommandResolutionIssue.pubGetFailed);
     }
+  }
 
-    String command;
-    String package;
-    if (descriptor.contains(':')) {
-      final parts = descriptor.split(':');
-      if (parts.length > 2) {
-        throw CommandResolutionFailedException(
-            '[<package>[:command]] cannot contain multiple ":"');
-      }
-      package = parts[0];
-      if (package.isEmpty) package = entrypoint.root.name;
-      command = parts[1];
-    } else {
-      package = descriptor;
-      if (package.isEmpty) package = entrypoint.root.name;
-      command = package;
+  late final String command;
+  String package;
+  if (descriptor.contains(':')) {
+    final parts = descriptor.split(':');
+    if (parts.length > 2) {
+      throw CommandResolutionFailedException._(
+        '[<package>[:command]] cannot contain multiple ":"',
+        CommandResolutionIssue.parseError,
+      );
     }
+    package = parts[0];
+    if (package.isEmpty) package = entrypoint.root.name;
+    command = parts[1];
+  } else {
+    package = descriptor;
+    if (package.isEmpty) package = entrypoint.root.name;
+    command = package;
+  }
 
-    final executable = Executable(package, p.join('bin', '$command.dart'));
-    if (!entrypoint.packageGraph.packages.containsKey(package)) {
-      throw CommandResolutionFailedException(
-          'Could not find package `$package` or file `$descriptor`');
-    }
-    final path = entrypoint.resolveExecutable(executable);
-    if (!fileExists(path)) {
-      throw CommandResolutionFailedException(
-          'Could not find `bin${p.separator}$command.dart` in package `$package`.');
-    }
-    if (!allowSnapshot) {
-      return p.relative(path, from: root);
-    } else {
-      final snapshotPath = entrypoint.pathOfExecutable(executable);
-      if (!fileExists(snapshotPath) ||
-          entrypoint.packageGraph.isPackageMutable(package)) {
+  if (!entrypoint.packageGraph.packages.containsKey(package)) {
+    throw CommandResolutionFailedException._(
+      'Could not find package `$package` or file `$descriptor`',
+      CommandResolutionIssue.packageNotFound,
+    );
+  }
+  final executable = Executable(package, p.join('bin', '$command.dart'));
+  final packageConfig = p.join('.dart_tool', 'package_config.json');
+
+  final path = entrypoint.resolveExecutable(executable);
+  if (!fileExists(path)) {
+    throw CommandResolutionFailedException._(
+      'Could not find `bin${p.separator}$command.dart` in package `$package`.',
+      CommandResolutionIssue.noBinaryFound,
+    );
+  }
+  if (!allowSnapshot) {
+    return DartExecutableWithPackageConfig(
+      executable: p.relative(path, from: root),
+      packageConfig: packageConfig,
+    );
+  } else {
+    final snapshotPath = entrypoint.pathOfExecutable(executable);
+    if (!fileExists(snapshotPath) ||
+        entrypoint.packageGraph.isPackageMutable(package)) {
+      try {
         await warningsOnlyUnlessTerminal(
           () => entrypoint.precompileExecutable(executable),
         );
+      } on ApplicationException catch (e) {
+        throw CommandResolutionFailedException._(
+          e.toString(),
+          CommandResolutionIssue.compilationFailed,
+        );
       }
-      return p.relative(snapshotPath, from: root);
     }
-  } on ApplicationException catch (e) {
-    throw CommandResolutionFailedException(e.toString());
+    return DartExecutableWithPackageConfig(
+      executable: p.relative(snapshotPath, from: root),
+      packageConfig: packageConfig,
+    );
   }
 }
 
+/// Information on why no executable is returned.
+enum CommandResolutionIssue {
+  /// The command string looked like a file (contained '.' '/' or '\\'), but no
+  /// such file exists.
+  fileNotFound,
+
+  /// The command-string was '<package>:<binary>' or '<package>', and <package>
+  /// was not in dependencies.
+  packageNotFound,
+
+  /// The command string was '<package>:<binary>' or ':<binary>' and <binary>
+  /// was not found.
+  noBinaryFound,
+
+  /// Failed retrieving dependencies (pub get).
+  pubGetFailed,
+
+  /// Pre-compilation of the binary failed.
+  compilationFailed,
+
+  /// The command string did not have a valid form (eg. more than one ':').
+  parseError,
+}
+
+/// Indicates that a command string did not resolve to an executable.
+@sealed
 class CommandResolutionFailedException implements Exception {
   final String message;
-  CommandResolutionFailedException(this.message);
+  final CommandResolutionIssue issue;
+  CommandResolutionFailedException._(this.message, this.issue);
 
   @override
   String toString() {
diff --git a/lib/src/global_packages.dart b/lib/src/global_packages.dart
index 9c66bb8..578eca1 100644
--- a/lib/src/global_packages.dart
+++ b/lib/src/global_packages.dart
@@ -218,7 +218,7 @@
     if (sameVersions) {
       log.message('''
 The package ${dep.name} is already activated at newest available version.
-To recompile executables, first run `global deactivate ${dep.name}`.
+To recompile executables, first run `$topLevelProgram pub global deactivate ${dep.name}`.
 ''');
     } else {
       await result.showReport(SolveType.GET, cache);
diff --git a/lib/src/io.dart b/lib/src/io.dart
index b4898ff..641da73 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -8,6 +8,7 @@
 import 'dart:convert';
 import 'dart:io';
 
+import 'package:async/async.dart';
 import 'package:cli_util/cli_util.dart'
     show EnvironmentNotFoundException, applicationConfigHome;
 import 'package:http/http.dart' show ByteStream;
@@ -562,8 +563,8 @@
 })();
 
 /// A line-by-line stream of standard input.
-final Stream<String> _stdinLines =
-    ByteStream(stdin).toStringStream().transform(const LineSplitter());
+final StreamQueue<String> _stdinLines = StreamQueue(
+    ByteStream(stdin).toStringStream().transform(const LineSplitter()));
 
 /// Displays a message and reads a yes/no confirmation from the user.
 ///
@@ -580,7 +581,7 @@
 }
 
 /// Writes [prompt] and reads a line from stdin.
-Future<String> stdinPrompt(String prompt, {bool? echoMode}) {
+Future<String> stdinPrompt(String prompt, {bool? echoMode}) async {
   if (runningFromTest) {
     log.message(prompt);
   } else {
@@ -588,12 +589,16 @@
   }
   if (echoMode != null && stdin.hasTerminal) {
     final previousEchoMode = stdin.echoMode;
-    stdin.echoMode = echoMode;
-    return _stdinLines.first.whenComplete(() {
+    try {
+      stdin.echoMode = echoMode;
+      final result = await _stdinLines.next;
+      stdout.write('\n');
+      return result;
+    } finally {
       stdin.echoMode = previousEchoMode;
-    });
+    }
   } else {
-    return _stdinLines.first;
+    return await _stdinLines.next;
   }
 }
 
diff --git a/lib/src/null_safety_analysis.dart b/lib/src/null_safety_analysis.dart
index 93c8993..2ad034d 100644
--- a/lib/src/null_safety_analysis.dart
+++ b/lib/src/null_safety_analysis.dart
@@ -4,9 +4,9 @@
 
 import 'dart:async';
 
-import 'package:analyzer/dart/analysis/context_builder.dart';
-import 'package:analyzer/dart/analysis/context_locator.dart';
+import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:cli_util/cli_util.dart';
 import 'package:path/path.dart' as path;
 import 'package:source_span/source_span.dart';
@@ -175,14 +175,13 @@
         final libDir =
             path.absolute(path.normalize(path.join(packageDir, 'lib')));
         if (dirExists(libDir)) {
-          final analysisSession = ContextBuilder()
-              .createContext(
-                sdkPath: getSdkPath(),
-                contextRoot: ContextLocator().locateRoots(
-                  includedPaths: [path.normalize(packageDir)],
-                ).first,
-              )
-              .currentSession;
+          var contextCollection = AnalysisContextCollection(
+            includedPaths: [path.normalize(packageDir)],
+            resourceProvider: PhysicalResourceProvider.INSTANCE,
+            sdkPath: getSdkPath(),
+          );
+          var analysisContext = contextCollection.contexts.first;
+          var analysisSession = analysisContext.currentSession;
 
           for (final file in listDir(libDir,
               recursive: true, includeDirs: false, includeHidden: true)) {
@@ -190,7 +189,7 @@
               final fileUrl =
                   'package:${dependencyId.name}/${path.relative(file, from: libDir)}';
               final someUnitResult =
-                  analysisSession.getParsedUnit2(path.normalize(file));
+                  analysisSession.getParsedUnit(path.normalize(file));
               ParsedUnitResult unitResult;
               if (someUnitResult is ParsedUnitResult) {
                 unitResult = someUnitResult;
diff --git a/lib/src/pubspec.dart b/lib/src/pubspec.dart
index b700638..cc75b2d 100644
--- a/lib/src/pubspec.dart
+++ b/lib/src/pubspec.dart
@@ -441,10 +441,8 @@
       VersionConstraint versionConstraint = VersionRange();
       var features = const <String, FeatureDependency>{};
       if (spec == null) {
-        descriptionNode = nameNode;
         sourceName = _sources!.defaultSource.name;
       } else if (spec is String) {
-        descriptionNode = nameNode;
         sourceName = _sources!.defaultSource.name;
         versionConstraint = _parseVersionConstraint(specNode);
       } else if (spec is Map) {
@@ -468,7 +466,6 @@
         } else if (sourceNames.isEmpty) {
           // Default to a hosted dependency if no source is specified.
           sourceName = 'hosted';
-          descriptionNode = nameNode;
         }
 
         sourceName ??= sourceNames.single;
@@ -491,8 +488,12 @@
           pubspecPath = path.fromUri(_location);
         }
 
-        return _sources![sourceName]!.parseRef(name, descriptionNode?.value,
-            containingPath: pubspecPath);
+        return _sources![sourceName]!.parseRef(
+          name,
+          descriptionNode?.value,
+          containingPath: pubspecPath,
+          languageVersion: languageVersion,
+        );
       }, targetPackage: name);
 
       dependencies[name] =
@@ -611,6 +612,9 @@
     try {
       return fn();
     } on FormatException catch (e) {
+      // If we already have a pub exception with a span, re-use that
+      if (e is PubspecException) rethrow;
+
       var msg = 'Invalid $description';
       if (targetPackage != null) {
         msg = '$msg in the "$name" pubspec on the "$targetPackage" dependency';
diff --git a/lib/src/solver/report.dart b/lib/src/solver/report.dart
index 04d6fa4..251c290 100644
--- a/lib/src/solver/report.dart
+++ b/lib/src/solver/report.dart
@@ -151,7 +151,11 @@
       if (id.source == null) continue;
       final status =
           await _cache.source(id.source).status(id, maxAge: Duration(days: 3));
-      if (status.isDiscontinued) numDiscontinued++;
+      if (status.isDiscontinued &&
+          (_root.dependencyType(id.name) == DependencyType.direct ||
+              _root.dependencyType(id.name) == DependencyType.dev)) {
+        numDiscontinued++;
+      }
     }
     if (numDiscontinued > 0) {
       if (numDiscontinued == 1) {
@@ -268,7 +272,9 @@
         } else {
           message = '(retracted)';
         }
-      } else if (status.isDiscontinued) {
+      } else if (status.isDiscontinued &&
+          (_root.dependencyType(name) == DependencyType.direct ||
+              _root.dependencyType(name) == DependencyType.dev)) {
         if (status.discontinuedReplacedBy == null) {
           message = '(discontinued)';
         } else {
diff --git a/lib/src/solver/result.dart b/lib/src/solver/result.dart
index 8d4e891..692b05f 100644
--- a/lib/src/solver/result.dart
+++ b/lib/src/solver/result.dart
@@ -134,31 +134,37 @@
     final analytics = pubAnalytics.analytics;
     if (analytics == null) return;
 
+    final dependenciesForAnalytics = <PackageId>[];
     for (final package in packages) {
-      final source = package.source;
       // Only send analytics for packages from pub.dev.
-      if (source is HostedSource &&
-          (runningFromTest ||
-              package.description['url'] == HostedSource.pubDevUrl)) {
-        final dependencyKind = const {
-          DependencyType.dev: 'dev',
-          DependencyType.direct: 'direct',
-          DependencyType.none: 'transitive'
-        }[_root.dependencyType(package.name)]!;
-        analytics.sendEvent(
-          'pub-get',
-          package.name,
-          label: package.version.canonicalizedVersion,
-          value: 1,
-          parameters: {
-            'ni': '1', // We consider a pub-get a non-interactive event.
-            pubAnalytics.dependencyKindCustomDimensionName: dependencyKind,
-          },
-        );
-        log.fine(
-            'Sending analytics hit for "pub-get" of ${package.name} version ${package.version} as dependency-kind $dependencyKind');
+      if (HostedSource.isFromPubDev(package) ||
+          (package.source is HostedSource && runningFromTest)) {
+        dependenciesForAnalytics.add(package);
       }
     }
+    // Randomize the dependencies, such that even if some analytics events don't
+    // get sent, the results will still be representative.
+    shuffle(dependenciesForAnalytics);
+    for (final package in dependenciesForAnalytics) {
+      final dependencyKind = const {
+        DependencyType.dev: 'dev',
+        DependencyType.direct: 'direct',
+        DependencyType.none: 'transitive'
+      }[_root.dependencyType(package.name)]!;
+      analytics.sendEvent(
+        'pub-get',
+        package.name,
+        label: package.version.canonicalizedVersion,
+        value: 1,
+        parameters: {
+          'ni': '1', // We consider a pub-get a non-interactive event.
+          pubAnalytics.dependencyKindCustomDimensionName: dependencyKind,
+        },
+      );
+      log.fine(
+          'Sending analytics hit for "pub-get" of ${package.name} version ${package.version} as dependency-kind $dependencyKind');
+    }
+
     analytics.sendTiming(
       'resolution',
       resolutionTime.inMilliseconds,
diff --git a/lib/src/source.dart b/lib/src/source.dart
index 53c694d..add55fa 100644
--- a/lib/src/source.dart
+++ b/lib/src/source.dart
@@ -8,6 +8,7 @@
 import 'package:pub_semver/pub_semver.dart';
 
 import 'exceptions.dart';
+import 'language_version.dart';
 import 'package_name.dart';
 import 'pubspec.dart';
 import 'system_cache.dart';
@@ -76,16 +77,27 @@
   /// should be interpreted. This will be called during parsing to validate that
   /// the given [description] is well-formed according to this source, and to
   /// give the source a chance to canonicalize the description.
+  /// For simple hosted dependencies like `foo:` or `foo: ^1.2.3`, the
+  /// [description] may also be `null`.
   ///
   /// [containingPath] is the path to the pubspec where this description
   /// appears. It may be `null` if the description is coming from some in-memory
   /// source (such as pulling down a pubspec from pub.dartlang.org).
   ///
+  /// [languageVersion] is the minimum Dart version parsed from the pubspec's
+  /// `environment` field. Source implementations may use this parameter to only
+  /// support specific syntax for some versions.
+  ///
   /// The description in the returned [PackageRef] need bear no resemblance to
   /// the original user-provided description.
   ///
   /// Throws a [FormatException] if the description is not valid.
-  PackageRef parseRef(String name, description, {String? containingPath});
+  PackageRef parseRef(
+    String name,
+    description, {
+    String? containingPath,
+    required LanguageVersion languageVersion,
+  });
 
   /// Parses a [PackageId] from a name and a serialized description.
   ///
diff --git a/lib/src/source/git.dart b/lib/src/source/git.dart
index 161c540..0d98ae9 100644
--- a/lib/src/source/git.dart
+++ b/lib/src/source/git.dart
@@ -12,6 +12,7 @@
 
 import '../git.dart' as git;
 import '../io.dart';
+import '../language_version.dart';
 import '../log.dart' as log;
 import '../package.dart';
 import '../package_name.dart';
@@ -43,7 +44,12 @@
   }
 
   @override
-  PackageRef parseRef(String name, description, {String? containingPath}) {
+  PackageRef parseRef(
+    String name,
+    description, {
+    String? containingPath,
+    LanguageVersion? languageVersion,
+  }) {
     dynamic url;
     dynamic ref;
     dynamic path;
diff --git a/lib/src/source/hosted.dart b/lib/src/source/hosted.dart
index 803bbc5..def4a98 100644
--- a/lib/src/source/hosted.dart
+++ b/lib/src/source/hosted.dart
@@ -18,6 +18,7 @@
 import '../exceptions.dart';
 import '../http.dart';
 import '../io.dart';
+import '../language_version.dart';
 import '../log.dart' as log;
 import '../package.dart';
 import '../package_name.dart';
@@ -98,6 +99,11 @@
 
   static String pubDevUrl = 'https://pub.dartlang.org';
 
+  static bool isFromPubDev(PackageId id) {
+    return id.source is HostedSource &&
+        (id.description as _HostedDescription).uri.toString() == pubDevUrl;
+  }
+
   /// Gets the default URL for the package server for hosted dependencies.
   Uri get defaultUrl {
     // Changing this to pub.dev raises the following concerns:
@@ -129,19 +135,19 @@
   /// should be downloaded. [url] most be normalized and validated using
   /// [validateAndNormalizeHostedUrl].
   PackageRef refFor(String name, {Uri? url}) =>
-      PackageRef(name, this, _descriptionFor(name, url));
+      PackageRef(name, this, _HostedDescription(name, url ?? defaultUrl));
 
   /// Returns an ID for a hosted package named [name] at [version].
   ///
   /// If [url] is passed, it's the URL of the pub server from which the package
   /// should be downloaded. [url] most be normalized and validated using
   /// [validateAndNormalizeHostedUrl].
-  PackageId idFor(String name, Version version, {Uri? url}) =>
-      PackageId(name, this, version, _descriptionFor(name, url));
+  PackageId idFor(String name, Version version, {Uri? url}) => PackageId(
+      name, this, version, _HostedDescription(name, url ?? defaultUrl));
 
   /// Returns the description for a hosted package named [name] with the
   /// given package server [url].
-  dynamic _descriptionFor(String name, [Uri? url]) {
+  dynamic _serializedDescriptionFor(String name, [Uri? url]) {
     if (url == null) {
       return name;
     }
@@ -154,54 +160,119 @@
   }
 
   @override
+  dynamic serializeDescription(String containingPath, description) {
+    final desc = _asDescription(description);
+    return _serializedDescriptionFor(desc.packageName, desc.uri);
+  }
+
+  @override
   String formatDescription(description) =>
-      'on ${_parseDescription(description).last}';
+      'on ${_asDescription(description).uri}';
 
   @override
   bool descriptionsEqual(description1, description2) =>
-      _parseDescription(description1) == _parseDescription(description2);
+      _asDescription(description1) == _asDescription(description2);
 
   @override
-  int hashDescription(description) => _parseDescription(description).hashCode;
+  int hashDescription(description) => _asDescription(description).hashCode;
 
   /// Ensures that [description] is a valid hosted package description.
   ///
-  /// There are two valid formats. A plain string refers to a package with the
-  /// given name from the default host, while a map with keys "name" and "url"
-  /// refers to a package with the given name from the host at the given URL.
+  /// Simple hosted dependencies only consist of a plain string, which is
+  /// resolved against the default host. In this case, [description] will be
+  /// null.
+  ///
+  /// Hosted dependencies may also specify a custom host from which the package
+  /// is fetched. There are two syntactic forms of those dependencies:
+  ///
+  ///  1. With an url and an optional name in a map: `hosted: {url: <url>}`
+  ///  2. With a direct url: `hosted: <url>`
   @override
-  PackageRef parseRef(String name, description, {String? containingPath}) {
-    _parseDescription(description);
-    return PackageRef(name, this, description);
+  PackageRef parseRef(String name, description,
+      {String? containingPath, required LanguageVersion languageVersion}) {
+    return PackageRef(
+        name, this, _parseDescription(name, description, languageVersion));
   }
 
   @override
   PackageId parseId(String name, Version version, description,
       {String? containingPath}) {
-    _parseDescription(description);
-    return PackageId(name, this, version, description);
+    // Old pub versions only wrote `description: <pkg>` into the lock file.
+    if (description is String) {
+      if (description != name) {
+        throw FormatException('The description should be the same as the name');
+      }
+      return PackageId(
+          name, this, version, _HostedDescription(name, defaultUrl));
+    }
+
+    final serializedDescription = (description as Map).cast<String, String>();
+
+    return PackageId(
+      name,
+      this,
+      version,
+      _HostedDescription(serializedDescription['name']!,
+          Uri.parse(serializedDescription['url']!)),
+    );
   }
 
+  _HostedDescription _asDescription(desc) => desc as _HostedDescription;
+
   /// Parses the description for a package.
   ///
   /// If the package parses correctly, this returns a (name, url) pair. If not,
   /// this throws a descriptive FormatException.
-  Pair<String, Uri> _parseDescription(description) {
+  _HostedDescription _parseDescription(
+    String packageName,
+    description,
+    LanguageVersion languageVersion,
+  ) {
+    if (description == null) {
+      // Simple dependency without a `hosted` block, use the default server.
+      return _HostedDescription(packageName, defaultUrl);
+    }
+
+    final canUseShorthandSyntax =
+        languageVersion >= _minVersionForShorterHostedSyntax;
+
     if (description is String) {
-      return Pair<String, Uri>(description, defaultUrl);
+      // Old versions of pub (pre Dart 2.15) interpret `hosted: foo` as
+      // `hosted: {name: foo, url: <default>}`.
+      // For later versions, we treat it as `hosted: {name: <inferred>,
+      // url: foo}` if a user opts in by raising their min SDK environment.
+      //
+      // Since the old behavior is very rarely used and we want to show a
+      // helpful error message if the new syntax is used without raising the SDK
+      // environment, we throw an error if something that looks like a URI is
+      // used as a package name.
+      if (canUseShorthandSyntax) {
+        return _HostedDescription(
+            packageName, validateAndNormalizeHostedUrl(description));
+      } else {
+        if (_looksLikePackageName.hasMatch(description)) {
+          // Valid use of `hosted: package` dependency with an old SDK
+          // environment.
+          return _HostedDescription(description, defaultUrl);
+        } else {
+          throw FormatException(
+            'Using `hosted: <url>` is only supported with a minimum SDK '
+            'constraint of $_minVersionForShorterHostedSyntax.',
+          );
+        }
+      }
     }
 
     if (description is! Map) {
       throw FormatException('The description must be a package name or map.');
     }
 
-    if (!description.containsKey('name')) {
-      throw FormatException("The description map must contain a 'name' key.");
-    }
-
     var name = description['name'];
+    if (canUseShorthandSyntax) name ??= packageName;
+
     if (name is! String) {
-      throw FormatException("The 'name' key must have a string value.");
+      throw FormatException("The 'name' key must have a string value without "
+          'a minimum Dart SDK constraint of $_minVersionForShorterHostedSyntax.0 or higher.');
     }
 
     var url = defaultUrl;
@@ -213,8 +284,26 @@
       url = validateAndNormalizeHostedUrl(u);
     }
 
-    return Pair<String, Uri>(name, url);
+    return _HostedDescription(name, url);
   }
+
+  /// Minimum language version at which short hosted syntax is supported.
+  ///
+  /// This allows `hosted` dependencies to be expressed as:
+  /// ```yaml
+  /// dependencies:
+  ///   foo:
+  ///     hosted: https://some-pub.com/path
+  ///     version: ^1.0.0
+  /// ```
+  ///
+  /// At older versions, `hosted` dependencies had to be a map with a `url` and
+  /// a `name` key.
+  static const LanguageVersion _minVersionForShorterHostedSyntax =
+      LanguageVersion(2, 15);
+
+  static final RegExp _looksLikePackageName =
+      RegExp(r'^[a-zA-Z_]+[a-zA-Z0-9_]*$');
 }
 
 /// Information about a package version retrieved from /api/packages/$package
@@ -226,6 +315,28 @@
   _VersionInfo(this.pubspec, this.archiveUrl, this.status);
 }
 
+/// The [PackageName.description] for a [HostedSource], storing the package name
+/// and resolved URI of the package server.
+class _HostedDescription {
+  final String packageName;
+  final Uri uri;
+
+  _HostedDescription(this.packageName, this.uri) {
+    ArgumentError.checkNotNull(packageName, 'packageName');
+    ArgumentError.checkNotNull(uri, 'uri');
+  }
+
+  @override
+  int get hashCode => Object.hash(packageName, uri);
+
+  @override
+  bool operator ==(Object other) {
+    return other is _HostedDescription &&
+        other.packageName == packageName &&
+        other.uri == uri;
+  }
+}
+
 /// The [BoundSource] for [HostedSource].
 class BoundHostedSource extends CachedSource {
   @override
@@ -277,9 +388,9 @@
     final url = _listVersionsUrl(ref.description);
     log.io('Get versions from $url.');
 
-    String bodyText;
-    Map<String, dynamic>? body;
-    Map<PackageId, _VersionInfo>? result;
+    late final String bodyText;
+    late final dynamic body;
+    late final Map<PackageId, _VersionInfo> result;
     try {
       // TODO(sigurdm): Implement cancellation of requests. This probably
       // requires resolution of: https://github.com/dart-lang/sdk/issues/22265.
@@ -288,16 +399,20 @@
         serverUrl,
         (client) => client.read(url, headers: pubApiHeaders),
       );
-      body = jsonDecode(bodyText);
-      result = _versionInfoFromPackageListing(body!, ref, url);
+      final decoded = jsonDecode(bodyText);
+      if (decoded is! Map<String, dynamic>) {
+        throw FormatException('version listing must be a mapping');
+      }
+      body = decoded;
+      result = _versionInfoFromPackageListing(body, ref, url);
     } on Exception catch (error, stackTrace) {
-      var parsed = source._parseDescription(ref.description);
-      _throwFriendlyError(error, stackTrace, parsed.first, parsed.last);
+      var parsed = source._asDescription(ref.description);
+      _throwFriendlyError(error, stackTrace, parsed.packageName, parsed.uri);
     }
 
     // Cache the response on disk.
     // Don't cache overly big responses.
-    if (body!.length < 100 * 1024) {
+    if (bodyText.length < 100 * 1024) {
       await _cacheVersionListingResponse(body, ref);
     }
     return result;
@@ -466,8 +581,8 @@
 
   // The path where the response from the package-listing api is cached.
   String _versionListingCachePath(PackageRef ref) {
-    final parsed = source._parseDescription(ref.description);
-    final dir = _urlToDirectory(parsed.last);
+    final parsed = source._asDescription(ref.description);
+    final dir = _urlToDirectory(parsed.uri);
     // Use a dot-dir because older versions of pub won't choke on that
     // name when iterating the cache (it is not listed by [listDir]).
     return p.join(systemCacheRoot, dir, _versionListingDirectory,
@@ -494,16 +609,16 @@
   /// Parses [description] into its server and package name components, then
   /// converts that to a Uri for listing versions of the given package.
   Uri _listVersionsUrl(description) {
-    final parsed = source._parseDescription(description);
-    final hostedUrl = parsed.last;
-    final package = Uri.encodeComponent(parsed.first);
+    final parsed = source._asDescription(description);
+    final hostedUrl = parsed.uri;
+    final package = Uri.encodeComponent(parsed.packageName);
     return hostedUrl.resolve('api/packages/$package');
   }
 
   /// Parses [description] into server name component.
   Uri _hostedUrl(description) {
-    final parsed = source._parseDescription(description);
-    return parsed.last;
+    final parsed = source._asDescription(description);
+    return parsed.uri;
   }
 
   /// Retrieves the pubspec for a specific version of a package that is
@@ -535,9 +650,9 @@
   /// package downloaded from that site.
   @override
   String getDirectoryInCache(PackageId id) {
-    var parsed = source._parseDescription(id.description);
-    var dir = _urlToDirectory(parsed.last);
-    return p.join(systemCacheRoot, dir, '${parsed.first}-${id.version}');
+    var parsed = source._asDescription(id.description);
+    var dir = _urlToDirectory(parsed.uri);
+    return p.join(systemCacheRoot, dir, '${parsed.packageName}-${id.version}');
   }
 
   /// Re-downloads all packages that have been previously downloaded into the
@@ -675,8 +790,8 @@
       throw PackageNotFoundException(
           'Package $packageName has no version $version');
     }
-    final parsedDescription = source._parseDescription(id.description);
-    final server = parsedDescription.last;
+    final parsedDescription = source._asDescription(id.description);
+    final server = parsedDescription.uri;
 
     var url = versionInfo.archiveUrl;
     log.io('Get package from $url.');
@@ -713,7 +828,7 @@
   /// this tries to translate into a more user friendly error message.
   ///
   /// Always throws an error, either the original one or a better one.
-  void _throwFriendlyError(
+  Never _throwFriendlyError(
     error,
     StackTrace stackTrace,
     String package,
@@ -809,7 +924,7 @@
   }
 
   /// Returns the server URL for [description].
-  Uri _serverFor(description) => source._parseDescription(description).last;
+  Uri _serverFor(description) => source._asDescription(description).uri;
 
   /// Enables speculative prefetching of dependencies of packages queried with
   /// [getVersions].
@@ -836,9 +951,11 @@
   /// Gets the list of all versions of [ref] that are in the system cache.
   @override
   Future<List<PackageId>> doGetVersions(
-      PackageRef ref, Duration? maxAge) async {
-    var parsed = source._parseDescription(ref.description);
-    var server = parsed.last;
+    PackageRef ref,
+    Duration? maxAge,
+  ) async {
+    var parsed = source._asDescription(ref.description);
+    var server = parsed.uri;
     log.io('Finding versions of ${ref.name} in '
         '$systemCacheRoot/${_urlToDirectory(server)}');
 
diff --git a/lib/src/source/path.dart b/lib/src/source/path.dart
index 545eae8..0401ebd 100644
--- a/lib/src/source/path.dart
+++ b/lib/src/source/path.dart
@@ -9,6 +9,7 @@
 
 import '../exceptions.dart';
 import '../io.dart';
+import '../language_version.dart';
 import '../package_name.dart';
 import '../pubspec.dart';
 import '../source.dart';
@@ -62,7 +63,12 @@
   /// original path but resolved relative to the containing path. The
   /// "relative" key will be `true` if the original path was relative.
   @override
-  PackageRef parseRef(String name, description, {String? containingPath}) {
+  PackageRef parseRef(
+    String name,
+    description, {
+    String? containingPath,
+    LanguageVersion? languageVersion,
+  }) {
     if (description is! String) {
       throw FormatException('The description must be a path string.');
     }
diff --git a/lib/src/source/sdk.dart b/lib/src/source/sdk.dart
index 3ff93fe..f1dde9e 100644
--- a/lib/src/source/sdk.dart
+++ b/lib/src/source/sdk.dart
@@ -7,6 +7,7 @@
 import 'package:pub_semver/pub_semver.dart';
 
 import '../exceptions.dart';
+import '../language_version.dart';
 import '../package_name.dart';
 import '../pubspec.dart';
 import '../sdk.dart';
@@ -33,7 +34,8 @@
 
   /// Parses an SDK dependency.
   @override
-  PackageRef parseRef(String name, description, {String? containingPath}) {
+  PackageRef parseRef(String name, description,
+      {String? containingPath, LanguageVersion? languageVersion}) {
     if (description is! String) {
       throw FormatException('The description must be an SDK name.');
     }
diff --git a/lib/src/source/unknown.dart b/lib/src/source/unknown.dart
index eaf94fd..baa46ea 100644
--- a/lib/src/source/unknown.dart
+++ b/lib/src/source/unknown.dart
@@ -6,6 +6,7 @@
 
 import 'package:pub_semver/pub_semver.dart';
 
+import '../language_version.dart';
 import '../package_name.dart';
 import '../pubspec.dart';
 import '../source.dart';
@@ -42,7 +43,12 @@
   int hashDescription(description) => description.hashCode;
 
   @override
-  PackageRef parseRef(String name, description, {String? containingPath}) =>
+  PackageRef parseRef(
+    String name,
+    description, {
+    String? containingPath,
+    LanguageVersion? languageVersion,
+  }) =>
       PackageRef(name, this, description);
 
   @override
diff --git a/pubspec.yaml b/pubspec.yaml
index 3bfe095..8c76708 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,7 +6,7 @@
 dependencies:
   # Note: Pub's test infrastructure assumes that any dependencies used in tests
   # will be hosted dependencies.
-  analyzer: ^1.5.0
+  analyzer: ^2.7.0
   args: ^2.1.0
   async: ^2.6.1
   cli_util: ^0.3.5
diff --git a/test/cache/add/bad_version_test.dart b/test/cache/add/bad_version_test.dart
index e0a57e0..8d5d2e1 100644
--- a/test/cache/add/bad_version_test.dart
+++ b/test/cache/add/bad_version_test.dart
@@ -11,16 +11,10 @@
 
 void main() {
   test('fails if the version constraint cannot be parsed', () {
-    return runPub(args: ['cache', 'add', 'foo', '-v', '1.0'], error: '''
-            Could not parse version "1.0". Unknown text at "1.0".
-            
-            Usage: pub cache add <package> [--version <constraint>] [--all]
-            -h, --help       Print this usage information.
-                --all        Install all matching versions.
-            -v, --version    Version constraint.
-
-            Run "pub help" to see global options.
-            See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
-            ''', exitCode: exit_codes.USAGE);
+    return runPub(
+      args: ['cache', 'add', 'foo', '-v', '1.0'],
+      error: contains('Could not parse version "1.0". Unknown text at "1.0".'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/cache/add/missing_package_arg_test.dart b/test/cache/add/missing_package_arg_test.dart
index 2274992..426b0d8 100644
--- a/test/cache/add/missing_package_arg_test.dart
+++ b/test/cache/add/missing_package_arg_test.dart
@@ -11,16 +11,10 @@
 
 void main() {
   test('fails if no package was given', () {
-    return runPub(args: ['cache', 'add'], error: '''
-            No package to add given.
-            
-            Usage: pub cache add <package> [--version <constraint>] [--all]
-            -h, --help       Print this usage information.
-                --all        Install all matching versions.
-            -v, --version    Version constraint.
-
-            Run "pub help" to see global options.
-            See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
-            ''', exitCode: exit_codes.USAGE);
+    return runPub(
+      args: ['cache', 'add'],
+      error: contains('No package to add given.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/cache/add/unexpected_arguments_test.dart b/test/cache/add/unexpected_arguments_test.dart
index 66174db..7ec0f30 100644
--- a/test/cache/add/unexpected_arguments_test.dart
+++ b/test/cache/add/unexpected_arguments_test.dart
@@ -11,16 +11,10 @@
 
 void main() {
   test('fails if there are extra arguments', () {
-    return runPub(args: ['cache', 'add', 'foo', 'bar', 'baz'], error: '''
-            Unexpected arguments "bar" and "baz".
-            
-            Usage: pub cache add <package> [--version <constraint>] [--all]
-            -h, --help       Print this usage information.
-                --all        Install all matching versions.
-            -v, --version    Version constraint.
-
-            Run "pub help" to see global options.
-            See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
-            ''', exitCode: exit_codes.USAGE);
+    return runPub(
+      args: ['cache', 'add', 'foo', 'bar', 'baz'],
+      error: contains('Unexpected arguments "bar" and "baz".'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/deps/executables_test.dart b/test/deps/executables_test.dart
index f568511..978cc24 100644
--- a/test/deps/executables_test.dart
+++ b/test/deps/executables_test.dart
@@ -4,11 +4,6 @@
 
 // @dart=2.10
 
-import 'package:pub/src/ascii_tree.dart' as tree;
-import 'package:pub/src/io.dart';
-import 'package:test/test.dart';
-
-import '../ascii_tree_test.dart';
 import '../descriptor.dart' as d;
 import '../golden_file.dart';
 import '../test_pub.dart';
@@ -16,31 +11,29 @@
 const _validMain = 'main() {}';
 const _invalidMain = 'main() {';
 
-Future<void> variations(String name) async {
-  final buffer = StringBuffer();
-  buffer.writeln(stripColors(
-      tree.fromFiles(listDir(d.sandbox, recursive: true), baseDir: d.sandbox)));
+extension on GoldenTestContext {
+  Future<void> runExecutablesTest() async {
+    await pubGet();
 
-  await pubGet();
-  await runPubIntoBuffer(['deps', '--executables'], buffer);
-  await runPubIntoBuffer(['deps', '--executables', '--dev'], buffer);
-  // The json ouput also lists the exectuables.
-  await runPubIntoBuffer(['deps', '--json'], buffer);
-  // The easiest way to update the golden files is to delete them and rerun the
-  // test.
-  expectMatchesGoldenFile(buffer.toString(), 'test/deps/goldens/$name.txt');
+    await tree();
+
+    await run(['deps', '--executables']);
+    await run(['deps', '--executables', '--dev']);
+    await run(['deps', '--json']);
+  }
 }
 
 void main() {
-  test('skips non-Dart executables', () async {
+  testWithGolden('skips non-Dart executables', (ctx) async {
     await d.dir(appPath, [
       d.appPubspec(),
       d.dir('bin', [d.file('foo.py'), d.file('bar.sh')])
     ]).create();
-    await variations('non_dart_executables');
+
+    await ctx.runExecutablesTest();
   });
 
-  test('lists Dart executables, even without entrypoints', () async {
+  testWithGolden('lists Dart executables, without entrypoints', (ctx) async {
     await d.dir(appPath, [
       d.appPubspec(),
       d.dir(
@@ -48,10 +41,11 @@
         [d.file('foo.dart', _validMain), d.file('bar.dart', _invalidMain)],
       )
     ]).create();
-    await variations('dart_executables');
+
+    await ctx.runExecutablesTest();
   });
 
-  test('skips executables in sub directories', () async {
+  testWithGolden('skips executables in sub directories', (ctx) async {
     await d.dir(appPath, [
       d.appPubspec(),
       d.dir('bin', [
@@ -59,10 +53,11 @@
         d.dir('sub', [d.file('bar.dart', _validMain)])
       ])
     ]).create();
-    await variations('nothing_in_sub_drectories');
+
+    await ctx.runExecutablesTest();
   });
 
-  test('lists executables from a dependency', () async {
+  testWithGolden('lists executables from a dependency', (ctx) async {
     await d.dir('foo', [
       d.libPubspec('foo', '1.0.0'),
       d.dir('bin', [d.file('bar.dart', _validMain)])
@@ -74,10 +69,11 @@
       })
     ]).create();
 
-    await variations('from_dependency');
+    await ctx.runExecutablesTest();
   });
 
-  test('lists executables only from immediate dependencies', () async {
+  testWithGolden('lists executables only from immediate dependencies',
+      (ctx) async {
     await d.dir(appPath, [
       d.appPubspec({
         'foo': {'path': '../foo'}
@@ -96,10 +92,10 @@
       d.dir('bin', [d.file('qux.dart', _validMain)])
     ]).create();
 
-    await variations('only_immediate');
+    await ctx.runExecutablesTest();
   });
 
-  test('applies formatting before printing executables', () async {
+  testWithGolden('applies formatting before printing executables', (ctx) async {
     await d.dir(appPath, [
       d.appPubspec({
         'foo': {'path': '../foo'},
@@ -119,10 +115,10 @@
       d.dir('bin', [d.file('qux.dart', _validMain)])
     ]).create();
 
-    await variations('formatting');
+    await ctx.runExecutablesTest();
   });
 
-  test('dev dependencies', () async {
+  testWithGolden('dev dependencies', (ctx) async {
     await d.dir('foo', [
       d.libPubspec('foo', '1.0.0'),
       d.dir('bin', [d.file('bar.dart', _validMain)])
@@ -136,10 +132,11 @@
         }
       })
     ]).create();
-    await variations('dev_dependencies');
+
+    await ctx.runExecutablesTest();
   });
 
-  test('overriden dependencies executables', () async {
+  testWithGolden('overriden dependencies executables', (ctx) async {
     await d.dir('foo-1.0', [
       d.libPubspec('foo', '1.0.0'),
       d.dir('bin', [d.file('bar.dart', _validMain)])
@@ -162,6 +159,7 @@
         }
       })
     ]).create();
-    await variations('overrides');
+
+    await ctx.runExecutablesTest();
   });
 }
diff --git a/test/deps/goldens/dart_executables.txt b/test/deps/goldens/dart_executables.txt
deleted file mode 100644
index 9284ceb..0000000
--- a/test/deps/goldens/dart_executables.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-'-- myapp
-    |-- bin
-    |   |-- bar.dart
-    |   '-- foo.dart
-    '-- pubspec.yaml
-
-$ pub deps --executables
-myapp: bar, foo
-
-$ pub deps --executables --dev
-myapp: bar, foo
-
-$ pub deps --json
-{
-  "root": "myapp",
-  "packages": [
-    {
-      "name": "myapp",
-      "version": "0.0.0",
-      "kind": "root",
-      "source": "root",
-      "dependencies": []
-    }
-  ],
-  "sdks": [
-    {
-      "name": "Dart",
-      "version": "0.1.2+3"
-    }
-  ],
-  "executables": [
-    ":bar",
-    ":foo"
-  ]
-}
-
diff --git a/test/deps/goldens/from_dependency.txt b/test/deps/goldens/from_dependency.txt
deleted file mode 100644
index 30836e5..0000000
--- a/test/deps/goldens/from_dependency.txt
+++ /dev/null
@@ -1,45 +0,0 @@
-|-- foo
-|   |-- bin
-|   |   '-- bar.dart
-|   '-- pubspec.yaml
-'-- myapp
-    '-- pubspec.yaml
-
-$ pub deps --executables
-foo:bar
-
-$ pub deps --executables --dev
-foo:bar
-
-$ pub deps --json
-{
-  "root": "myapp",
-  "packages": [
-    {
-      "name": "myapp",
-      "version": "0.0.0",
-      "kind": "root",
-      "source": "root",
-      "dependencies": [
-        "foo"
-      ]
-    },
-    {
-      "name": "foo",
-      "version": "1.0.0",
-      "kind": "direct",
-      "source": "path",
-      "dependencies": []
-    }
-  ],
-  "sdks": [
-    {
-      "name": "Dart",
-      "version": "0.1.2+3"
-    }
-  ],
-  "executables": [
-    "foo:bar"
-  ]
-}
-
diff --git a/test/deps/goldens/non_dart_executables.txt b/test/deps/goldens/non_dart_executables.txt
deleted file mode 100644
index 646e21e..0000000
--- a/test/deps/goldens/non_dart_executables.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-'-- myapp
-    |-- bin
-    |   |-- bar.sh
-    |   '-- foo.py
-    '-- pubspec.yaml
-
-$ pub deps --executables
-
-$ pub deps --executables --dev
-
-$ pub deps --json
-{
-  "root": "myapp",
-  "packages": [
-    {
-      "name": "myapp",
-      "version": "0.0.0",
-      "kind": "root",
-      "source": "root",
-      "dependencies": []
-    }
-  ],
-  "sdks": [
-    {
-      "name": "Dart",
-      "version": "0.1.2+3"
-    }
-  ],
-  "executables": []
-}
-
diff --git a/test/deps/goldens/nothing_in_sub_drectories.txt b/test/deps/goldens/nothing_in_sub_drectories.txt
deleted file mode 100644
index db3c77c..0000000
--- a/test/deps/goldens/nothing_in_sub_drectories.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-'-- myapp
-    |-- bin
-    |   |-- foo.dart
-    |   '-- sub
-    |       '-- bar.dart
-    '-- pubspec.yaml
-
-$ pub deps --executables
-myapp:foo
-
-$ pub deps --executables --dev
-myapp:foo
-
-$ pub deps --json
-{
-  "root": "myapp",
-  "packages": [
-    {
-      "name": "myapp",
-      "version": "0.0.0",
-      "kind": "root",
-      "source": "root",
-      "dependencies": []
-    }
-  ],
-  "sdks": [
-    {
-      "name": "Dart",
-      "version": "0.1.2+3"
-    }
-  ],
-  "executables": [
-    ":foo"
-  ]
-}
-
diff --git a/test/directory_option_test.dart b/test/directory_option_test.dart
index c993200..541ddd4 100644
--- a/test/directory_option_test.dart
+++ b/test/directory_option_test.dart
@@ -8,14 +8,14 @@
 
 import 'package:path/path.dart' as p;
 import 'package:shelf/shelf.dart' as shelf;
-import 'package:test/test.dart';
 
 import 'descriptor.dart';
 import 'golden_file.dart';
 import 'test_pub.dart';
 
 Future<void> main() async {
-  test('commands taking a --directory/-C parameter work', () async {
+  testWithGolden('commands taking a --directory/-C parameter work',
+      (ctx) async {
     await servePackages((b) => b
       ..serve('foo', '1.0.0')
       ..serve('foo', '0.1.2')
@@ -24,10 +24,11 @@
     globalPackageServer
         .extraHandlers[RegExp('/api/packages/test_pkg/uploaders')] = (request) {
       return shelf.Response.ok(
-          jsonEncode({
-            'success': {'message': 'Good job!'}
-          }),
-          headers: {'content-type': 'application/json'});
+        jsonEncode({
+          'success': {'message': 'Good job!'}
+        }),
+        headers: {'content-type': 'application/json'},
+      );
     };
 
     await validPackage.create();
@@ -56,42 +57,34 @@
         })
       ]),
     ]).create();
-    final buffer = StringBuffer();
-    Future<void> run(List<String> args) async {
-      await runPubIntoBuffer(
-        args,
-        buffer,
+
+    final cases = [
+      // Try --directory after command.
+      ['add', '--directory=$appPath', 'foo'],
+      // Try the top-level version also.
+      ['-C', appPath, 'add', 'bar'],
+      // When both top-level and after command, the one after command takes
+      // precedence.
+      ['-C', p.join(appPath, 'example'), 'get', '--directory=$appPath', 'bar'],
+      ['remove', 'bar', '-C', appPath],
+      ['get', 'bar', '-C', appPath],
+      ['get', 'bar', '-C', '$appPath/example'],
+      ['get', 'bar', '-C', '$appPath/example2'],
+      ['get', 'bar', '-C', '$appPath/broken_dir'],
+      ['downgrade', '-C', appPath],
+      ['upgrade', 'bar', '-C', appPath],
+      ['run', '-C', appPath, 'bin/app.dart'],
+      ['publish', '-C', appPath, '--dry-run'],
+      ['uploader', '-C', appPath, 'add', 'sigurdm@google.com'],
+      ['deps', '-C', appPath],
+    ];
+
+    for (var i = 0; i < cases.length; i++) {
+      await ctx.run(
+        cases[i],
         workingDirectory: sandbox,
         environment: {'_PUB_TEST_SDK_VERSION': '1.12.0'},
       );
     }
-
-    await run(['add', '--directory=$appPath', 'foo']);
-    // Try the top-level version also.
-    await run(['-C', appPath, 'add', 'bar']);
-    // When both top-level and after command, the one after command takes
-    // precedence.
-    await run([
-      '-C',
-      p.join(appPath, 'example'),
-      'get',
-      '--directory=$appPath',
-      'bar',
-    ]);
-    await run(['remove', 'bar', '-C', appPath]);
-    await run(['get', 'bar', '-C', appPath]);
-    await run(['get', 'bar', '-C', '$appPath/example']);
-    await run(['get', 'bar', '-C', '$appPath/example2']);
-    await run(['get', 'bar', '-C', '$appPath/broken_dir']);
-    await run(['downgrade', '-C', appPath]);
-    await run(['upgrade', 'bar', '-C', appPath]);
-    await run(['run', '-C', appPath, 'bin/app.dart']);
-    await run(['publish', '-C', appPath, '--dry-run']);
-    await run(['uploader', '-C', appPath, 'add', 'sigurdm@google.com']);
-    await run(['deps', '-C', appPath]);
-    // TODO(sigurdm): we should also test `list-package-dirs` - it is a bit
-    // hard on windows due to quoted back-slashes on windows.
-    expectMatchesGoldenFile(
-        buffer.toString(), 'test/goldens/directory_option.txt');
   });
 }
diff --git a/test/embedding/embedding_test.dart b/test/embedding/embedding_test.dart
index dbe6c89..b5cf8bd 100644
--- a/test/embedding/embedding_test.dart
+++ b/test/embedding/embedding_test.dart
@@ -19,10 +19,13 @@
 String snapshot;
 
 /// Runs `dart tool/test-bin/pub_command_runner.dart [args]` and appends the output to [buffer].
-Future<void> runEmbedding(List<String> args, StringBuffer buffer,
-    {String workingDirextory,
-    Map<String, String> environment,
-    dynamic exitCode = 0}) async {
+Future<void> runEmbeddingToBuffer(
+  List<String> args,
+  StringBuffer buffer, {
+  String workingDirextory,
+  Map<String, String> environment,
+  dynamic exitCode = 0,
+}) async {
   final process = await TestProcess.start(
     Platform.resolvedExecutable,
     ['--enable-asserts', snapshot, ...args],
@@ -45,6 +48,28 @@
   buffer.write('\n');
 }
 
+extension on GoldenTestContext {
+  /// Runs `dart tool/test-bin/pub_command_runner.dart [args]` and compare to
+  /// next section in golden file.
+  Future<void> runEmbedding(
+    List<String> args, {
+    String workingDirextory,
+    Map<String, String> environment,
+    dynamic exitCode = 0,
+  }) async {
+    final buffer = StringBuffer();
+    await runEmbeddingToBuffer(
+      args,
+      buffer,
+      workingDirextory: workingDirextory,
+      environment: environment,
+      exitCode: exitCode,
+    );
+
+    expectNextSection(buffer.toString());
+  }
+}
+
 Future<void> main() async {
   setUpAll(() async {
     final tempDir = Directory.systemTemp.createTempSync();
@@ -57,20 +82,8 @@
   tearDownAll(() {
     File(snapshot).parent.deleteSync(recursive: true);
   });
-  test('help text', () async {
-    final buffer = StringBuffer();
-    await runEmbedding([''], buffer, exitCode: 64);
-    await runEmbedding(['--help'], buffer);
-    await runEmbedding(['pub'], buffer, exitCode: 64);
-    await runEmbedding(['pub', '--help'], buffer);
-    await runEmbedding(['pub', 'get', '--help'], buffer);
-    await runEmbedding(['pub', 'global'], buffer, exitCode: 64);
-    expectMatchesGoldenFile(
-        buffer.toString(), 'test/embedding/goldens/helptext.txt');
-  });
 
-  test('run works, though hidden', () async {
-    final buffer = StringBuffer();
+  testWithGolden('run works, though hidden', (ctx) async {
     await d.dir(appPath, [
       d.pubspec({
         'name': 'myapp',
@@ -88,21 +101,15 @@
 ''')
       ]),
     ]).create();
-    await runEmbedding(
+    await ctx.runEmbedding(
       ['pub', 'get'],
-      buffer,
       workingDirextory: d.path(appPath),
     );
-    await runEmbedding(
+    await ctx.runEmbedding(
       ['pub', 'run', 'bin/main.dart'],
-      buffer,
       exitCode: 123,
       workingDirextory: d.path(appPath),
     );
-    expectMatchesGoldenFile(
-      buffer.toString(),
-      'test/embedding/goldens/run.txt',
-    );
   });
 
   test('analytics', () async {
@@ -126,7 +133,7 @@
 
     final buffer = StringBuffer();
 
-    await runEmbedding(
+    await runEmbeddingToBuffer(
       ['pub', 'get'],
       buffer,
       workingDirextory: app.io.path,
diff --git a/test/embedding/get_executable_for_command_test.dart b/test/embedding/get_executable_for_command_test.dart
index e174f3a..7667e12 100644
--- a/test/embedding/get_executable_for_command_test.dart
+++ b/test/embedding/get_executable_for_command_test.dart
@@ -9,15 +9,26 @@
 import 'package:path/path.dart' show separator;
 import 'package:path/path.dart' as p;
 import 'package:pub/pub.dart';
+import 'package:pub/src/log.dart' as log;
+
 import 'package:test/test.dart';
 
 import '../descriptor.dart' as d;
 import '../test_pub.dart';
 
-Future<void> testGetExecutable(String command, String root,
-    {allowSnapshot = true, result, errorMessage}) async {
+Future<void> testGetExecutable(
+  String command,
+  String root, {
+  allowSnapshot = true,
+  executable,
+  packageConfig,
+  errorMessage,
+  CommandResolutionIssue issue,
+}) async {
   final _cachePath = getPubTestEnvironment()['PUB_CACHE'];
-  if (result == null) {
+  final oldVerbosity = log.verbosity;
+  log.verbosity = log.Verbosity.NONE;
+  if (executable == null) {
     expect(
       () => getExecutableForCommand(
         command,
@@ -27,18 +38,25 @@
       ),
       throwsA(
         isA<CommandResolutionFailedException>()
-            .having((e) => e.message, 'message', errorMessage),
+            .having((e) => e.message, 'message', errorMessage)
+            .having((e) => e.issue, 'issue', issue),
       ),
     );
   } else {
-    final path = await getExecutableForCommand(
+    final e = await getExecutableForCommand(
       command,
       root: root,
       pubCacheDir: _cachePath,
       allowSnapshot: allowSnapshot,
     );
-    expect(path, result);
-    expect(File(p.join(root, path)).existsSync(), true);
+    expect(
+      e,
+      isA<DartExecutableWithPackageConfig>()
+          .having((e) => e.executable, 'executable', executable)
+          .having((e) => e.packageConfig, 'packageConfig', packageConfig),
+    );
+    expect(File(p.join(root, e.executable)).existsSync(), true);
+    log.verbosity = oldVerbosity;
   }
 }
 
@@ -50,13 +68,13 @@
     final dir = d.path('foo');
 
     await testGetExecutable('bar/bar.dart', dir,
-        result: p.join('bar', 'bar.dart'));
+        executable: p.join('bar', 'bar.dart'));
 
     await testGetExecutable(p.join('bar', 'bar.dart'), dir,
-        result: p.join('bar', 'bar.dart'));
+        executable: p.join('bar', 'bar.dart'));
 
     await testGetExecutable('${p.toUri(dir)}/bar/bar.dart', dir,
-        result: p.join('bar', 'bar.dart'));
+        executable: p.join('bar', 'bar.dart'));
   });
 
   test('Looks for file when no pubspec.yaml', () async {
@@ -66,9 +84,11 @@
     final dir = d.path('foo');
 
     await testGetExecutable('bar/m.dart', dir,
-        errorMessage: contains('Could not find file `bar/m.dart`'));
+        errorMessage: contains('Could not find file `bar/m.dart`'),
+        issue: CommandResolutionIssue.fileNotFound);
     await testGetExecutable(p.join('bar', 'm.dart'), dir,
-        errorMessage: contains('Could not find file `bar${separator}m.dart`'));
+        errorMessage: contains('Could not find file `bar${separator}m.dart`'),
+        issue: CommandResolutionIssue.fileNotFound);
   });
 
   test('Error message when pubspec is broken', () async {
@@ -95,13 +115,15 @@
             contains(
                 'Error on line 1, column 9 of ${d.sandbox}${p.separator}foo${p.separator}pubspec.yaml: "name" field must be a valid Dart identifier.'),
             contains(
-                '{"name":"broken name","environment":{"sdk":">=0.1.2 <1.0.0"}}')));
+                '{"name":"broken name","environment":{"sdk":">=0.1.2 <1.0.0"}}')),
+        issue: CommandResolutionIssue.pubGetFailed);
   });
 
   test('Does `pub get` if there is a pubspec.yaml', () async {
     await d.dir(appPath, [
       d.pubspec({
         'name': 'myapp',
+        'environment': {'sdk': '>=$_currentVersion <3.0.0'},
         'dependencies': {'foo': '^1.0.0'}
       }),
       d.dir('bin', [
@@ -112,8 +134,49 @@
     await serveNoPackages();
     // The solver uses word-wrapping in its error message, so we use \s to
     // accomodate.
-    await testGetExecutable('bar/m.dart', d.path(appPath),
-        errorMessage: matches(r'version\s+solving\s+failed'));
+    await testGetExecutable(
+      'bar/m.dart',
+      d.path(appPath),
+      errorMessage: matches(r'version\s+solving\s+failed'),
+      issue: CommandResolutionIssue.pubGetFailed,
+    );
+  });
+
+  test('Reports parse failure', () async {
+    await d.dir(appPath, [
+      d.pubspec({
+        'name': 'myapp',
+        'environment': {'sdk': '>=$_currentVersion <3.0.0'},
+      }),
+    ]).create();
+    await testGetExecutable(
+      '::',
+      d.path(appPath),
+      errorMessage: contains(r'cannot contain multiple ":"'),
+      issue: CommandResolutionIssue.parseError,
+    );
+  });
+
+  test('Reports compilation failure', () async {
+    await d.dir(appPath, [
+      d.pubspec({
+        'name': 'myapp',
+        'environment': {'sdk': '>=$_currentVersion <3.0.0'},
+      }),
+      d.dir('bin', [
+        d.file('foo.dart', 'main() {'),
+      ])
+    ]).create();
+
+    await serveNoPackages();
+    // The solver uses word-wrapping in its error message, so we use \s to
+    // accomodate.
+    await testGetExecutable(
+      ':foo',
+      d.path(appPath),
+      errorMessage: matches(r'foo.dart:1:8:'),
+      issue: CommandResolutionIssue.compilationFailed,
+    );
   });
 
   test('Finds files', () async {
@@ -148,46 +211,81 @@
     ]).create();
     final dir = d.path(appPath);
 
-    await testGetExecutable('myapp', dir,
-        result: p.join('.dart_tool', 'pub', 'bin', 'myapp',
-            'myapp.dart-$_currentVersion.snapshot'));
-    await testGetExecutable('myapp:myapp', dir,
-        result: p.join('.dart_tool', 'pub', 'bin', 'myapp',
-            'myapp.dart-$_currentVersion.snapshot'));
-    await testGetExecutable(':myapp', dir,
-        result: p.join('.dart_tool', 'pub', 'bin', 'myapp',
-            'myapp.dart-$_currentVersion.snapshot'));
-    await testGetExecutable(':tool', dir,
-        result: p.join('.dart_tool', 'pub', 'bin', 'myapp',
-            'tool.dart-$_currentVersion.snapshot'));
-    await testGetExecutable('foo', dir,
-        allowSnapshot: false,
-        result: endsWith('foo-1.0.0${separator}bin${separator}foo.dart'));
-    await testGetExecutable('foo', dir,
-        result:
-            '.dart_tool${separator}pub${separator}bin${separator}foo${separator}foo.dart-$_currentVersion.snapshot');
-    await testGetExecutable('foo:tool', dir,
-        allowSnapshot: false,
-        result: endsWith('foo-1.0.0${separator}bin${separator}tool.dart'));
-    await testGetExecutable('foo:tool', dir,
-        result:
-            '.dart_tool${separator}pub${separator}bin${separator}foo${separator}tool.dart-$_currentVersion.snapshot');
+    await testGetExecutable(
+      'myapp',
+      dir,
+      executable: p.join('.dart_tool', 'pub', 'bin', 'myapp',
+          'myapp.dart-$_currentVersion.snapshot'),
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      'myapp:myapp',
+      dir,
+      executable: p.join('.dart_tool', 'pub', 'bin', 'myapp',
+          'myapp.dart-$_currentVersion.snapshot'),
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      ':myapp',
+      dir,
+      executable: p.join('.dart_tool', 'pub', 'bin', 'myapp',
+          'myapp.dart-$_currentVersion.snapshot'),
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      ':tool',
+      dir,
+      executable: p.join('.dart_tool', 'pub', 'bin', 'myapp',
+          'tool.dart-$_currentVersion.snapshot'),
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      'foo',
+      dir,
+      allowSnapshot: false,
+      executable: endsWith('foo-1.0.0${separator}bin${separator}foo.dart'),
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      'foo',
+      dir,
+      executable:
+          '.dart_tool${separator}pub${separator}bin${separator}foo${separator}foo.dart-$_currentVersion.snapshot',
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      'foo:tool',
+      dir,
+      allowSnapshot: false,
+      executable: endsWith('foo-1.0.0${separator}bin${separator}tool.dart'),
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
+    await testGetExecutable(
+      'foo:tool',
+      dir,
+      executable:
+          '.dart_tool${separator}pub${separator}bin${separator}foo${separator}tool.dart-$_currentVersion.snapshot',
+      packageConfig: p.join('.dart_tool', 'package_config.json'),
+    );
     await testGetExecutable(
       'unknown:tool',
       dir,
       errorMessage: 'Could not find package `unknown` or file `unknown:tool`',
+      issue: CommandResolutionIssue.packageNotFound,
     );
     await testGetExecutable(
       'foo:unknown',
       dir,
       errorMessage:
           'Could not find `bin${separator}unknown.dart` in package `foo`.',
+      issue: CommandResolutionIssue.noBinaryFound,
     );
     await testGetExecutable(
       'unknownTool',
       dir,
       errorMessage:
           'Could not find package `unknownTool` or file `unknownTool`',
+      issue: CommandResolutionIssue.packageNotFound,
     );
   });
 }
diff --git a/test/embedding/goldens/helptext.txt b/test/embedding/goldens/helptext.txt
deleted file mode 100644
index b5c206f..0000000
--- a/test/embedding/goldens/helptext.txt
+++ /dev/null
@@ -1,113 +0,0 @@
-$ tool/test-bin/pub_command_runner.dart 
-[E] Could not find a command named "".
-[E] 
-[E] Usage: pub_command_runner <command> [arguments]
-[E] 
-[E] Global options:
-[E] -h, --help    Print this usage information.
-[E] 
-[E] Available commands:
-[E]   pub   Work with packages.
-[E] 
-[E] Run "pub_command_runner help <command>" for more information about a command.
-
-$ tool/test-bin/pub_command_runner.dart --help
-Tests the embeddable pub command.
-
-Usage: pub_command_runner <command> [arguments]
-
-Global options:
--h, --help    Print this usage information.
-
-Available commands:
-  pub   Work with packages.
-
-Run "pub_command_runner help <command>" for more information about a command.
-
-$ tool/test-bin/pub_command_runner.dart pub
-[E] Missing subcommand for "pub_command_runner pub".
-[E] 
-[E] Usage: pub_command_runner pub [arguments...]
-[E] -h, --help               Print this usage information.
-[E]     --[no-]trace         Print debugging information when an error occurs.
-[E] -v, --verbose            Shortcut for "--verbosity=all".
-[E] -C, --directory=<dir>    Run the subcommand in the directory<dir>.
-[E]                          (defaults to ".")
-[E] 
-[E] Available subcommands:
-[E]   add         Add a dependency to pubspec.yaml.
-[E]   cache       Work with the system cache.
-[E]   deps        Print package dependencies.
-[E]   downgrade   Downgrade the current package's dependencies to oldest versions.
-[E]   get         Get the current package's dependencies.
-[E]   global      Work with global packages.
-[E]   login       Log into pub.dev.
-[E]   logout      Log out of pub.dev.
-[E]   outdated    Analyze your dependencies to find which ones can be upgraded.
-[E]   publish     Publish the current package to pub.dartlang.org.
-[E]   remove      Removes a dependency from the current package.
-[E]   token       Manage authentication tokens for hosted pub repositories.
-[E]   upgrade     Upgrade the current package's dependencies to latest versions.
-[E]   uploader    Manage uploaders for a package on pub.dartlang.org.
-[E] 
-[E] Run "pub_command_runner help" to see global options.
-[E] See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation.
-
-$ tool/test-bin/pub_command_runner.dart pub --help
-Work with packages.
-
-Usage: pub_command_runner pub [arguments...]
--h, --help               Print this usage information.
-    --[no-]trace         Print debugging information when an error occurs.
--v, --verbose            Shortcut for "--verbosity=all".
--C, --directory=<dir>    Run the subcommand in the directory<dir>.
-                         (defaults to ".")
-
-Available subcommands:
-  add         Add a dependency to pubspec.yaml.
-  cache       Work with the system cache.
-  deps        Print package dependencies.
-  downgrade   Downgrade the current package's dependencies to oldest versions.
-  get         Get the current package's dependencies.
-  global      Work with global packages.
-  login       Log into pub.dev.
-  logout      Log out of pub.dev.
-  outdated    Analyze your dependencies to find which ones can be upgraded.
-  publish     Publish the current package to pub.dartlang.org.
-  remove      Removes a dependency from the current package.
-  token       Manage authentication tokens for hosted pub repositories.
-  upgrade     Upgrade the current package's dependencies to latest versions.
-  uploader    Manage uploaders for a package on pub.dartlang.org.
-
-Run "pub_command_runner help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation.
-
-$ tool/test-bin/pub_command_runner.dart pub get --help
-Get the current package's dependencies.
-
-Usage: pub_command_runner pub get <subcommand> [arguments...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
--C, --directory=<dir>    Run this in the directory<dir>.
-
-Run "pub_command_runner help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-get for detailed documentation.
-
-$ tool/test-bin/pub_command_runner.dart pub global
-[E] Missing subcommand for "pub_command_runner pub global".
-[E] 
-[E] Usage: pub_command_runner pub global [arguments...]
-[E] -h, --help    Print this usage information.
-[E] 
-[E] Available subcommands:
-[E]   activate     Make a package's executables globally available.
-[E]   deactivate   Remove a previously activated package.
-[E]   list         List globally activated packages.
-[E]   run          Run an executable from a globally activated package.
-[E] 
-[E] Run "pub_command_runner help" to see global options.
-[E] See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation.
-
diff --git a/test/embedding/goldens/run.txt b/test/embedding/goldens/run.txt
deleted file mode 100644
index 6f0f46a..0000000
--- a/test/embedding/goldens/run.txt
+++ /dev/null
@@ -1,7 +0,0 @@
-$ tool/test-bin/pub_command_runner.dart pub get
-Resolving dependencies...
-Got dependencies!
-
-$ tool/test-bin/pub_command_runner.dart pub run bin/main.dart
-Hi
-
diff --git a/test/get/hosted/warn_about_discontinued_test.dart b/test/get/hosted/warn_about_discontinued_test.dart
index 3913b2c..76558a0 100644
--- a/test/get/hosted/warn_about_discontinued_test.dart
+++ b/test/get/hosted/warn_about_discontinued_test.dart
@@ -15,7 +15,7 @@
 import '../../test_pub.dart';
 
 void main() {
-  test('Warns about discontinued packages', () async {
+  test('Warns about discontinued dependencies', () async {
     await servePackages((builder) => builder
       ..serve('foo', '1.2.3', deps: {'transitive': 'any'})
       ..serve('transitive', '1.0.0'));
@@ -40,7 +40,6 @@
     await pubGet(output: '''
 Resolving dependencies...
   foo 1.2.3 (discontinued)
-  transitive 1.0.0 (discontinued)
 Got dependencies!
 ''');
     expect(fileExists(fooVersionsCache), isTrue);
@@ -54,7 +53,6 @@
     await pubGet(output: '''
 Resolving dependencies...
   foo 1.2.3 (discontinued replaced by bar)
-  transitive 1.0.0 (discontinued)
 Got dependencies!''');
     final c2 = json.decode(readTextFile(fooVersionsCache));
     // Make a bad cached value to test that responses are actually from cache.
@@ -62,14 +60,12 @@
     writeTextFile(fooVersionsCache, json.encode(c2));
     await pubGet(output: '''
 Resolving dependencies...
-  transitive 1.0.0 (discontinued)
 Got dependencies!''');
     // Repairing the cache should reset the package listing caches.
     await runPub(args: ['cache', 'repair']);
     await pubGet(output: '''
 Resolving dependencies...
   foo 1.2.3 (discontinued replaced by bar)
-  transitive 1.0.0 (discontinued)
 Got dependencies!''');
     // Test that --offline won't try to access the server for retrieving the
     // status.
@@ -77,7 +73,6 @@
     await pubGet(args: ['--offline'], output: '''
 Resolving dependencies...
   foo 1.2.3 (discontinued replaced by bar)
-  transitive 1.0.0 (discontinued)
 Got dependencies!''');
     deleteEntry(fooVersionsCache);
     deleteEntry(transitiveVersionsCache);
@@ -87,6 +82,79 @@
 ''');
   });
 
+  test('Warns about discontinued dev dependencies', () async {
+    await servePackages((builder) => builder
+      ..serve('foo', '1.2.3', deps: {'transitive': 'any'})
+      ..serve('transitive', '1.0.0'));
+
+    await d.dir(appPath, [
+      d.file('pubspec.yaml', '''
+name: myapp
+dependencies:
+
+dev_dependencies:
+  foo: 1.2.3
+environment:
+  sdk: '>=0.1.2 <1.0.0'
+''')
+    ]).create();
+    await pubGet();
+
+    globalPackageServer.add((builder) => builder
+      ..discontinue('foo')
+      ..discontinue('transitive'));
+    // A pub get straight away will not trigger the warning, as we cache
+    // responses for a while.
+    await pubGet();
+    final fooVersionsCache =
+        p.join(globalPackageServer.cachingPath, '.cache', 'foo-versions.json');
+    expect(fileExists(fooVersionsCache), isTrue);
+    deleteEntry(fooVersionsCache);
+    // We warn only about the direct dependency here:
+    await pubGet(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued)
+Got dependencies!
+''');
+    expect(fileExists(fooVersionsCache), isTrue);
+    final c = json.decode(readTextFile(fooVersionsCache));
+    // Make the cache artificially old.
+    c['_fetchedAt'] =
+        DateTime.now().subtract(Duration(days: 5)).toIso8601String();
+    writeTextFile(fooVersionsCache, json.encode(c));
+    globalPackageServer
+        .add((builder) => builder.discontinue('foo', replacementText: 'bar'));
+    await pubGet(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued replaced by bar)
+Got dependencies!''');
+    final c2 = json.decode(readTextFile(fooVersionsCache));
+    // Make a bad cached value to test that responses are actually from cache.
+    c2['isDiscontinued'] = false;
+    writeTextFile(fooVersionsCache, json.encode(c2));
+    await pubGet(output: '''
+Resolving dependencies...
+Got dependencies!''');
+    // Repairing the cache should reset the package listing caches.
+    await runPub(args: ['cache', 'repair']);
+    await pubGet(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued replaced by bar)
+Got dependencies!''');
+    // Test that --offline won't try to access the server for retrieving the
+    // status.
+    await serveErrors();
+    await pubGet(args: ['--offline'], output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued replaced by bar)
+Got dependencies!''');
+    deleteEntry(fooVersionsCache);
+    await pubGet(args: ['--offline'], output: '''
+Resolving dependencies...
+Got dependencies!
+''');
+  });
+
   test('get does not fail when status listing fails', () async {
     await servePackages((builder) => builder..serve('foo', '1.2.3'));
     await d.appDir({'foo': '1.2.3'}).create();
diff --git a/test/global/activate/activate_hosted_twice_test.dart b/test/global/activate/activate_hosted_twice_test.dart
index 925a205..50ed807 100644
--- a/test/global/activate/activate_hosted_twice_test.dart
+++ b/test/global/activate/activate_hosted_twice_test.dart
@@ -39,7 +39,7 @@
 Package foo is currently active at version 1.0.0.
 Resolving dependencies...
 The package foo is already activated at newest available version.
-To recompile executables, first run `global deactivate foo`.
+To recompile executables, first run `dart pub global deactivate foo`.
 Activated foo 1.0.0.''');
 
     var pub = await pubRun(global: true, args: ['foo']);
diff --git a/test/global/activate/missing_package_arg_test.dart b/test/global/activate/missing_package_arg_test.dart
index 5923bd9..0b747af 100644
--- a/test/global/activate/missing_package_arg_test.dart
+++ b/test/global/activate/missing_package_arg_test.dart
@@ -12,8 +12,9 @@
 void main() {
   test('fails if no package was given', () {
     return runPub(
-        args: ['global', 'activate'],
-        error: contains('No package to activate given.'),
-        exitCode: exit_codes.USAGE);
+      args: ['global', 'activate'],
+      error: contains('No package to activate given.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/global/deactivate/missing_package_arg_test.dart b/test/global/deactivate/missing_package_arg_test.dart
index 7d41b82..6671991 100644
--- a/test/global/deactivate/missing_package_arg_test.dart
+++ b/test/global/deactivate/missing_package_arg_test.dart
@@ -11,13 +11,10 @@
 
 void main() {
   test('fails if no package was given', () {
-    return runPub(args: ['global', 'deactivate'], error: '''
-            No package to deactivate given.
-
-            Usage: pub global deactivate <package>
-            -h, --help    Print this usage information.
-
-            Run "pub help" to see global options.
-            ''', exitCode: exit_codes.USAGE);
+    return runPub(
+      args: ['global', 'deactivate'],
+      error: contains('No package to deactivate given.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/global/deactivate/unexpected_arguments_test.dart b/test/global/deactivate/unexpected_arguments_test.dart
index d5748f2..426b7f8 100644
--- a/test/global/deactivate/unexpected_arguments_test.dart
+++ b/test/global/deactivate/unexpected_arguments_test.dart
@@ -12,13 +12,9 @@
 void main() {
   test('fails if there are extra arguments', () {
     return runPub(
-        args: ['global', 'deactivate', 'foo', 'bar', 'baz'], error: '''
-            Unexpected arguments "bar" and "baz".
-
-            Usage: pub global deactivate <package>
-            -h, --help    Print this usage information.
-
-            Run "pub help" to see global options.
-            ''', exitCode: exit_codes.USAGE);
+      args: ['global', 'deactivate', 'foo', 'bar', 'baz'],
+      error: contains('Unexpected arguments "bar" and "baz".'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/global/run/errors_if_outside_bin_test.dart b/test/global/run/errors_if_outside_bin_test.dart
index 497f26a..e6c15f7 100644
--- a/test/global/run/errors_if_outside_bin_test.dart
+++ b/test/global/run/errors_if_outside_bin_test.dart
@@ -19,20 +19,11 @@
     });
 
     await runPub(args: ['global', 'activate', 'foo']);
-    await runPub(args: ['global', 'run', 'foo:example/script'], error: '''
-Cannot run an executable in a subdirectory of a global package.
-
-Usage: pub global run <package>:<executable> [args...]
--h, --help                              Print this usage information.
-    --[no-]enable-asserts               Enable assert statements.
-    --enable-experiment=<experiment>    Runs the executable in a VM with the
-                                        given experiments enabled. (Will disable
-                                        snapshotting, resulting in slower
-                                        startup).
-    --[no-]sound-null-safety            Override the default null safety
-                                        execution mode.
-
-Run "pub help" to see global options.
-''', exitCode: exit_codes.USAGE);
+    await runPub(
+      args: ['global', 'run', 'foo:example/script'],
+      error: contains(
+          'Cannot run an executable in a subdirectory of a global package.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/global/run/missing_executable_arg_test.dart b/test/global/run/missing_executable_arg_test.dart
index 2bb8ec8..efab258 100644
--- a/test/global/run/missing_executable_arg_test.dart
+++ b/test/global/run/missing_executable_arg_test.dart
@@ -11,20 +11,10 @@
 
 void main() {
   test('fails if no executable was given', () {
-    return runPub(args: ['global', 'run'], error: '''
-Must specify an executable to run.
-
-Usage: pub global run <package>:<executable> [args...]
--h, --help                              Print this usage information.
-    --[no-]enable-asserts               Enable assert statements.
-    --enable-experiment=<experiment>    Runs the executable in a VM with the
-                                        given experiments enabled. (Will disable
-                                        snapshotting, resulting in slower
-                                        startup).
-    --[no-]sound-null-safety            Override the default null safety
-                                        execution mode.
-
-Run "pub help" to see global options.
-''', exitCode: exit_codes.USAGE);
+    return runPub(
+      args: ['global', 'run'],
+      error: contains('Must specify an executable to run.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/golden_file.dart b/test/golden_file.dart
index 055c5c5..90d2340 100644
--- a/test/golden_file.dart
+++ b/test/golden_file.dart
@@ -4,33 +4,210 @@
 
 // @dart=2.10
 
+import 'dart:async';
 import 'dart:io';
 
-import 'package:path/path.dart' as path;
+import 'package:path/path.dart' as p;
+import 'package:pub/src/ascii_tree.dart' as ascii_tree;
+import 'package:pub/src/io.dart';
+import 'package:stack_trace/stack_trace.dart' show Trace;
 import 'package:test/test.dart';
 
-/// Will test [actual] against the contests of the file at [goldenFilePath].
-///
-/// If the file doesn't exist, the file is instead created containing [actual].
-void expectMatchesGoldenFile(String actual, String goldenFilePath) {
-  var goldenFile = File(goldenFilePath);
-  if (goldenFile.existsSync()) {
-    expect(
-        actual, equals(goldenFile.readAsStringSync().replaceAll('\r\n', '\n')),
-        reason: 'goldenFilePath: "$goldenFilePath"');
-  } else {
-    // This enables writing the updated file when run in otherwise hermetic
-    // settings.
-    //
-    // This is to make updating the golden files easier in a bazel environment
-    // See https://docs.bazel.build/versions/2.0.0/user-manual.html#run .
-    final workspaceDirectory =
-        Platform.environment['BUILD_WORKSPACE_DIRECTORY'];
-    if (workspaceDirectory != null) {
-      goldenFile = File(path.join(workspaceDirectory, goldenFilePath));
-    }
-    goldenFile
-      ..createSync(recursive: true)
-      ..writeAsStringSync(actual);
+import 'ascii_tree_test.dart';
+import 'descriptor.dart' as d;
+import 'test_pub.dart';
+
+final _isCI = () {
+  final p = RegExp(r'^1|(?:true)$', caseSensitive: false);
+  final ci = Platform.environment['CI'];
+  return ci != null && ci.isNotEmpty && p.hasMatch(ci);
+}();
+
+/// Find the current `_test.dart` filename invoked from stack-trace.
+String _findCurrentTestFilename() => Trace.current()
+    .frames
+    .lastWhere(
+      (frame) =>
+          frame.uri.isScheme('file') &&
+          p.basename(frame.uri.toFilePath()).endsWith('_test.dart'),
+    )
+    .uri
+    .toFilePath();
+
+class GoldenTestContext {
+  static const _endOfSection = ''
+      '--------------------------------'
+      ' END OF OUTPUT '
+      '---------------------------------\n\n';
+
+  final String _currentTestFile;
+  final String _testName;
+
+  String _goldenFilePath;
+  File _goldenFile;
+  String _header;
+  final _results = <String>[];
+  bool _goldenFileExists;
+  bool _generatedNewData = false; // track if new data is generated
+  int _nextSectionIndex = 0;
+
+  GoldenTestContext._(this._currentTestFile, this._testName) {
+    final rel = p.relative(
+      _currentTestFile.replaceAll(RegExp(r'\.dart$'), ''),
+      from: p.join(p.current, 'test'),
+    );
+    _goldenFilePath = p.join(
+      'test',
+      'testdata',
+      'goldens',
+      rel,
+      // Sanitize the name, and add .txt
+      _testName.replaceAll(RegExp(r'[<>:"/\|?*%#]'), '~') + '.txt',
+    );
+    _goldenFile = File(_goldenFilePath);
+    _header = '# GENERATED BY: ${p.relative(_currentTestFile)}\n\n';
   }
+
+  void _readGoldenFile() {
+    _goldenFileExists = _goldenFile.existsSync();
+
+    // Read the golden file for this test
+    if (_goldenFileExists) {
+      var text = _goldenFile.readAsStringSync().replaceAll('\r\n', '\n');
+      // Strip header line
+      if (text.startsWith('#') && text.contains('\n\n')) {
+        text = text.substring(text.indexOf('\n\n') + 2);
+      }
+      _results.addAll(text.split(_endOfSection));
+    }
+  }
+
+  /// Expect section [sectionIndex] to match [actual].
+  void _expectSection(int sectionIndex, String actual) {
+    if (_goldenFileExists &&
+        _results.length > sectionIndex &&
+        _results[sectionIndex].isNotEmpty) {
+      expect(
+        actual,
+        equals(_results[sectionIndex]),
+        reason: 'Expect matching section $sectionIndex from "$_goldenFilePath"',
+      );
+    } else {
+      while (_results.length <= sectionIndex) {
+        _results.add('');
+      }
+      _results[sectionIndex] = actual;
+      _generatedNewData = true;
+    }
+  }
+
+  void _writeGoldenFile() {
+    // If we generated new data, then we need to write a new file, and fail the
+    // test case, or mark it as skipped.
+    if (_generatedNewData) {
+      // This enables writing the updated file when run in otherwise hermetic
+      // settings.
+      //
+      // This is to make updating the golden files easier in a bazel environment
+      // See https://docs.bazel.build/versions/2.0.0/user-manual.html#run .
+      var goldenFile = _goldenFile;
+      final workspaceDirectory =
+          Platform.environment['BUILD_WORKSPACE_DIRECTORY'];
+      if (workspaceDirectory != null) {
+        goldenFile = File(p.join(workspaceDirectory, _goldenFilePath));
+      }
+      goldenFile
+        ..createSync(recursive: true)
+        ..writeAsStringSync(_header + _results.join(_endOfSection));
+
+      // If running in CI we should fail if the golden file doesn't already
+      // exist, or is missing entries.
+      // This typically happens if we forgot to commit a file to git.
+      if (_isCI) {
+        fail('Missing golden file: "$_goldenFilePath", '
+            'try running tests again and commit the file');
+      } else {
+        // If not running in CI, then we consider the test as skipped, we've
+        // generated the file, but the user should run the tests again.
+        // Or push to CI in which case we'll run the tests again anyways.
+        markTestSkipped(
+          'Generated golden file: "$_goldenFilePath" instead of running test',
+        );
+      }
+    }
+  }
+
+  /// Expect the next section in the golden file to match [actual].
+  ///
+  /// This will create the section if it is missing.
+  ///
+  /// **Warning**: Take care when using this in an async context, sections are
+  /// numbered based on the other in which calls are made. Hence, ensure
+  /// consistent ordering of calls.
+  void expectNextSection(String actual) =>
+      _expectSection(_nextSectionIndex++, actual);
+
+  /// Run `pub` [args] with [environment] variables in [workingDirectory], and
+  /// log stdout/stderr and exitcode to golden file.
+  Future<void> run(
+    List<String> args, {
+    Map<String, String> environment,
+    String workingDirectory,
+  }) async {
+    // Create new section index number (before doing anything async)
+    final sectionIndex = _nextSectionIndex++;
+    final s = StringBuffer();
+    s.writeln('## Section $sectionIndex');
+    await runPubIntoBuffer(
+      args,
+      s,
+      environment: environment,
+      workingDirectory: workingDirectory,
+    );
+
+    _expectSection(sectionIndex, s.toString());
+  }
+
+  /// Log directory tree structure under [directory] to golden file.
+  Future<void> tree([String directory]) async {
+    // Create new section index number (before doing anything async)
+    final sectionIndex = _nextSectionIndex++;
+
+    final target = p.join(d.sandbox, directory ?? '.');
+
+    final s = StringBuffer();
+    s.writeln('## Section $sectionIndex');
+    if (directory != null) {
+      s.writeln('\$ cd $directory');
+    }
+    s.writeln('\$ tree');
+    s.writeln(stripColors(ascii_tree.fromFiles(
+      listDir(target, recursive: true),
+      baseDir: target,
+    )));
+
+    _expectSection(sectionIndex, s.toString());
+  }
+}
+
+/// Create a [test] with [GoldenTestContext] which allows running golden tests.
+///
+/// This will create a golden file containing output of calls to:
+///  * [GoldenTestContext.run]
+///  * [GoldenTestContext.tree]
+///
+/// The golden file with the recorded output will be created at:
+///   `test/testdata/goldens/path/to/myfile_test/<name>.txt`
+/// , when `path/to/myfile_test.dart` is the `_test.dart` file from which this
+/// function is called.
+void testWithGolden(
+  String name,
+  FutureOr<void> Function(GoldenTestContext ctx) fn,
+) {
+  final ctx = GoldenTestContext._(_findCurrentTestFilename(), name);
+  test(name, () async {
+    ctx._readGoldenFile();
+    await fn(ctx);
+    ctx._writeGoldenFile();
+  });
 }
diff --git a/test/goldens/directory_option.txt b/test/goldens/directory_option.txt
deleted file mode 100644
index 356480c..0000000
--- a/test/goldens/directory_option.txt
+++ /dev/null
@@ -1,84 +0,0 @@
-$ pub add --directory=myapp foo
-Resolving dependencies in myapp...
-+ foo 1.0.0
-Changed 1 dependency in myapp!
-
-$ pub -C myapp add bar
-Resolving dependencies in myapp...
-+ bar 1.2.3
-Changed 1 dependency in myapp!
-
-$ pub -C myapp/example get --directory=myapp bar
-Resolving dependencies in myapp...
-Got dependencies in myapp!
-
-$ pub remove bar -C myapp
-Resolving dependencies in myapp...
-These packages are no longer being depended on:
-- bar 1.2.3
-Changed 1 dependency in myapp!
-
-$ pub get bar -C myapp
-Resolving dependencies in myapp...
-Got dependencies in myapp!
-
-$ pub get bar -C myapp/example
-Resolving dependencies in myapp/example...
-+ foo 1.0.0
-+ test_pkg 1.0.0 from path myapp
-Changed 2 dependencies in myapp/example!
-
-$ pub get bar -C myapp/example2
-Resolving dependencies in myapp/example2...
-[ERR] Error on line 1, column 9 of myapp/pubspec.yaml: "name" field doesn't match expected name "myapp".
-[ERR]   ╷
-[ERR] 1 │ {"name":"test_pkg","version":"1.0.0","homepage":"http://pub.dartlang.org","description":"A package, I guess.","environment":{"sdk":">=1.8.0 <=2.0.0"}, dependencies: { foo: ^1.0.0}}
-[ERR]   │         ^^^^^^^^^^
-[ERR]   ╵
-[Exit code] 65
-
-$ pub get bar -C myapp/broken_dir
-[ERR] Could not find a file named "pubspec.yaml" in "$SANDBOX/myapp/broken_dir".
-[Exit code] 66
-
-$ pub downgrade -C myapp
-Resolving dependencies in myapp...
-  foo 1.0.0
-No dependencies changed in myapp.
-
-$ pub upgrade bar -C myapp
-Resolving dependencies in myapp...
-  foo 1.0.0
-No dependencies changed in myapp.
-
-$ pub run -C myapp bin/app.dart
-Building package executable...
-Built test_pkg:app.
-Hi
-
-$ pub publish -C myapp --dry-run
-Publishing test_pkg 1.0.0 to http://localhost:$PORT:
-|-- CHANGELOG.md
-|-- LICENSE
-|-- README.md
-|-- bin
-|   '-- app.dart
-|-- example
-|   '-- pubspec.yaml
-|-- example2
-|   '-- pubspec.yaml
-|-- lib
-|   '-- test_pkg.dart
-'-- pubspec.yaml
-The server may enforce additional checks.
-[ERR] 
-[ERR] Package has 0 warnings.
-
-$ pub uploader -C myapp add sigurdm@google.com
-Good job!
-
-$ pub deps -C myapp
-Dart SDK 1.12.0
-test_pkg 1.0.0
-'-- foo 1.0.0
-
diff --git a/test/goldens/help.txt b/test/goldens/help.txt
deleted file mode 100644
index 10997b4..0000000
--- a/test/goldens/help.txt
+++ /dev/null
@@ -1,386 +0,0 @@
-[command]
-> pub add --help
-[stdout]
-Add a dependency to pubspec.yaml.
-
-Usage: pub add <package>[:<constraint>] [options]
--h, --help               Print this usage information.
--d, --dev                Adds package to the development dependencies instead.
-    --git-url            Git URL of the package
-    --git-ref            Git branch or commit to be retrieved
-    --git-path           Path of git package in repository
-    --hosted-url         URL of package host server
-    --path               Local path
-    --sdk                SDK source for package
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-add for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub build --help
-[stdout]
-Deprecated command
-
-Usage: pub build <subcommand> [arguments...]
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub cache --help
-[stdout]
-Work with the system cache.
-
-Usage: pub cache [arguments...]
--h, --help    Print this usage information.
-
-Available subcommands:
-  add      Install a package.
-  repair   Reinstall cached packages.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub cache add --help
-[stdout]
-Install a package.
-
-Usage: pub cache add <package> [--version <constraint>] [--all]
--h, --help       Print this usage information.
-    --all        Install all matching versions.
--v, --version    Version constraint.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub cache list --help
-[stdout]
-List packages in the system cache.
-
-Usage: pub cache list <subcommand> [arguments...]
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub cache repair --help
-[stdout]
-Reinstall cached packages.
-
-Usage: pub cache repair <subcommand> [arguments...]
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub deps --help
-[stdout]
-Print package dependencies.
-
-Usage: pub deps [arguments...]
--h, --help           Print this usage information.
--s, --style          How output should be displayed.
-                     [compact, tree (default), list]
-    --[no-]dev       Whether to include dev dependencies.
-                     (defaults to on)
-    --executables    List all available executables.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-deps for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub downgrade --help
-[stdout]
-Downgrade the current package's dependencies to oldest versions.
-
-This doesn't modify the lockfile, so it can be reset with "pub get".
-
-Usage: pub downgrade [dependencies...]
--h, --help            Print this usage information.
-    --[no-]offline    Use cached packages instead of accessing the network.
--n, --dry-run         Report what dependencies would change but don't change
-                      any.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-downgrade for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub global --help
-[stdout]
-Work with global packages.
-
-Usage: pub global [arguments...]
--h, --help    Print this usage information.
-
-Available subcommands:
-  activate     Make a package's executables globally available.
-  deactivate   Remove a previously activated package.
-  list         List globally activated packages.
-  run          Run an executable from a globally activated package.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub get --help
-[stdout]
-Get the current package's dependencies.
-
-Usage: pub get <subcommand> [arguments...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-get for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub list-package-dirs --help
-[stdout]
-Print local paths to dependencies.
-
-Usage: pub list-package-dirs
--h, --help      Print this usage information.
-    --format    How output should be displayed.
-                [json]
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub publish --help
-[stdout]
-Publish the current package to pub.dartlang.org.
-
-Usage: pub publish [options]
--h, --help       Print this usage information.
--n, --dry-run    Validate but do not publish the package.
--f, --force      Publish without confirmation if there are no errors.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-lish for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub outdated --help
-[stdout]
-Analyze your dependencies to find which ones can be upgraded.
-
-Usage: pub outdated [options]
--h, --help                         Print this usage information.
-    --[no-]color                   Whether to color the output.
-                                   Defaults to color when connected to a
-                                   terminal, and no-color otherwise.
-    --[no-]dependency-overrides    Show resolutions with `dependency_overrides`.
-                                   (defaults to on)
-    --[no-]dev-dependencies        Take dev dependencies into account.
-                                   (defaults to on)
-    --json                         Output the results using a json format.
-    --mode=<PROPERTY>              Highlight versions with PROPERTY.
-                                   Only packages currently missing that PROPERTY
-                                   will be included unless --show-all.
-                                   [outdated (default), null-safety]
-    --[no-]prereleases             Include prereleases in latest version.
-                                   (defaults to on in --mode=null-safety).
-    --[no-]show-all                Include dependencies that are already
-                                   fullfilling --mode.
-    --[no-]transitive              Show transitive dependencies.
-                                   (defaults to off in --mode=null-safety).
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-outdated for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub remove --help
-[stdout]
-Removes a dependency from the current package.
-
-Usage: pub remove <package>
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-remove for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub run --help
-[stdout]
-Run an executable from a package.
-
-Usage: pub run <executable> [arguments...]
--h, --help                              Print this usage information.
-    --[no-]enable-asserts               Enable assert statements.
-    --enable-experiment=<experiment>    Runs the executable in a VM with the
-                                        given experiments enabled.
-                                        (Will disable snapshotting, resulting in
-                                        slower startup).
-    --[no-]sound-null-safety            Override the default null safety
-                                        execution mode.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub serve --help
-[stdout]
-Deprecated command
-
-Usage: pub serve <subcommand> [arguments...]
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub upgrade --help
-[stdout]
-Upgrade the current package's dependencies to latest versions.
-
-Usage: pub upgrade [dependencies...]
--h, --help               Print this usage information.
-    --[no-]offline       Use cached packages instead of accessing the network.
--n, --dry-run            Report what dependencies would change but don't change
-                         any.
-    --[no-]precompile    Build executables in immediate dependencies.
-    --null-safety        Upgrade constraints in pubspec.yaml to null-safety
-                         versions
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-upgrade for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub uploader --help
-[stdout]
-Manage uploaders for a package on pub.dartlang.org.
-
-Usage: pub uploader [options] {add/remove} <email>
--h, --help       Print this usage information.
-    --package    The package whose uploaders will be modified.
-                 (defaults to the current package)
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-uploader for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub login --help
-[stdout]
-Log into pub.dev.
-
-Usage: pub login
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub logout --help
-[stdout]
-Log out of pub.dev.
-
-Usage: pub logout <subcommand> [arguments...]
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub version --help
-[stdout]
-Print pub version.
-
-Usage: pub version
--h, --help    Print this usage information.
-
-Run "pub help" to see global options.
-[stderr]
-
-[exitCode]
-0
-
diff --git a/test/goldens/usage_exception.txt b/test/goldens/usage_exception.txt
deleted file mode 100644
index 5c7ead7..0000000
--- a/test/goldens/usage_exception.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-[command]
-> pub 
-[stdout]
-Pub is a package manager for Dart.
-
-Usage: pub <command> [arguments]
-
-Global options:
--h, --help             Print this usage information.
-    --version          Print pub version.
-    --[no-]trace       Print debugging information when an error occurs.
-    --verbosity        Control output verbosity.
-
-          [all]        Show all output including internal tracing messages.
-          [error]      Show only errors.
-          [io]         Also show IO operations.
-          [normal]     Show errors, warnings, and user messages.
-          [solver]     Show steps during version resolution.
-          [warning]    Show only errors and warnings.
-
--v, --verbose          Shortcut for "--verbosity=all".
-
-Available commands:
-  add         Add a dependency to pubspec.yaml.
-  cache       Work with the system cache.
-  deps        Print package dependencies.
-  downgrade   Downgrade the current package's dependencies to oldest versions.
-  get         Get the current package's dependencies.
-  global      Work with global packages.
-  login       Log into pub.dev.
-  logout      Log out of pub.dev.
-  outdated    Analyze your dependencies to find which ones can be upgraded.
-  publish     Publish the current package to pub.dartlang.org.
-  remove      Removes a dependency from the current package.
-  run         Run an executable from a package.
-  upgrade     Upgrade the current package's dependencies to latest versions.
-  uploader    Manage uploaders for a package on pub.dartlang.org.
-  version     Print pub version.
-
-Run "pub help <command>" for more information about a command.
-See https://dart.dev/tools/pub/cmd for detailed documentation.
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub global
-[stdout]
-
-[stderr]
-Missing subcommand for "pub global".
-
-Usage: pub global [arguments...]
--h, --help    Print this usage information.
-
-Available subcommands:
-  activate     Make a package's executables globally available.
-  deactivate   Remove a previously activated package.
-  list         List globally activated packages.
-  run          Run an executable from a globally activated package.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation.
-[exitCode]
-64
diff --git a/test/goldens/version.txt b/test/goldens/version.txt
deleted file mode 100644
index a093c93..0000000
--- a/test/goldens/version.txt
+++ /dev/null
@@ -1,17 +0,0 @@
-[command]
-> pub --version
-[stdout]
-Pub 0.1.2+3
-[stderr]
-
-[exitCode]
-0
-
-[command]
-> pub version
-[stdout]
-Pub 0.1.2+3
-[stderr]
-
-[exitCode]
-0
diff --git a/test/help_test.dart b/test/help_test.dart
new file mode 100644
index 0000000..7ef96ff
--- /dev/null
+++ b/test/help_test.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.10
+import 'package:args/command_runner.dart';
+import 'package:pub/src/command_runner.dart' show PubCommandRunner;
+
+import 'golden_file.dart';
+
+/// Extract all commands and subcommands.
+///
+/// Result will be an iterable of lists, illustrated as follows:
+/// ```
+/// [
+///   [pub]
+///   [pub, get]
+///   ...
+/// ]
+/// ```
+Iterable<List<String>> _extractCommands(
+  List<String> parents,
+  Iterable<Command> cmds,
+) sync* {
+  if (parents.isNotEmpty) {
+    yield parents;
+  }
+  // Track that we don't add more than once, we don't want to test aliases
+  final names = <String>{};
+  yield* cmds
+      .where((sub) => !sub.hidden && names.add(sub.name))
+      .map((sub) => _extractCommands(
+            [...parents, sub.name],
+            sub.subcommands.values,
+          ))
+      .expand((cmds) => cmds);
+}
+
+/// Tests for `pub ... --help`.
+Future<void> main() async {
+  final cmds = _extractCommands([], PubCommandRunner().commands.values);
+  for (final c in cmds) {
+    testWithGolden('pub ${c.join(' ')} --help', (ctx) async {
+      await ctx.run([...c, '--help']);
+    });
+  }
+}
diff --git a/test/hosted/short_syntax_test.dart b/test/hosted/short_syntax_test.dart
new file mode 100644
index 0000000..bfbb3b4
--- /dev/null
+++ b/test/hosted/short_syntax_test.dart
@@ -0,0 +1,89 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.10
+
+import 'dart:io';
+
+import 'package:path/path.dart' as p;
+import 'package:test/test.dart';
+import 'package:yaml/yaml.dart';
+
+import '../descriptor.dart' as d;
+import '../test_pub.dart';
+
+void main() {
+  setUp(() => servePackages((b) => b.serve('foo', '1.2.3', pubspec: {
+        'environment': {'sdk': '^2.0.0'}
+      })));
+
+  forBothPubGetAndUpgrade((command) {
+    Future<void> testWith(dynamic dependency) async {
+      await d.dir(appPath, [
+        d.libPubspec(
+          'app',
+          '1.0.0',
+          deps: {'foo': dependency},
+          sdk: '^2.15.0',
+        ),
+      ]).create();
+
+      await pubCommand(
+        command,
+        exitCode: 0,
+        environment: {'_PUB_TEST_SDK_VERSION': '2.15.0'},
+      );
+
+      final lockFile = loadYaml(
+        await File(p.join(d.sandbox, appPath, 'pubspec.lock')).readAsString(),
+      );
+
+      expect(lockFile['packages']['foo'], {
+        'dependency': 'direct main',
+        'source': 'hosted',
+        'description': {
+          'name': 'foo',
+          'url': globalPackageServer.url,
+        },
+        'version': '1.2.3',
+      });
+    }
+
+    test('supports hosted: <url> syntax', () async {
+      return testWith({'hosted': globalPackageServer.url});
+    });
+
+    test('supports hosted map without name', () {
+      return testWith({
+        'hosted': {'url': globalPackageServer.url},
+      });
+    });
+
+    test('interprets hosted string as name for older versions', () async {
+      await d.dir(appPath, [
+        d.libPubspec(
+          'app',
+          '1.0.0',
+          deps: {
+            'foo': {'hosted': 'foo', 'version': '^1.2.3'}
+          },
+          sdk: '^2.0.0',
+        ),
+      ]).create();
+
+      await pubCommand(
+        command,
+        exitCode: 0,
+        environment: {'_PUB_TEST_SDK_VERSION': '2.15.0'},
+      );
+
+      final lockFile = loadYaml(
+        await File(p.join(d.sandbox, appPath, 'pubspec.lock')).readAsString(),
+      );
+
+      expect(lockFile['packages']['foo']['description']['url'],
+          globalPackageServer.url);
+    });
+  });
+}
diff --git a/test/lish/force_cannot_be_combined_with_dry_run_test.dart b/test/lish/force_cannot_be_combined_with_dry_run_test.dart
index def3f7a..6e77aa3 100644
--- a/test/lish/force_cannot_be_combined_with_dry_run_test.dart
+++ b/test/lish/force_cannot_be_combined_with_dry_run_test.dart
@@ -14,17 +14,10 @@
   setUp(d.validPackage.create);
 
   test('--force cannot be combined with --dry-run', () async {
-    await runPub(args: ['lish', '--force', '--dry-run'], error: '''
-Cannot use both --force and --dry-run.
-
-Usage: pub publish [options]
--h, --help               Print this usage information.
--n, --dry-run            Validate but do not publish the package.
--f, --force              Publish without confirmation if there are no errors.
--C, --directory=<dir>    Run this in the directory<dir>.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-lish for detailed documentation.
-''', exitCode: exit_codes.USAGE);
+    await runPub(
+      args: ['lish', '--force', '--dry-run'],
+      error: contains('Cannot use both --force and --dry-run.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/lock_file_test.dart b/test/lock_file_test.dart
index 18edc94..2faf8c5 100644
--- a/test/lock_file_test.dart
+++ b/test/lock_file_test.dart
@@ -4,6 +4,7 @@
 
 // @dart=2.10
 
+import 'package:pub/src/language_version.dart';
 import 'package:pub/src/lock_file.dart';
 import 'package:pub/src/package_name.dart';
 import 'package:pub/src/source.dart';
@@ -22,7 +23,8 @@
       throw UnsupportedError('Cannot download fake packages.');
 
   @override
-  PackageRef parseRef(String name, description, {String containingPath}) {
+  PackageRef parseRef(String name, description,
+      {String containingPath, LanguageVersion languageVersion}) {
     if (!description.endsWith(' desc')) throw FormatException('Bad');
     return PackageRef(name, this, description);
   }
diff --git a/test/outdated/goldens/bad_arguments.txt b/test/outdated/goldens/bad_arguments.txt
deleted file mode 100644
index b184530..0000000
--- a/test/outdated/goldens/bad_arguments.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-$ pub outdated random_argument
-[ERR] Command "outdated" does not take any arguments.
-[ERR] 
-[ERR] Usage: pub outdated [options]
-[ERR] -h, --help                         Print this usage information.
-[ERR]     --[no-]color                   Whether to color the output.
-[ERR]                                    Defaults to color when connected to a
-[ERR]                                    terminal, and no-color otherwise.
-[ERR]     --[no-]dependency-overrides    Show resolutions with `dependency_overrides`.
-[ERR]                                    (defaults to on)
-[ERR]     --[no-]dev-dependencies        Take dev dependencies into account.
-[ERR]                                    (defaults to on)
-[ERR]     --json                         Output the results using a json format.
-[ERR]     --mode=<PROPERTY>              Highlight versions with PROPERTY.
-[ERR]                                    Only packages currently missing that PROPERTY
-[ERR]                                    will be included unless --show-all.
-[ERR]                                    [outdated (default), null-safety]
-[ERR]     --[no-]prereleases             Include prereleases in latest version.
-[ERR]                                    (defaults to on in --mode=null-safety).
-[ERR]     --[no-]show-all                Include dependencies that are already
-[ERR]                                    fullfilling --mode.
-[ERR]     --[no-]transitive              Show transitive dependencies.
-[ERR]                                    (defaults to off in --mode=null-safety).
-[ERR] -C, --directory=<dir>              Run this in the directory<dir>.
-[ERR] 
-[ERR] Run "pub help" to see global options.
-[ERR] See https://dart.dev/tools/pub/cmd/pub-outdated for detailed documentation.
-[Exit code] 64
-
-$ pub outdated --bad_flag
-[ERR] Could not find an option named "bad_flag".
-[ERR] 
-[ERR] Usage: pub outdated [options]
-[ERR] -h, --help                         Print this usage information.
-[ERR]     --[no-]color                   Whether to color the output.
-[ERR]                                    Defaults to color when connected to a
-[ERR]                                    terminal, and no-color otherwise.
-[ERR]     --[no-]dependency-overrides    Show resolutions with `dependency_overrides`.
-[ERR]                                    (defaults to on)
-[ERR]     --[no-]dev-dependencies        Take dev dependencies into account.
-[ERR]                                    (defaults to on)
-[ERR]     --json                         Output the results using a json format.
-[ERR]     --mode=<PROPERTY>              Highlight versions with PROPERTY.
-[ERR]                                    Only packages currently missing that PROPERTY
-[ERR]                                    will be included unless --show-all.
-[ERR]                                    [outdated (default), null-safety]
-[ERR]     --[no-]prereleases             Include prereleases in latest version.
-[ERR]                                    (defaults to on in --mode=null-safety).
-[ERR]     --[no-]show-all                Include dependencies that are already
-[ERR]                                    fullfilling --mode.
-[ERR]     --[no-]transitive              Show transitive dependencies.
-[ERR]                                    (defaults to off in --mode=null-safety).
-[ERR] -C, --directory=<dir>              Run this in the directory<dir>.
-[ERR] 
-[ERR] Run "pub help" to see global options.
-[ERR] See https://dart.dev/tools/pub/cmd/pub-outdated for detailed documentation.
-[Exit code] 64
-
diff --git a/test/outdated/goldens/no_pubspec.txt b/test/outdated/goldens/no_pubspec.txt
deleted file mode 100644
index ec3a40a..0000000
--- a/test/outdated/goldens/no_pubspec.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-$ pub outdated
-[ERR] Could not find a file named "pubspec.yaml" in "$SANDBOX/myapp".
-[Exit code] 66
-
diff --git a/test/outdated/outdated_test.dart b/test/outdated/outdated_test.dart
index 925e0de..2f47bbd 100644
--- a/test/outdated/outdated_test.dart
+++ b/test/outdated/outdated_test.dart
@@ -4,72 +4,65 @@
 
 // @dart=2.10
 
-import 'package:test/test.dart';
 import '../descriptor.dart' as d;
 import '../golden_file.dart';
 import '../test_pub.dart';
 
-/// Try running 'pub outdated' with a number of different sets of arguments.
-///
-/// Compare the stdout and stderr output to the file in goldens/$[name].
-Future<void> variations(String name, {Map<String, String> environment}) async {
-  final buffer = StringBuffer();
-  for (final args in [
-    ['outdated', '--json'],
-    ['outdated', '--no-color'],
-    ['outdated', '--no-color', '--no-transitive'],
-    ['outdated', '--no-color', '--up-to-date'],
-    ['outdated', '--no-color', '--prereleases'],
-    ['outdated', '--no-color', '--no-dev-dependencies'],
-    ['outdated', '--no-color', '--no-dependency-overrides'],
-    ['outdated', '--no-color', '--mode=null-safety'],
-    ['outdated', '--no-color', '--mode=null-safety', '--transitive'],
-    ['outdated', '--no-color', '--mode=null-safety', '--no-prereleases'],
-    ['outdated', '--json', '--mode=null-safety'],
-    ['outdated', '--json', '--no-dev-dependencies'],
-  ]) {
-    await runPubIntoBuffer(args, buffer, environment: environment);
+extension on GoldenTestContext {
+  /// Try running 'pub outdated' with a number of different sets of arguments.
+  /// And compare to results from test/testdata/goldens/...
+  Future<void> runOutdatedTests({
+    Map<String, String> environment,
+    String workingDirectory,
+  }) async {
+    const commands = [
+      ['outdated', '--json'],
+      ['outdated', '--no-color'],
+      ['outdated', '--no-color', '--no-transitive'],
+      ['outdated', '--no-color', '--up-to-date'],
+      ['outdated', '--no-color', '--prereleases'],
+      ['outdated', '--no-color', '--no-dev-dependencies'],
+      ['outdated', '--no-color', '--no-dependency-overrides'],
+      ['outdated', '--no-color', '--mode=null-safety'],
+      ['outdated', '--no-color', '--mode=null-safety', '--transitive'],
+      ['outdated', '--no-color', '--mode=null-safety', '--no-prereleases'],
+      ['outdated', '--json', '--mode=null-safety'],
+      ['outdated', '--json', '--no-dev-dependencies'],
+    ];
+    for (final args in commands) {
+      await run(
+        args,
+        environment: environment,
+        workingDirectory: workingDirectory,
+      );
+    }
   }
-  // The easiest way to update the golden files is to delete them and rerun the
-  // test.
-  expectMatchesGoldenFile(buffer.toString(), 'test/outdated/goldens/$name.txt');
 }
 
 Future<void> main() async {
-  test('help text', () async {
-    final buffer = StringBuffer();
-    await runPubIntoBuffer(
-      ['outdated', '--help'],
-      buffer,
-    );
-    expectMatchesGoldenFile(
-        buffer.toString(), 'test/outdated/goldens/helptext.txt');
-  });
-
-  test('no pubspec', () async {
+  testWithGolden('no pubspec', (ctx) async {
     await d.dir(appPath, []).create();
-    final buffer = StringBuffer();
-    await runPubIntoBuffer(['outdated'], buffer);
-    expectMatchesGoldenFile(
-        buffer.toString(), 'test/outdated/goldens/no_pubspec.txt');
+    await ctx.run(['outdated']);
   });
 
-  test('no lockfile', () async {
+  testWithGolden('no lockfile', (ctx) async {
     await d.appDir({'foo': '^1.0.0', 'bar': '^1.0.0'}).create();
     await servePackages((builder) => builder
       ..serve('foo', '1.2.3')
       ..serve('bar', '1.2.3')
       ..serve('bar', '2.0.0'));
-    await variations('no_lockfile');
+
+    await ctx.runOutdatedTests();
   });
 
-  test('no dependencies', () async {
+  testWithGolden('no dependencies', (ctx) async {
     await d.appDir().create();
     await pubGet();
-    await variations('no_dependencies');
+
+    await ctx.runOutdatedTests();
   });
 
-  test('newer versions available', () async {
+  testWithGolden('newer versions available', (ctx) async {
     await servePackages((builder) => builder
       ..serve('foo', '1.2.3', deps: {'transitive': '^1.0.0'})
       ..serve('bar', '1.0.0')
@@ -114,10 +107,11 @@
       ..serve('transitive2', '1.0.0')
       ..serve('transitive3', '1.0.0')
       ..serve('dev_trans', '2.0.0'));
-    await variations('newer_versions');
+
+    await ctx.runOutdatedTests();
   });
 
-  test('circular dependency on root', () async {
+  testWithGolden('circular dependency on root', (ctx) async {
     await servePackages(
       (builder) => builder..serve('foo', '1.2.3', deps: {'app': '^1.0.0'}),
     );
@@ -137,10 +131,11 @@
     globalPackageServer.add(
       (builder) => builder..serve('foo', '1.3.0', deps: {'app': '^1.0.1'}),
     );
-    await variations('circular_dependencies');
+
+    await ctx.runOutdatedTests();
   });
 
-  test('mutually incompatible newer versions', () async {
+  testWithGolden('mutually incompatible newer versions', (ctx) async {
     await d.dir(appPath, [
       d.pubspec({
         'name': 'app',
@@ -159,10 +154,10 @@
       ..serve('bar', '2.0.0', deps: {'foo': '^1.0.0'}));
     await pubGet();
 
-    await variations('mutually_incompatible');
+    await ctx.runOutdatedTests();
   });
 
-  test('null safety compliance', () async {
+  testWithGolden('null safety compliance', (ctx) async {
     await d.dir(appPath, [
       d.pubspec({
         'name': 'app',
@@ -237,11 +232,12 @@
     );
     await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
 
-    await variations('null_safety',
-        environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
+    await ctx.runOutdatedTests(environment: {
+      '_PUB_TEST_SDK_VERSION': '2.13.0',
+    });
   });
 
-  test('null-safety no resolution', () async {
+  testWithGolden('null-safety no resolution', (ctx) async {
     await servePackages((builder) => builder
       ..serve('foo', '1.0.0', pubspec: {
         'environment': {'sdk': '>=2.9.0 < 3.0.0'}
@@ -274,11 +270,12 @@
 
     await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
 
-    await variations('null_safety_no_resolution',
-        environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
+    await ctx.runOutdatedTests(environment: {
+      '_PUB_TEST_SDK_VERSION': '2.13.0',
+    });
   });
 
-  test('null-safety already migrated', () async {
+  testWithGolden('null-safety already migrated', (ctx) async {
     await servePackages((builder) => builder
       ..serve('foo', '1.0.0', pubspec: {
         'environment': {'sdk': '>=2.9.0 < 3.0.0'}
@@ -314,11 +311,12 @@
 
     await pubGet(environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
 
-    await variations('null_safety_already_migrated',
-        environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'});
+    await ctx.runOutdatedTests(environment: {
+      '_PUB_TEST_SDK_VERSION': '2.13.0',
+    });
   });
 
-  test('overridden dependencies', () async {
+  testWithGolden('overridden dependencies', (ctx) async {
     ensureGit();
     await servePackages(
       (builder) => builder
@@ -359,10 +357,10 @@
 
     await pubGet();
 
-    await variations('dependency_overrides');
+    await ctx.runOutdatedTests();
   });
 
-  test('overridden dependencies - no resolution', () async {
+  testWithGolden('overridden dependencies - no resolution', (ctx) async {
     ensureGit();
     await servePackages(
       (builder) => builder
@@ -389,12 +387,12 @@
 
     await pubGet();
 
-    await variations('dependency_overrides_no_solution');
+    await ctx.runOutdatedTests();
   });
 
-  test(
+  testWithGolden(
       'latest version reported while locked on a prerelease can be a prerelease',
-      () async {
+      (ctx) async {
     await servePackages((builder) => builder
       ..serve('foo', '0.9.0')
       ..serve('foo', '1.0.0-dev.1')
@@ -419,10 +417,10 @@
 
     await pubGet();
 
-    await variations('prereleases');
+    await ctx.runOutdatedTests();
   });
 
-  test('Handles SDK dependencies', () async {
+  testWithGolden('Handles SDK dependencies', (ctx) async {
     await servePackages((builder) => builder
       ..serve('foo', '1.0.0', pubspec: {
         'environment': {'sdk': '>=2.10.0 <3.0.0'}
@@ -471,7 +469,7 @@
       '_PUB_TEST_SDK_VERSION': '2.13.0'
     });
 
-    await variations('handles_sdk_dependencies', environment: {
+    await ctx.runOutdatedTests(environment: {
       'FLUTTER_ROOT': d.path('flutter-root'),
       '_PUB_TEST_SDK_VERSION': '2.13.0',
       // To test that the reproduction command is reflected correctly.
@@ -479,11 +477,8 @@
     });
   });
 
-  test("doesn't allow arguments. Handles bad flags", () async {
-    final sb = StringBuffer();
-    await runPubIntoBuffer(['outdated', 'random_argument'], sb);
-    await runPubIntoBuffer(['outdated', '--bad_flag'], sb);
-    expectMatchesGoldenFile(
-        sb.toString(), 'test/outdated/goldens/bad_arguments.txt');
+  testWithGolden('does not allow arguments - handles bad flags', (ctx) async {
+    await ctx.run(['outdated', 'random_argument']);
+    await ctx.run(['outdated', '--bad_flag']);
   });
 }
diff --git a/test/pub_uploader_test.dart b/test/pub_uploader_test.dart
index 98bd2fe..c45c8ed 100644
--- a/test/pub_uploader_test.dart
+++ b/test/pub_uploader_test.dart
@@ -15,19 +15,6 @@
 import 'descriptor.dart' as d;
 import 'test_pub.dart';
 
-const _usageString = '''
-Manage uploaders for a package on pub.dartlang.org.
-
-Usage: pub uploader [options] {add/remove} <email>
--h, --help               Print this usage information.
-    --package            The package whose uploaders will be modified.
-                         (defaults to the current package)
--C, --directory=<dir>    Run this in the directory<dir>.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-uploader for detailed documentation.
-''';
-
 Future<TestProcess> startPubUploader(PackageServer server, List<String> args) {
   var tokenEndpoint = Uri.parse(server.url).resolve('/token').toString();
   var allArgs = ['uploader', ...args];
@@ -40,22 +27,16 @@
 void main() {
   group('displays usage', () {
     test('when run with no arguments', () {
-      return runPub(
-          args: ['uploader'], output: _usageString, exitCode: exit_codes.USAGE);
+      return runPub(args: ['uploader'], exitCode: exit_codes.USAGE);
     });
 
     test('when run with only a command', () {
-      return runPub(
-          args: ['uploader', 'add'],
-          output: _usageString,
-          exitCode: exit_codes.USAGE);
+      return runPub(args: ['uploader', 'add'], exitCode: exit_codes.USAGE);
     });
 
     test('when run with an invalid command', () {
       return runPub(
-          args: ['uploader', 'foo', 'email'],
-          output: _usageString,
-          exitCode: exit_codes.USAGE);
+          args: ['uploader', 'foo', 'email'], exitCode: exit_codes.USAGE);
     });
   });
 
diff --git a/test/pubspec_test.dart b/test/pubspec_test.dart
index 5785b16..c293d85 100644
--- a/test/pubspec_test.dart
+++ b/test/pubspec_test.dart
@@ -4,6 +4,7 @@
 
 // @dart=2.10
 
+import 'package:pub/src/language_version.dart';
 import 'package:pub/src/package_name.dart';
 import 'package:pub/src/pubspec.dart';
 import 'package:pub/src/sdk.dart';
@@ -22,7 +23,8 @@
       throw UnsupportedError('Cannot download fake packages.');
 
   @override
-  PackageRef parseRef(String name, description, {String containingPath}) {
+  PackageRef parseRef(String name, description,
+      {String containingPath, LanguageVersion languageVersion}) {
     if (description != 'ok') throw FormatException('Bad');
     return PackageRef(name, this, description);
   }
@@ -294,6 +296,170 @@
               'local pubspec.');
     });
 
+    group('source dependencies', () {
+      test('with url and name', () {
+        var pubspec = Pubspec.parse(
+          '''
+name: pkg
+dependencies:
+  foo:
+    hosted:
+      url: https://example.org/pub/
+      name: bar
+''',
+          sources,
+        );
+
+        var foo = pubspec.dependencies['foo'];
+        expect(foo.name, equals('foo'));
+        expect(foo.source.name, 'hosted');
+        expect(foo.source.serializeDescription(null, foo.description), {
+          'url': 'https://example.org/pub/',
+          'name': 'bar',
+        });
+      });
+
+      test('with url only', () {
+        var pubspec = Pubspec.parse(
+          '''
+name: pkg
+environment:
+  sdk: ^2.15.0
+dependencies:
+  foo:
+    hosted:
+      url: https://example.org/pub/
+''',
+          sources,
+        );
+
+        var foo = pubspec.dependencies['foo'];
+        expect(foo.name, equals('foo'));
+        expect(foo.source.name, 'hosted');
+        expect(foo.source.serializeDescription(null, foo.description), {
+          'url': 'https://example.org/pub/',
+          'name': 'foo',
+        });
+      });
+
+      test('with url as string', () {
+        var pubspec = Pubspec.parse(
+          '''
+name: pkg
+environment:
+  sdk: ^2.15.0
+dependencies:
+  foo:
+    hosted: https://example.org/pub/
+''',
+          sources,
+        );
+
+        var foo = pubspec.dependencies['foo'];
+        expect(foo.name, equals('foo'));
+        expect(foo.source.name, 'hosted');
+        expect(foo.source.serializeDescription(null, foo.description), {
+          'url': 'https://example.org/pub/',
+          'name': 'foo',
+        });
+      });
+
+      test('interprets string description as name for older versions', () {
+        var pubspec = Pubspec.parse(
+          '''
+name: pkg
+environment:
+  sdk: ^2.14.0
+dependencies:
+  foo:
+    hosted: bar
+''',
+          sources,
+        );
+
+        var foo = pubspec.dependencies['foo'];
+        expect(foo.name, equals('foo'));
+        expect(foo.source.name, 'hosted');
+        expect(foo.source.serializeDescription(null, foo.description), {
+          'url': 'https://pub.dartlang.org',
+          'name': 'bar',
+        });
+      });
+
+      test(
+        'reports helpful span when using new syntax with invalid environment',
+        () {
+          var pubspec = Pubspec.parse('''
+name: pkg
+environment:
+  sdk: invalid value
+dependencies:
+  foo:
+    hosted: https://example.org/pub/
+''', sources);
+
+          expect(
+            () => pubspec.dependencies,
+            throwsA(
+              isA<PubspecException>()
+                  .having((e) => e.span.text, 'span.text', 'invalid value'),
+            ),
+          );
+        },
+      );
+
+      test('without a description', () {
+        var pubspec = Pubspec.parse(
+          '''
+name: pkg
+dependencies:
+  foo:
+''',
+          sources,
+        );
+
+        var foo = pubspec.dependencies['foo'];
+        expect(foo.name, equals('foo'));
+        expect(foo.source.name, 'hosted');
+        expect(foo.source.serializeDescription(null, foo.description), {
+          'url': 'https://pub.dartlang.org',
+          'name': 'foo',
+        });
+      });
+
+      group('throws without a min SDK constraint', () {
+        test('and without a name', () {
+          expectPubspecException(
+              '''
+name: pkg
+dependencies:
+  foo:
+    hosted:
+      url: https://example.org/pub/
+''',
+              (pubspec) => pubspec.dependencies,
+              "The 'name' key must have a string value without a minimum Dart "
+                  'SDK constraint of 2.15.');
+        });
+
+        test(
+          'and a hosted: <value> syntax that looks like an URI was meant',
+          () {
+            expectPubspecException(
+              '''
+name: pkg
+dependencies:
+  foo:
+    hosted: http://pub.example.org
+''',
+              (pubspec) => pubspec.dependencies,
+              'Using `hosted: <url>` is only supported with a minimum SDK constraint of 2.15.',
+            );
+          },
+        );
+      });
+    });
+
     group('git dependencies', () {
       test('path must be a string', () {
         expectPubspecException('''
diff --git a/test/run/errors_if_no_executable_is_given_test.dart b/test/run/errors_if_no_executable_is_given_test.dart
index 2bec1a3..92f0073 100644
--- a/test/run/errors_if_no_executable_is_given_test.dart
+++ b/test/run/errors_if_no_executable_is_given_test.dart
@@ -14,22 +14,10 @@
   test('Errors if the executable does not exist.', () async {
     await d.dir(appPath, [d.appPubspec()]).create();
 
-    await runPub(args: ['run'], error: '''
-Must specify an executable to run.
-
-Usage: pub run <executable> [arguments...]
--h, --help                              Print this usage information.
-    --[no-]enable-asserts               Enable assert statements.
-    --enable-experiment=<experiment>    Runs the executable in a VM with the
-                                        given experiments enabled.
-                                        (Will disable snapshotting, resulting in
-                                        slower startup).
-    --[no-]sound-null-safety            Override the default null safety
-                                        execution mode.
--C, --directory=<dir>                   Run this in the directory<dir>.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
-''', exitCode: exit_codes.USAGE);
+    await runPub(
+      args: ['run'],
+      error: contains('Must specify an executable to run.'),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/run/errors_if_path_in_dependency_test.dart b/test/run/errors_if_path_in_dependency_test.dart
index 5625c4e..2affef7 100644
--- a/test/run/errors_if_path_in_dependency_test.dart
+++ b/test/run/errors_if_path_in_dependency_test.dart
@@ -22,22 +22,12 @@
       })
     ]).create();
 
-    await runPub(args: ['run', 'foo:sub/dir'], error: '''
-Cannot run an executable in a subdirectory of a dependency.
-
-Usage: pub run <executable> [arguments...]
--h, --help                              Print this usage information.
-    --[no-]enable-asserts               Enable assert statements.
-    --enable-experiment=<experiment>    Runs the executable in a VM with the
-                                        given experiments enabled.
-                                        (Will disable snapshotting, resulting in
-                                        slower startup).
-    --[no-]sound-null-safety            Override the default null safety
-                                        execution mode.
--C, --directory=<dir>                   Run this in the directory<dir>.
-
-Run "pub help" to see global options.
-See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
-''', exitCode: exit_codes.USAGE);
+    await runPub(
+      args: ['run', 'foo:sub/dir'],
+      error: contains(
+        'Cannot run an executable in a subdirectory of a dependency.',
+      ),
+      exitCode: exit_codes.USAGE,
+    );
   });
 }
diff --git a/test/test_pub.dart b/test/test_pub.dart
index cccefbe..2d68233 100644
--- a/test/test_pub.dart
+++ b/test/test_pub.dart
@@ -934,15 +934,24 @@
   );
   final exitCode = await process.exitCode;
 
+  // TODO(jonasfj): Clean out temporary directory names from env vars...
+  // if (workingDirectory != null) {
+  //   buffer.writeln('\$ cd $workingDirectory');
+  // }
+  // if (environment != null && environment.isNotEmpty) {
+  //   buffer.writeln(environment.entries
+  //       .map((e) => '\$ export ${e.key}=${e.value}')
+  //       .join('\n'));
+  // }
   buffer.writeln(_filter([
     '\$ pub ${args.join(' ')}',
     ...await process.stdout.rest.toList(),
   ]).join('\n'));
   for (final line in _filter(await process.stderr.rest.toList())) {
-    buffer.writeln('[ERR] $line');
+    buffer.writeln('[STDERR] $line');
   }
   if (exitCode != 0) {
-    buffer.writeln('[Exit code] $exitCode');
+    buffer.writeln('[EXIT CODE] $exitCode');
   }
   buffer.write('\n');
 }
diff --git a/test/testdata/README.md b/test/testdata/README.md
new file mode 100644
index 0000000..ad4a817
--- /dev/null
+++ b/test/testdata/README.md
@@ -0,0 +1,25 @@
+# Test Data
+
+Data used in tests is called _test data_ and is located in this folder, or
+sub-folders thereof. This is not for test files, this folder should not contain
+test code, only data used in tests.
+
+## Golden Test
+
+The `test` wrapper `testWithGolden('<name>', (ctx) async {` will register a
+test case, and create a file:
+  `test/testdata/goldens/path/to/myfile_test/<name>.txt`
+, where `path/to/myfile_test.dart` is the name of the file containing the test
+case, and `<name>` is the name of the test case.
+
+Any calls to `ctx.run` will run `pub` and compare the output to a section in the
+golden file. If the file does not exist, it is created and the
+test is marked as skipped.
+Thus, it is safe to delete all files in `test/testdata/goldens` and recreate
+them -- just carefully review the changes before committing.
+
+**Maintaining goldens**:
+ 1. Delete `test/testdata/goldens/`.
+ 2. Re-run tests to re-create files in `test/testdata/goldens/`.
+ 3. Compare changes, using `git diff test/testdata/goldens/`.
+
diff --git a/test/deps/goldens/formatting.txt b/test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt
similarity index 71%
rename from test/deps/goldens/formatting.txt
rename to test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt
index ff5aec9..d8448df 100644
--- a/test/deps/goldens/formatting.txt
+++ b/test/testdata/goldens/deps/executables_test/applies formatting before printing executables.txt
@@ -1,3 +1,7 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
 |-- bar
 |   |-- bin
 |   |   '-- qux.dart
@@ -10,18 +14,28 @@
 '-- myapp
     |-- bin
     |   '-- myapp.dart
+    |-- pubspec.lock
     '-- pubspec.yaml
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub deps --executables
 myapp
 foo: foo, baz
 bar:qux
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub deps --executables --dev
 myapp
 foo: foo, baz
 bar:qux
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub deps --json
 {
   "root": "myapp",
diff --git a/test/deps/goldens/dev_dependencies.txt b/test/testdata/goldens/deps/executables_test/dev dependencies.txt
similarity index 62%
rename from test/deps/goldens/dev_dependencies.txt
rename to test/testdata/goldens/deps/executables_test/dev dependencies.txt
index 3fb677c..2d72b44 100644
--- a/test/deps/goldens/dev_dependencies.txt
+++ b/test/testdata/goldens/deps/executables_test/dev dependencies.txt
@@ -1,16 +1,30 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
 |-- foo
 |   |-- bin
 |   |   '-- bar.dart
 |   '-- pubspec.yaml
 '-- myapp
+    |-- pubspec.lock
     '-- pubspec.yaml
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub deps --executables
 foo:bar
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub deps --executables --dev
 foo:bar
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub deps --json
 {
   "root": "myapp",
diff --git a/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt b/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt
new file mode 100644
index 0000000..a12d363
--- /dev/null
+++ b/test/testdata/goldens/deps/executables_test/lists Dart executables, without entrypoints.txt
@@ -0,0 +1,50 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
+'-- myapp
+    |-- bin
+    |   |-- bar.dart
+    |   '-- foo.dart
+    |-- pubspec.lock
+    '-- pubspec.yaml
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub deps --executables
+myapp: bar, foo
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
+$ pub deps --executables --dev
+myapp: bar, foo
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
+$ pub deps --json
+{
+  "root": "myapp",
+  "packages": [
+    {
+      "name": "myapp",
+      "version": "0.0.0",
+      "kind": "root",
+      "source": "root",
+      "dependencies": []
+    }
+  ],
+  "sdks": [
+    {
+      "name": "Dart",
+      "version": "0.1.2+3"
+    }
+  ],
+  "executables": [
+    ":bar",
+    ":foo"
+  ]
+}
+
diff --git a/test/deps/goldens/only_immediate.txt b/test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt
similarity index 62%
copy from test/deps/goldens/only_immediate.txt
copy to test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt
index 5e42925..75a1b33 100644
--- a/test/deps/goldens/only_immediate.txt
+++ b/test/testdata/goldens/deps/executables_test/lists executables from a dependency.txt
@@ -1,20 +1,30 @@
-|-- baz
-|   |-- bin
-|   |   '-- qux.dart
-|   '-- pubspec.yaml
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
 |-- foo
 |   |-- bin
 |   |   '-- bar.dart
 |   '-- pubspec.yaml
 '-- myapp
+    |-- pubspec.lock
     '-- pubspec.yaml
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub deps --executables
 foo:bar
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub deps --executables --dev
 foo:bar
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub deps --json
 {
   "root": "myapp",
@@ -33,15 +43,6 @@
       "version": "1.0.0",
       "kind": "direct",
       "source": "path",
-      "dependencies": [
-        "baz"
-      ]
-    },
-    {
-      "name": "baz",
-      "version": "1.0.0",
-      "kind": "transitive",
-      "source": "path",
       "dependencies": []
     }
   ],
diff --git a/test/deps/goldens/only_immediate.txt b/test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt
similarity index 68%
rename from test/deps/goldens/only_immediate.txt
rename to test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt
index 5e42925..de70643 100644
--- a/test/deps/goldens/only_immediate.txt
+++ b/test/testdata/goldens/deps/executables_test/lists executables only from immediate dependencies.txt
@@ -1,3 +1,7 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
 |-- baz
 |   |-- bin
 |   |   '-- qux.dart
@@ -7,14 +11,24 @@
 |   |   '-- bar.dart
 |   '-- pubspec.yaml
 '-- myapp
+    |-- pubspec.lock
     '-- pubspec.yaml
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub deps --executables
 foo:bar
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub deps --executables --dev
 foo:bar
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub deps --json
 {
   "root": "myapp",
diff --git a/test/deps/goldens/overrides.txt b/test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt
similarity index 66%
rename from test/deps/goldens/overrides.txt
rename to test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt
index ab76e79..910210e 100644
--- a/test/deps/goldens/overrides.txt
+++ b/test/testdata/goldens/deps/executables_test/overriden dependencies executables.txt
@@ -1,3 +1,7 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
 |-- foo-1.0
 |   |-- bin
 |   |   '-- bar.dart
@@ -8,14 +12,24 @@
 |   |   '-- baz.dart
 |   '-- pubspec.yaml
 '-- myapp
+    |-- pubspec.lock
     '-- pubspec.yaml
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub deps --executables
 foo: bar, baz
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub deps --executables --dev
 foo: bar, baz
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub deps --json
 {
   "root": "myapp",
diff --git a/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt b/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt
new file mode 100644
index 0000000..289f7c8
--- /dev/null
+++ b/test/testdata/goldens/deps/executables_test/skips executables in sub directories.txt
@@ -0,0 +1,50 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
+'-- myapp
+    |-- bin
+    |   |-- foo.dart
+    |   '-- sub
+    |       '-- bar.dart
+    |-- pubspec.lock
+    '-- pubspec.yaml
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub deps --executables
+myapp:foo
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
+$ pub deps --executables --dev
+myapp:foo
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
+$ pub deps --json
+{
+  "root": "myapp",
+  "packages": [
+    {
+      "name": "myapp",
+      "version": "0.0.0",
+      "kind": "root",
+      "source": "root",
+      "dependencies": []
+    }
+  ],
+  "sdks": [
+    {
+      "name": "Dart",
+      "version": "0.1.2+3"
+    }
+  ],
+  "executables": [
+    ":foo"
+  ]
+}
+
diff --git a/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt b/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt
new file mode 100644
index 0000000..d4a2db3
--- /dev/null
+++ b/test/testdata/goldens/deps/executables_test/skips non-Dart executables.txt
@@ -0,0 +1,45 @@
+# GENERATED BY: test/deps/executables_test.dart
+
+## Section 0
+$ tree
+'-- myapp
+    |-- bin
+    |   |-- bar.sh
+    |   '-- foo.py
+    |-- pubspec.lock
+    '-- pubspec.yaml
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub deps --executables
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
+$ pub deps --executables --dev
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
+$ pub deps --json
+{
+  "root": "myapp",
+  "packages": [
+    {
+      "name": "myapp",
+      "version": "0.0.0",
+      "kind": "root",
+      "source": "root",
+      "dependencies": []
+    }
+  ],
+  "sdks": [
+    {
+      "name": "Dart",
+      "version": "0.1.2+3"
+    }
+  ],
+  "executables": []
+}
+
diff --git a/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt b/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt
new file mode 100644
index 0000000..237b3b5
--- /dev/null
+++ b/test/testdata/goldens/directory_option_test/commands taking a --directory~-C parameter work.txt
@@ -0,0 +1,126 @@
+# GENERATED BY: test/directory_option_test.dart
+
+## Section 0
+$ pub add --directory=myapp foo
+Resolving dependencies in myapp...
++ foo 1.0.0
+Changed 1 dependency in myapp!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub -C myapp add bar
+Resolving dependencies in myapp...
++ bar 1.2.3
+Changed 1 dependency in myapp!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
+$ pub -C myapp/example get --directory=myapp bar
+Resolving dependencies in myapp...
+Got dependencies in myapp!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
+$ pub remove bar -C myapp
+Resolving dependencies in myapp...
+These packages are no longer being depended on:
+- bar 1.2.3
+Changed 1 dependency in myapp!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
+$ pub get bar -C myapp
+Resolving dependencies in myapp...
+Got dependencies in myapp!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
+$ pub get bar -C myapp/example
+Resolving dependencies in myapp/example...
++ foo 1.0.0
++ test_pkg 1.0.0 from path myapp
+Changed 2 dependencies in myapp/example!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
+$ pub get bar -C myapp/example2
+Resolving dependencies in myapp/example2...
+[STDERR] Error on line 1, column 9 of myapp/pubspec.yaml: "name" field doesn't match expected name "myapp".
+[STDERR]   ╷
+[STDERR] 1 │ {"name":"test_pkg","version":"1.0.0","homepage":"http://pub.dartlang.org","description":"A package, I guess.","environment":{"sdk":">=1.8.0 <=2.0.0"}, dependencies: { foo: ^1.0.0}}
+[STDERR]   │         ^^^^^^^^^^
+[STDERR]   ╵
+[EXIT CODE] 65
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
+$ pub get bar -C myapp/broken_dir
+[STDERR] Could not find a file named "pubspec.yaml" in "$SANDBOX/myapp/broken_dir".
+[EXIT CODE] 66
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
+$ pub downgrade -C myapp
+Resolving dependencies in myapp...
+  foo 1.0.0
+No dependencies changed in myapp.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
+$ pub upgrade bar -C myapp
+Resolving dependencies in myapp...
+  foo 1.0.0
+No dependencies changed in myapp.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
+$ pub run -C myapp bin/app.dart
+Building package executable...
+Built test_pkg:app.
+Hi
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
+$ pub publish -C myapp --dry-run
+Publishing test_pkg 1.0.0 to http://localhost:$PORT:
+|-- CHANGELOG.md
+|-- LICENSE
+|-- README.md
+|-- bin
+|   '-- app.dart
+|-- example
+|   '-- pubspec.yaml
+|-- example2
+|   '-- pubspec.yaml
+|-- lib
+|   '-- test_pkg.dart
+'-- pubspec.yaml
+The server may enforce additional checks.
+[STDERR] 
+[STDERR] Package has 0 warnings.
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 12
+$ pub uploader -C myapp add sigurdm@google.com
+Good job!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 13
+$ pub deps -C myapp
+Dart SDK 1.12.0
+test_pkg 1.0.0
+'-- foo 1.0.0
+
diff --git a/test/testdata/goldens/embedding/embedding_test/run works, though hidden.txt b/test/testdata/goldens/embedding/embedding_test/run works, though hidden.txt
new file mode 100644
index 0000000..214712a
--- /dev/null
+++ b/test/testdata/goldens/embedding/embedding_test/run works, though hidden.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/embedding/embedding_test.dart
+
+$ tool/test-bin/pub_command_runner.dart pub get
+Resolving dependencies...
+Got dependencies!
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+$ tool/test-bin/pub_command_runner.dart pub run bin/main.dart
+Hi
+
diff --git a/test/testdata/goldens/help_test/pub add --help.txt b/test/testdata/goldens/help_test/pub add --help.txt
new file mode 100644
index 0000000..5d32e5e
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub add --help.txt
@@ -0,0 +1,24 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub add --help
+Add a dependency to pubspec.yaml.
+
+Usage: pub add <package>[:<constraint>] [options]
+-h, --help               Print this usage information.
+-d, --dev                Adds package to the development dependencies instead.
+    --git-url            Git URL of the package
+    --git-ref            Git branch or commit to be retrieved
+    --git-path           Path of git package in repository
+    --hosted-url         URL of package host server
+    --path               Local path
+    --sdk                SDK source for package
+    --[no-]offline       Use cached packages instead of accessing the network.
+-n, --dry-run            Report what dependencies would change but don't change
+                         any.
+    --[no-]precompile    Build executables in immediate dependencies.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-add for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub cache --help.txt b/test/testdata/goldens/help_test/pub cache --help.txt
new file mode 100644
index 0000000..fdb0b2c
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub cache --help.txt
@@ -0,0 +1,17 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub cache --help
+Work with the system cache.
+
+Usage: pub cache [arguments...]
+-h, --help    Print this usage information.
+
+Available subcommands:
+  add      Install a package.
+  clean    Clears the global PUB_CACHE.
+  repair   Reinstall cached packages.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub cache add --help.txt b/test/testdata/goldens/help_test/pub cache add --help.txt
new file mode 100644
index 0000000..05068f0
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub cache add --help.txt
@@ -0,0 +1,14 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub cache add --help
+Install a package.
+
+Usage: pub cache add <package> [--version <constraint>] [--all]
+-h, --help       Print this usage information.
+    --all        Install all matching versions.
+-v, --version    Version constraint.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub cache clean --help.txt b/test/testdata/goldens/help_test/pub cache clean --help.txt
new file mode 100644
index 0000000..71dd1d6
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub cache clean --help.txt
@@ -0,0 +1,12 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub cache clean --help
+Clears the global PUB_CACHE.
+
+Usage: pub cache clean <subcommand> [arguments...]
+-h, --help     Print this usage information.
+-f, --force    Don't ask for confirmation.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub cache repair --help.txt b/test/testdata/goldens/help_test/pub cache repair --help.txt
new file mode 100644
index 0000000..ac09115
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub cache repair --help.txt
@@ -0,0 +1,12 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub cache repair --help
+Reinstall cached packages.
+
+Usage: pub cache repair <subcommand> [arguments...]
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-cache for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub deps --help.txt b/test/testdata/goldens/help_test/pub deps --help.txt
new file mode 100644
index 0000000..475353f
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub deps --help.txt
@@ -0,0 +1,19 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub deps --help
+Print package dependencies.
+
+Usage: pub deps [arguments...]
+-h, --help               Print this usage information.
+-s, --style              How output should be displayed.
+                         [compact, tree (default), list]
+    --[no-]dev           Whether to include dev dependencies.
+                         (defaults to on)
+    --executables        List all available executables.
+    --json               Output dependency information in a json format.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-deps for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub downgrade --help.txt b/test/testdata/goldens/help_test/pub downgrade --help.txt
new file mode 100644
index 0000000..4497da2
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub downgrade --help.txt
@@ -0,0 +1,18 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub downgrade --help
+Downgrade the current package's dependencies to oldest versions.
+
+
+
+Usage: pub downgrade [dependencies...]
+-h, --help               Print this usage information.
+    --[no-]offline       Use cached packages instead of accessing the network.
+-n, --dry-run            Report what dependencies would change but don't change
+                         any.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-downgrade for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub get --help.txt b/test/testdata/goldens/help_test/pub get --help.txt
new file mode 100644
index 0000000..74648a2
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub get --help.txt
@@ -0,0 +1,17 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub get --help
+Get the current package's dependencies.
+
+Usage: pub get <subcommand> [arguments...]
+-h, --help               Print this usage information.
+    --[no-]offline       Use cached packages instead of accessing the network.
+-n, --dry-run            Report what dependencies would change but don't change
+                         any.
+    --[no-]precompile    Build executables in immediate dependencies.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-get for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub global --help.txt b/test/testdata/goldens/help_test/pub global --help.txt
new file mode 100644
index 0000000..6aa723c
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub global --help.txt
@@ -0,0 +1,18 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub global --help
+Work with global packages.
+
+Usage: pub global [arguments...]
+-h, --help    Print this usage information.
+
+Available subcommands:
+  activate     Make a package's executables globally available.
+  deactivate   Remove a previously activated package.
+  list         List globally activated packages.
+  run          Run an executable from a globally activated package.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-global for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub global activate --help.txt b/test/testdata/goldens/help_test/pub global activate --help.txt
new file mode 100644
index 0000000..5341266
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub global activate --help.txt
@@ -0,0 +1,19 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub global activate --help
+Make a package's executables globally available.
+
+Usage: pub global activate <package> [version-constraint]
+-h, --help              Print this usage information.
+-s, --source            The source used to find the package.
+                        [git, hosted (default), path]
+    --no-executables    Do not put executables on PATH.
+-x, --executable        Executable(s) to place on PATH.
+    --overwrite         Overwrite executables from other packages with the same
+                        name.
+-u, --hosted-url        A custom pub server URL for the package. Only applies
+                        when using the `hosted` source.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub global deactivate --help.txt b/test/testdata/goldens/help_test/pub global deactivate --help.txt
new file mode 100644
index 0000000..8b8178b
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub global deactivate --help.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub global deactivate --help
+Remove a previously activated package.
+
+Usage: pub global deactivate <package>
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub global list --help.txt b/test/testdata/goldens/help_test/pub global list --help.txt
new file mode 100644
index 0000000..12244be
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub global list --help.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub global list --help
+List globally activated packages.
+
+Usage: pub global list <subcommand> [arguments...]
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub global run --help.txt b/test/testdata/goldens/help_test/pub global run --help.txt
new file mode 100644
index 0000000..f829064
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub global run --help.txt
@@ -0,0 +1,18 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub global run --help
+Run an executable from a globally activated package.
+
+Usage: pub global run <package>:<executable> [args...]
+-h, --help                              Print this usage information.
+    --[no-]enable-asserts               Enable assert statements.
+    --enable-experiment=<experiment>    Runs the executable in a VM with the
+                                        given experiments enabled. (Will disable
+                                        snapshotting, resulting in slower
+                                        startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub login --help.txt b/test/testdata/goldens/help_test/pub login --help.txt
new file mode 100644
index 0000000..ce7233b
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub login --help.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub login --help
+Log into pub.dev.
+
+Usage: pub login
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub logout --help.txt b/test/testdata/goldens/help_test/pub logout --help.txt
new file mode 100644
index 0000000..3937ef7
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub logout --help.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub logout --help
+Log out of pub.dev.
+
+Usage: pub logout <subcommand> [arguments...]
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+
diff --git a/test/outdated/goldens/helptext.txt b/test/testdata/goldens/help_test/pub outdated --help.txt
similarity index 96%
rename from test/outdated/goldens/helptext.txt
rename to test/testdata/goldens/help_test/pub outdated --help.txt
index 20a4ecc..9a7b1bc 100644
--- a/test/outdated/goldens/helptext.txt
+++ b/test/testdata/goldens/help_test/pub outdated --help.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
 $ pub outdated --help
 Analyze your dependencies to find which ones can be upgraded.
 
diff --git a/test/testdata/goldens/help_test/pub publish --help.txt b/test/testdata/goldens/help_test/pub publish --help.txt
new file mode 100644
index 0000000..2977db0
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub publish --help.txt
@@ -0,0 +1,15 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub publish --help
+Publish the current package to pub.dartlang.org.
+
+Usage: pub publish [options]
+-h, --help               Print this usage information.
+-n, --dry-run            Validate but do not publish the package.
+-f, --force              Publish without confirmation if there are no errors.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-lish for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub remove --help.txt b/test/testdata/goldens/help_test/pub remove --help.txt
new file mode 100644
index 0000000..7d14ed0
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub remove --help.txt
@@ -0,0 +1,17 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub remove --help
+Removes a dependency from the current package.
+
+Usage: pub remove <package>
+-h, --help               Print this usage information.
+    --[no-]offline       Use cached packages instead of accessing the network.
+-n, --dry-run            Report what dependencies would change but don't change
+                         any.
+    --[no-]precompile    Precompile executables in immediate dependencies.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-remove for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub run --help.txt b/test/testdata/goldens/help_test/pub run --help.txt
new file mode 100644
index 0000000..f2844f3
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub run --help.txt
@@ -0,0 +1,20 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub run --help
+Run an executable from a package.
+
+Usage: pub run <executable> [arguments...]
+-h, --help                              Print this usage information.
+    --[no-]enable-asserts               Enable assert statements.
+    --enable-experiment=<experiment>    Runs the executable in a VM with the
+                                        given experiments enabled.
+                                        (Will disable snapshotting, resulting in
+                                        slower startup).
+    --[no-]sound-null-safety            Override the default null safety
+                                        execution mode.
+-C, --directory=<dir>                   Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-run for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub token --help.txt b/test/testdata/goldens/help_test/pub token --help.txt
new file mode 100644
index 0000000..fa164c5
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub token --help.txt
@@ -0,0 +1,16 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub token --help
+Manage authentication tokens for hosted pub repositories.
+
+Usage: pub token [arguments...]
+-h, --help    Print this usage information.
+
+Available subcommands:
+  add      Add authentication tokens for a package repository.
+  list     List servers for which a token exists.
+  remove   Remove secret token for package repository.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub token add --help.txt b/test/testdata/goldens/help_test/pub token add --help.txt
new file mode 100644
index 0000000..7068a5c
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub token add --help.txt
@@ -0,0 +1,13 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub token add --help
+Add authentication tokens for a package repository.
+
+Usage: pub token add
+-h, --help       Print this usage information.
+    --env-var    Read the secret token from this environment variable when
+                 making requests.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub token list --help.txt b/test/testdata/goldens/help_test/pub token list --help.txt
new file mode 100644
index 0000000..5c333b5
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub token list --help.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub token list --help
+List servers for which a token exists.
+
+Usage: pub token list
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub token remove --help.txt b/test/testdata/goldens/help_test/pub token remove --help.txt
new file mode 100644
index 0000000..1f79f81
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub token remove --help.txt
@@ -0,0 +1,12 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub token remove --help
+Remove secret token for package repository.
+
+Usage: pub token remove
+-h, --help    Print this usage information.
+    --all     Remove all secret tokens.
+
+Run "pub help" to see global options.
+
diff --git a/test/testdata/goldens/help_test/pub upgrade --help.txt b/test/testdata/goldens/help_test/pub upgrade --help.txt
new file mode 100644
index 0000000..d1a29b9
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub upgrade --help.txt
@@ -0,0 +1,21 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub upgrade --help
+Upgrade the current package's dependencies to latest versions.
+
+Usage: pub upgrade [dependencies...]
+-h, --help               Print this usage information.
+    --[no-]offline       Use cached packages instead of accessing the network.
+-n, --dry-run            Report what dependencies would change but don't change
+                         any.
+    --[no-]precompile    Precompile executables in immediate dependencies.
+    --null-safety        Upgrade constraints in pubspec.yaml to null-safety
+                         versions
+    --major-versions     Upgrades packages to their latest resolvable versions,
+                         and updates pubspec.yaml.
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-upgrade for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub uploader --help.txt b/test/testdata/goldens/help_test/pub uploader --help.txt
new file mode 100644
index 0000000..10c9d9d
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub uploader --help.txt
@@ -0,0 +1,15 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub uploader --help
+Manage uploaders for a package on pub.dartlang.org.
+
+Usage: pub uploader [options] {add/remove} <email>
+-h, --help               Print this usage information.
+    --package            The package whose uploaders will be modified.
+                         (defaults to the current package)
+-C, --directory=<dir>    Run this in the directory<dir>.
+
+Run "pub help" to see global options.
+See https://dart.dev/tools/pub/cmd/pub-uploader for detailed documentation.
+
diff --git a/test/testdata/goldens/help_test/pub version --help.txt b/test/testdata/goldens/help_test/pub version --help.txt
new file mode 100644
index 0000000..82e9f9f
--- /dev/null
+++ b/test/testdata/goldens/help_test/pub version --help.txt
@@ -0,0 +1,11 @@
+# GENERATED BY: test/help_test.dart
+
+## Section 0
+$ pub version --help
+Print pub version.
+
+Usage: pub version
+-h, --help    Print this usage information.
+
+Run "pub help" to see global options.
+
diff --git a/test/outdated/goldens/handles_sdk_dependencies.txt b/test/testdata/goldens/outdated/outdated_test/Handles SDK dependencies.txt
similarity index 84%
rename from test/outdated/goldens/handles_sdk_dependencies.txt
rename to test/testdata/goldens/outdated/outdated_test/Handles SDK dependencies.txt
index aba2f87..c9bc73f 100644
--- a/test/outdated/goldens/handles_sdk_dependencies.txt
+++ b/test/testdata/goldens/outdated/outdated_test/Handles SDK dependencies.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -19,6 +22,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -33,6 +39,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -47,6 +56,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -63,6 +75,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -77,6 +92,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -89,6 +107,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -103,6 +124,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -119,6 +143,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -135,6 +162,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -151,6 +181,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `flutter pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -195,6 +228,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/outdated/goldens/circular_dependencies.txt b/test/testdata/goldens/outdated/outdated_test/circular dependency on root.txt
similarity index 80%
rename from test/outdated/goldens/circular_dependencies.txt
rename to test/testdata/goldens/outdated/outdated_test/circular dependency on root.txt
index 27b6202..b9ab0a5 100644
--- a/test/outdated/goldens/circular_dependencies.txt
+++ b/test/testdata/goldens/outdated/outdated_test/circular dependency on root.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -19,6 +22,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -31,6 +37,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -43,6 +52,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -55,6 +67,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -67,6 +82,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -79,6 +97,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -91,6 +112,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -104,6 +128,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -117,6 +144,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -130,6 +160,9 @@
 1 upgradable dependency is locked (in pubspec.lock) to an older version.
 To update it, use `dart pub upgrade`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -155,6 +188,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt b/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt
new file mode 100644
index 0000000..6ab6163
--- /dev/null
+++ b/test/testdata/goldens/outdated/outdated_test/does not allow arguments - handles bad flags.txt
@@ -0,0 +1,64 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
+$ pub outdated random_argument
+[STDERR] Command "outdated" does not take any arguments.
+[STDERR] 
+[STDERR] Usage: pub outdated [options]
+[STDERR] -h, --help                         Print this usage information.
+[STDERR]     --[no-]color                   Whether to color the output.
+[STDERR]                                    Defaults to color when connected to a
+[STDERR]                                    terminal, and no-color otherwise.
+[STDERR]     --[no-]dependency-overrides    Show resolutions with `dependency_overrides`.
+[STDERR]                                    (defaults to on)
+[STDERR]     --[no-]dev-dependencies        Take dev dependencies into account.
+[STDERR]                                    (defaults to on)
+[STDERR]     --json                         Output the results using a json format.
+[STDERR]     --mode=<PROPERTY>              Highlight versions with PROPERTY.
+[STDERR]                                    Only packages currently missing that PROPERTY
+[STDERR]                                    will be included unless --show-all.
+[STDERR]                                    [outdated (default), null-safety]
+[STDERR]     --[no-]prereleases             Include prereleases in latest version.
+[STDERR]                                    (defaults to on in --mode=null-safety).
+[STDERR]     --[no-]show-all                Include dependencies that are already
+[STDERR]                                    fullfilling --mode.
+[STDERR]     --[no-]transitive              Show transitive dependencies.
+[STDERR]                                    (defaults to off in --mode=null-safety).
+[STDERR] -C, --directory=<dir>              Run this in the directory<dir>.
+[STDERR] 
+[STDERR] Run "pub help" to see global options.
+[STDERR] See https://dart.dev/tools/pub/cmd/pub-outdated for detailed documentation.
+[EXIT CODE] 64
+
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
+$ pub outdated --bad_flag
+[STDERR] Could not find an option named "bad_flag".
+[STDERR] 
+[STDERR] Usage: pub outdated [options]
+[STDERR] -h, --help                         Print this usage information.
+[STDERR]     --[no-]color                   Whether to color the output.
+[STDERR]                                    Defaults to color when connected to a
+[STDERR]                                    terminal, and no-color otherwise.
+[STDERR]     --[no-]dependency-overrides    Show resolutions with `dependency_overrides`.
+[STDERR]                                    (defaults to on)
+[STDERR]     --[no-]dev-dependencies        Take dev dependencies into account.
+[STDERR]                                    (defaults to on)
+[STDERR]     --json                         Output the results using a json format.
+[STDERR]     --mode=<PROPERTY>              Highlight versions with PROPERTY.
+[STDERR]                                    Only packages currently missing that PROPERTY
+[STDERR]                                    will be included unless --show-all.
+[STDERR]                                    [outdated (default), null-safety]
+[STDERR]     --[no-]prereleases             Include prereleases in latest version.
+[STDERR]                                    (defaults to on in --mode=null-safety).
+[STDERR]     --[no-]show-all                Include dependencies that are already
+[STDERR]                                    fullfilling --mode.
+[STDERR]     --[no-]transitive              Show transitive dependencies.
+[STDERR]                                    (defaults to off in --mode=null-safety).
+[STDERR] -C, --directory=<dir>              Run this in the directory<dir>.
+[STDERR] 
+[STDERR] Run "pub help" to see global options.
+[STDERR] See https://dart.dev/tools/pub/cmd/pub-outdated for detailed documentation.
+[EXIT CODE] 64
+
diff --git a/test/outdated/goldens/prereleases.txt b/test/testdata/goldens/outdated/outdated_test/latest version reported while locked on a prerelease can be a prerelease.txt
similarity index 87%
rename from test/outdated/goldens/prereleases.txt
rename to test/testdata/goldens/outdated/outdated_test/latest version reported while locked on a prerelease can be a prerelease.txt
index 8b91475..12c2ee3 100644
--- a/test/outdated/goldens/prereleases.txt
+++ b/test/testdata/goldens/outdated/outdated_test/latest version reported while locked on a prerelease can be a prerelease.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -34,6 +37,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -47,6 +53,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -60,6 +69,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -74,6 +86,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -88,6 +103,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -101,6 +119,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -114,6 +135,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -129,6 +153,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -144,6 +171,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -159,6 +189,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -222,6 +255,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/outdated/goldens/mutually_incompatible.txt b/test/testdata/goldens/outdated/outdated_test/mutually incompatible newer versions.txt
similarity index 84%
rename from test/outdated/goldens/mutually_incompatible.txt
rename to test/testdata/goldens/outdated/outdated_test/mutually incompatible newer versions.txt
index ae40331..18baa88 100644
--- a/test/outdated/goldens/mutually_incompatible.txt
+++ b/test/testdata/goldens/outdated/outdated_test/mutually incompatible newer versions.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -34,6 +37,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -46,6 +52,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -58,6 +67,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -70,6 +82,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -82,6 +97,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -94,6 +112,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -106,6 +127,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -119,6 +143,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -132,6 +159,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -145,6 +175,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -189,6 +222,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/outdated/goldens/newer_versions.txt b/test/testdata/goldens/outdated/outdated_test/newer versions available.txt
similarity index 91%
rename from test/outdated/goldens/newer_versions.txt
rename to test/testdata/goldens/outdated/outdated_test/newer versions available.txt
index 6e74a3e..d6c0ff9 100644
--- a/test/outdated/goldens/newer_versions.txt
+++ b/test/testdata/goldens/outdated/outdated_test/newer versions available.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -84,6 +87,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -110,6 +116,9 @@
 3  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -128,6 +137,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -156,6 +168,9 @@
 3  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -182,6 +197,9 @@
 3  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -200,6 +218,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -226,6 +247,9 @@
 3  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -247,6 +271,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -276,6 +303,9 @@
 3  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -297,6 +327,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -440,6 +473,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/outdated/goldens/no_dependencies.txt b/test/testdata/goldens/outdated/outdated_test/no dependencies.txt
similarity index 63%
rename from test/outdated/goldens/no_dependencies.txt
rename to test/testdata/goldens/outdated/outdated_test/no dependencies.txt
index 672c124..d256d7f 100644
--- a/test/outdated/goldens/no_dependencies.txt
+++ b/test/testdata/goldens/outdated/outdated_test/no dependencies.txt
@@ -1,44 +1,68 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": []
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -46,6 +70,9 @@
 
 All your dependencies declare support for null-safety.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -53,6 +80,9 @@
 
 All your dependencies declare support for null-safety.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -60,11 +90,17 @@
 
 All your dependencies declare support for null-safety.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": []
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": []
diff --git a/test/outdated/goldens/no_lockfile.txt b/test/testdata/goldens/outdated/outdated_test/no lockfile.txt
similarity index 86%
rename from test/outdated/goldens/no_lockfile.txt
rename to test/testdata/goldens/outdated/outdated_test/no lockfile.txt
index bca5ab7..7f670db 100644
--- a/test/outdated/goldens/no_lockfile.txt
+++ b/test/testdata/goldens/outdated/outdated_test/no lockfile.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -30,6 +33,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -46,6 +52,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -62,6 +71,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -78,6 +90,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -94,6 +109,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -110,6 +128,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -126,6 +147,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -143,6 +167,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -160,6 +187,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -177,6 +207,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -215,6 +248,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/testdata/goldens/outdated/outdated_test/no pubspec.txt b/test/testdata/goldens/outdated/outdated_test/no pubspec.txt
new file mode 100644
index 0000000..ba8858a
--- /dev/null
+++ b/test/testdata/goldens/outdated/outdated_test/no pubspec.txt
@@ -0,0 +1,7 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
+$ pub outdated
+[STDERR] Could not find a file named "pubspec.yaml" in "$SANDBOX/myapp".
+[EXIT CODE] 66
+
diff --git a/test/outdated/goldens/null_safety.txt b/test/testdata/goldens/outdated/outdated_test/null safety compliance.txt
similarity index 90%
rename from test/outdated/goldens/null_safety.txt
rename to test/testdata/goldens/outdated/outdated_test/null safety compliance.txt
index 9a9ce9c..d345f55 100644
--- a/test/outdated/goldens/null_safety.txt
+++ b/test/testdata/goldens/outdated/outdated_test/null safety compliance.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -94,6 +97,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -111,6 +117,9 @@
 6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -128,6 +137,9 @@
 6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -145,6 +157,9 @@
 6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -162,6 +177,9 @@
 6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -179,6 +197,9 @@
 6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -196,6 +217,9 @@
 6  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -210,6 +234,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -224,6 +251,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -238,6 +268,9 @@
 2  dependencies are constrained to versions that are older than a resolvable version.
 To update these dependencies, edit pubspec.yaml, or run `dart pub upgrade --null-safety`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -282,6 +315,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/outdated/goldens/null_safety_already_migrated.txt b/test/testdata/goldens/outdated/outdated_test/null-safety already migrated.txt
similarity index 74%
rename from test/outdated/goldens/null_safety_already_migrated.txt
rename to test/testdata/goldens/outdated/outdated_test/null-safety already migrated.txt
index b7ad48e..3d227ec 100644
--- a/test/outdated/goldens/null_safety_already_migrated.txt
+++ b/test/testdata/goldens/outdated/outdated_test/null-safety already migrated.txt
@@ -1,20 +1,32 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": []
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -32,24 +44,36 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -62,6 +86,9 @@
 dev_dependencies: all support null safety.
 All dependencies opt in to null-safety.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -77,6 +104,9 @@
 devTransitive  ✗1.0.0   ✗1.0.0      ✗1.0.0      ✗1.0.0  
 All dependencies opt in to null-safety.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -89,6 +119,9 @@
 dev_dependencies: all support null safety.
 All dependencies opt in to null-safety.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -114,6 +147,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": []
diff --git a/test/outdated/goldens/null_safety_no_resolution.txt b/test/testdata/goldens/outdated/outdated_test/null-safety no resolution.txt
similarity index 78%
rename from test/outdated/goldens/null_safety_no_resolution.txt
rename to test/testdata/goldens/outdated/outdated_test/null-safety no resolution.txt
index 89fb0e2..1ab20ad 100644
--- a/test/outdated/goldens/null_safety_no_resolution.txt
+++ b/test/testdata/goldens/outdated/outdated_test/null-safety no resolution.txt
@@ -1,20 +1,32 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": []
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -27,6 +39,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -39,18 +54,27 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
 
 Found no outdated packages
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -63,6 +87,9 @@
 foo           ✗1.0.0   ✗1.0.0      -           ✓2.0.0-nullsafety.0  
 No resolution was found. Try running `dart pub upgrade --null-safety --dry-run` to explore why.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -75,6 +102,9 @@
 foo           ✗1.0.0   ✗1.0.0      -           ✓2.0.0-nullsafety.0  
 No resolution was found. Try running `dart pub upgrade --null-safety --dry-run` to explore why.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -87,6 +117,9 @@
 foo           ✗1.0.0   ✗1.0.0      -           ✗1.0.0  
 No resolution was found. Try running `dart pub upgrade --null-safety --dry-run` to explore why.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -125,6 +158,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": []
diff --git a/test/outdated/goldens/dependency_overrides_no_solution.txt b/test/testdata/goldens/outdated/outdated_test/overridden dependencies - no resolution.txt
similarity index 86%
rename from test/outdated/goldens/dependency_overrides_no_solution.txt
rename to test/testdata/goldens/outdated/outdated_test/overridden dependencies - no resolution.txt
index 9efeb58..909ee0a 100644
--- a/test/outdated/goldens/dependency_overrides_no_solution.txt
+++ b/test/testdata/goldens/outdated/outdated_test/overridden dependencies - no resolution.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -40,6 +43,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -52,6 +58,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -64,6 +73,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -76,6 +88,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -88,6 +103,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -100,6 +118,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -111,6 +132,9 @@
 foo           *1.0.0 (overridden)  -           -           2.0.0   
 No resolution was found. Try running `dart pub upgrade --dry-run` to explore why.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -124,6 +148,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -137,6 +164,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -150,6 +180,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -200,6 +233,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/outdated/goldens/dependency_overrides.txt b/test/testdata/goldens/outdated/outdated_test/overridden dependencies.txt
similarity index 89%
rename from test/outdated/goldens/dependency_overrides.txt
rename to test/testdata/goldens/outdated/outdated_test/overridden dependencies.txt
index eb0c881..49abe1b 100644
--- a/test/outdated/goldens/dependency_overrides.txt
+++ b/test/testdata/goldens/outdated/outdated_test/overridden dependencies.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/outdated/outdated_test.dart
+
+## Section 0
 $ pub outdated --json
 {
   "packages": [
@@ -58,6 +61,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub outdated --no-color
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -71,6 +77,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 2
 $ pub outdated --no-color --no-transitive
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -84,6 +93,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 3
 $ pub outdated --no-color --up-to-date
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -97,6 +109,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 4
 $ pub outdated --no-color --prereleases
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -110,6 +125,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 5
 $ pub outdated --no-color --no-dev-dependencies
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -123,6 +141,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 6
 $ pub outdated --no-color --no-dependency-overrides
 Showing outdated packages.
 [*] indicates versions that are not the latest available.
@@ -140,6 +161,9 @@
 1 dependency is constrained to a version that is older than a resolvable version.
 To update it, edit pubspec.yaml, or run `dart pub upgrade --major-versions`.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 7
 $ pub outdated --no-color --mode=null-safety
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -154,6 +178,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 8
 $ pub outdated --no-color --mode=null-safety --transitive
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -168,6 +195,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 9
 $ pub outdated --no-color --mode=null-safety --no-prereleases
 Showing dependencies that are currently not opted in to null-safety.
 [✗] indicates versions without null safety support.
@@ -182,6 +212,9 @@
 You are already using the newest resolvable versions listed in the 'Resolvable' column.
 Newer versions, listed in 'Latest', may not be mutually compatible.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 10
 $ pub outdated --json --mode=null-safety
 {
   "packages": [
@@ -254,6 +287,9 @@
   ]
 }
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 11
 $ pub outdated --json --no-dev-dependencies
 {
   "packages": [
diff --git a/test/goldens/upgrade_major_versions_example.txt b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt
similarity index 60%
rename from test/goldens/upgrade_major_versions_example.txt
rename to test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt
index 448a874..2cc96c0 100644
--- a/test/goldens/upgrade_major_versions_example.txt
+++ b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --major-versions does not update major versions in example~.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/upgrade/example_warns_about_major_versions_test.dart
+
+## Section 0
 $ pub upgrade --major-versions --example
 Resolving dependencies...
 + bar 2.0.0
@@ -7,8 +10,11 @@
   bar: ^1.0.0 -> ^2.0.0
 Resolving dependencies in ./example...
 Got dependencies in ./example.
-[ERR] Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory example/` separately.
+[STDERR] Running `upgrade --major-versions` only in `.`. Run `dart pub upgrade --major-versions --directory example/` separately.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub upgrade --major-versions --directory example
 Resolving dependencies in example...
   bar 2.0.0
diff --git a/test/goldens/upgrade_null_safety_example.txt b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --null-safety does not update null-safety of dependencies in example~.txt
similarity index 62%
rename from test/goldens/upgrade_null_safety_example.txt
rename to test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --null-safety does not update null-safety of dependencies in example~.txt
index 5c5b31c..cabc0be 100644
--- a/test/goldens/upgrade_null_safety_example.txt
+++ b/test/testdata/goldens/upgrade/example_warns_about_major_versions_test/pub upgrade --null-safety does not update null-safety of dependencies in example~.txt
@@ -1,3 +1,6 @@
+# GENERATED BY: test/upgrade/example_warns_about_major_versions_test.dart
+
+## Section 0
 $ pub upgrade --null-safety --example
 Resolving dependencies...
 + bar 2.0.0
@@ -7,8 +10,11 @@
   bar: ^1.0.0 -> ^2.0.0
 Resolving dependencies in ./example...
 Got dependencies in ./example.
-[ERR] Running `upgrade --null-safety` only in `.`. Run `dart pub upgrade --null-safety --directory example/` separately.
+[STDERR] Running `upgrade --null-safety` only in `.`. Run `dart pub upgrade --null-safety --directory example/` separately.
 
+-------------------------------- END OF OUTPUT ---------------------------------
+
+## Section 1
 $ pub upgrade --null-safety --directory example
 Resolving dependencies in example...
 > bar 2.0.0 (was 1.0.0)
diff --git a/test/upgrade/example_warns_about_major_versions_test.dart b/test/upgrade/example_warns_about_major_versions_test.dart
index 0813ed2..259e787 100644
--- a/test/upgrade/example_warns_about_major_versions_test.dart
+++ b/test/upgrade/example_warns_about_major_versions_test.dart
@@ -4,16 +4,14 @@
 
 // @dart = 2.11
 
-import 'package:test/test.dart';
-
 import '../descriptor.dart' as d;
 import '../golden_file.dart';
 import '../test_pub.dart';
 
 void main() {
-  test(
+  testWithGolden(
       'pub upgrade --major-versions does not update major versions in example/',
-      () async {
+      (ctx) async {
     await servePackages((b) => b
       ..serve('foo', '1.0.0')
       ..serve('foo', '2.0.0')
@@ -36,23 +34,13 @@
       ])
     ]).create();
 
-    final buffer = StringBuffer();
-    await runPubIntoBuffer(
-      ['upgrade', '--major-versions', '--example'],
-      buffer,
-    );
-    await runPubIntoBuffer(
-      ['upgrade', '--major-versions', '--directory', 'example'],
-      buffer,
-    );
-
-    expectMatchesGoldenFile(
-        buffer.toString(), 'test/goldens/upgrade_major_versions_example.txt');
+    await ctx.run(['upgrade', '--major-versions', '--example']);
+    await ctx.run(['upgrade', '--major-versions', '--directory', 'example']);
   });
 
-  test(
+  testWithGolden(
       'pub upgrade --null-safety does not update null-safety of dependencies in example/',
-      () async {
+      (ctx) async {
     await servePackages((b) => b
       ..serve('foo', '1.0.0', pubspec: {
         'environment': {'sdk': '>=2.7.0 <3.0.0'},
@@ -86,20 +74,14 @@
       ])
     ]).create();
 
-    final buffer = StringBuffer();
-    await runPubIntoBuffer(
+    await ctx.run(
       ['upgrade', '--null-safety', '--example'],
-      buffer,
       environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'},
     );
 
-    await runPubIntoBuffer(
+    await ctx.run(
       ['upgrade', '--null-safety', '--directory', 'example'],
-      buffer,
       environment: {'_PUB_TEST_SDK_VERSION': '2.13.0'},
     );
-
-    expectMatchesGoldenFile(
-        buffer.toString(), 'test/goldens/upgrade_null_safety_example.txt');
   });
 }
diff --git a/test/upgrade/hosted/warn_about_discontinued_test.dart b/test/upgrade/hosted/warn_about_discontinued_test.dart
new file mode 100644
index 0000000..426e14d
--- /dev/null
+++ b/test/upgrade/hosted/warn_about_discontinued_test.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+// @dart=2.10
+import 'package:test/test.dart';
+
+import '../../descriptor.dart' as d;
+import '../../test_pub.dart';
+
+void main() {
+  test('Warns about discontinued dependencies', () async {
+    await servePackages((builder) => builder
+      ..serve('foo', '1.2.3', deps: {'transitive': 'any'})
+      ..serve('transitive', '1.0.0'));
+    await d.appDir({'foo': '1.2.3'}).create();
+    await pubGet();
+
+    globalPackageServer.add((builder) => builder
+      ..discontinue('foo')
+      ..discontinue('transitive'));
+    // We warn only about the direct dependency here:
+    await pubUpgrade(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued)
+  transitive 1.0.0
+  No dependencies changed.
+  1 package is discontinued.
+''');
+    globalPackageServer
+        .add((builder) => builder.discontinue('foo', replacementText: 'bar'));
+    // We warn only about the direct dependency here:
+    await pubUpgrade(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued replaced by bar)
+  transitive 1.0.0
+  No dependencies changed.
+  1 package is discontinued.
+''');
+  });
+
+  test('Warns about discontinued dev_dependencies', () async {
+    await servePackages((builder) => builder
+      ..serve('foo', '1.2.3', deps: {'transitive': 'any'})
+      ..serve('transitive', '1.0.0'));
+
+    await d.dir(appPath, [
+      d.file('pubspec.yaml', '''
+name: myapp
+dependencies:
+
+dev_dependencies:
+  foo: 1.2.3
+environment:
+  sdk: '>=0.1.2 <1.0.0'
+''')
+    ]).create();
+    await pubGet();
+
+    globalPackageServer.add((builder) => builder
+      ..discontinue('foo')
+      ..discontinue('transitive'));
+
+    // We warn only about the direct dependency here:
+    await pubUpgrade(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued)
+    transitive 1.0.0
+  No dependencies changed.
+  1 package is discontinued.
+''');
+    globalPackageServer
+        .add((builder) => builder.discontinue('foo', replacementText: 'bar'));
+    // We warn only about the direct dependency here:
+    await pubUpgrade(output: '''
+Resolving dependencies...
+  foo 1.2.3 (discontinued replaced by bar)
+  transitive 1.0.0
+  No dependencies changed.
+  1 package is discontinued.
+''');
+  });
+}
diff --git a/test/version_solver_test.dart b/test/version_solver_test.dart
index caca301..cf6f65f 100644
--- a/test/version_solver_test.dart
+++ b/test/version_solver_test.dart
@@ -3003,8 +3003,9 @@
   for (var dep in resultPubspec.dependencies.values) {
     expect(ids, contains(dep.name));
     var id = ids.remove(dep.name);
+    final source = dep.source;
 
-    if (dep.source is HostedSource && dep.description is String) {
+    if (source is HostedSource && (dep.description.uri == source.defaultUrl)) {
       // If the dep uses the default hosted source, grab it from the test
       // package server rather than pub.dartlang.org.
       dep = registry.hosted