Version 2.12.0-26.0.dev

Merge commit '7322bc027e43390c86cd84d2f8f720361a74f00c' into 'dev'
diff --git a/pkg/analysis_server/test/edit/bulk_fixes_test.dart b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
index 5ad4e47..7f22771 100644
--- a/pkg/analysis_server/test/edit/bulk_fixes_test.dart
+++ b/pkg/analysis_server/test/edit/bulk_fixes_test.dart
@@ -148,6 +148,30 @@
 ''');
   }
 
+  @FailingTest(issue: 'https://github.com/dart-lang/sdk/issues/44080')
+  Future<void> test_unnecessaryNew_collectionLiteral_overlap() async {
+    addAnalysisOptionsFile('''
+linter:
+  rules:
+    - prefer_collection_literals
+    - unnecessary_new
+''');
+
+    addTestFile('''
+class A {
+  Map<String, Object> _map = {};
+  Set<String> _set = new Set<String>();
+}
+''');
+
+    await assertEditEquals('''
+class A {
+  Map<String, Object> _map = {};
+  Set<String> _set = <String>{};
+}
+''');
+  }
+
   Future<void> test_unnecessaryNew_ignoredInOptions() async {
     addAnalysisOptionsFile('''
 analyzer:
diff --git a/pkg/dartdev/test/utils.dart b/pkg/dartdev/test/utils.dart
index e1a3cd2..4d67099 100644
--- a/pkg/dartdev/test/utils.dart
+++ b/pkg/dartdev/test/utils.dart
@@ -70,9 +70,6 @@
   }) {
     var arguments = [
       command,
-      if (command == 'migrate')
-        // TODO(srawlins): Enable `pub outdated` in tests.
-        '--skip-pub-outdated',
       ...?args,
     ];
 
diff --git a/pkg/nnbd_migration/lib/migration_cli.dart b/pkg/nnbd_migration/lib/migration_cli.dart
index 43a71e6..8f0d07d 100644
--- a/pkg/nnbd_migration/lib/migration_cli.dart
+++ b/pkg/nnbd_migration/lib/migration_cli.dart
@@ -3,8 +3,6 @@
 // BSD-style license that can be found in the LICENSE file.
 
 import 'dart:async';
-import 'dart:convert' show jsonDecode;
-import 'dart:math';
 import 'dart:io' hide File;
 
 import 'package:analyzer/dart/analysis/analysis_context_collection.dart';
@@ -33,7 +31,7 @@
 import 'package:nnbd_migration/src/front_end/migration_state.dart';
 import 'package:nnbd_migration/src/front_end/non_nullable_fix.dart';
 import 'package:nnbd_migration/src/messages.dart';
-import 'package:nnbd_migration/src/utilities/json.dart' as json;
+import 'package:nnbd_migration/src/utilities/progress_bar.dart';
 import 'package:nnbd_migration/src/utilities/source_edit_diff_formatter.dart';
 import 'package:path/path.dart' show Context;
 
@@ -100,6 +98,10 @@
   static const previewHostnameOption = 'preview-hostname';
   static const previewPortOption = 'preview-port';
   static const sdkPathOption = 'sdk-path';
+  static const skipImportCheckFlag = 'skip-import-check';
+
+  /// TODO(paulberry): remove this flag once internal sources have been updated.
+  @Deprecated('The migration tool no longer performs "pub outdated" checks')
   static const skipPubOutdatedFlag = 'skip-pub-outdated';
   static const summaryOption = 'summary';
   static const verboseFlag = 'verbose';
@@ -119,105 +121,43 @@
 
   final String sdkPath;
 
-  final bool skipPubOutdated;
+  final bool skipImportCheck;
 
   final String summary;
 
   final bool webPreview;
 
   CommandLineOptions(
-      {@required this.applyChanges,
-      @required this.directory,
-      @required this.ignoreErrors,
-      @required this.ignoreExceptions,
-      @required this.previewHostname,
-      @required this.previewPort,
-      @required this.sdkPath,
-      @required this.skipPubOutdated,
-      @required this.summary,
-      @required this.webPreview});
-}
-
-@visibleForTesting
-class DependencyChecker {
-  /// The directory which contains the package being migrated.
-  final String _directory;
-  final Context _pathContext;
-  final Logger _logger;
-  final ProcessManager _processManager;
-
-  DependencyChecker(
-      this._directory, this._pathContext, this._logger, this._processManager);
-
-  bool check() {
-    var pubPath = _pathContext.join(getSdkPath(), 'bin', 'dart');
-    var pubArguments = ['pub', 'outdated', '--mode=null-safety', '--json'];
-    var preNullSafetyPackages = <String, String>{};
-    try {
-      var result = _processManager.runSync(pubPath, pubArguments,
-          workingDirectory: _directory);
-      if ((result.stderr as String).isNotEmpty) {
-        throw FormatException(
-            '`dart pub outdated --mode=null-safety` exited with exit code '
-            '${result.exitCode} and stderr:\n\n${result.stderr}');
-      }
-      var outdatedOutput = jsonDecode(result.stdout as String);
-      var outdatedMap = json.expectType<Map>(outdatedOutput, 'root');
-      var packageList =
-          json.expectType<List>(outdatedMap['packages'], 'packages');
-      for (var package_ in packageList) {
-        var package = json.expectType<Map>(package_, '');
-        var current_ = json.expectKey(package, 'current');
-        if (current_ == null) {
-          continue;
-        }
-        var current = json.expectType<Map>(current_, 'current');
-        if (json.expectType<bool>(current['nullSafety'], 'nullSafety')) {
-          // For whatever reason, there is no "current" version of this package.
-          // TODO(srawlins): We may want to report this to the user. But it may
-          // be inconsequential.
-          continue;
-        }
-
-        json.expectKey(package, 'package');
-        json.expectKey(current, 'version');
-        var name = json.expectType<String>(package['package'], 'package');
-        // A version will be given, even if a package was provided with a local
-        // or git path.
-        var version = json.expectType<String>(current['version'], 'version');
-        preNullSafetyPackages[name] = version;
-      }
-    } on ProcessException catch (e) {
-      _logger.stderr(
-          'Warning: Could not execute `$pubPath ${pubArguments.join(' ')}`: '
-          '"${e.message}"');
-      // Allow the program to continue; users should be allowed to attempt to
-      // migrate when `pub outdated` is misbehaving, or if there is a bug above.
-    } on FormatException catch (e) {
-      _logger.stderr('Warning: ${e.message}');
-      // Allow the program to continue; users should be allowed to attempt to
-      // migrate when `pub outdated` is misbehaving, or if there is a bug above.
-    }
-    if (preNullSafetyPackages.isNotEmpty) {
-      _logger.stderr('Warning: not all current dependencies have migrated to '
-          'null safety:');
-      _logger.stderr('');
-      for (var package in preNullSafetyPackages.entries) {
-        _logger.stderr(
-            '  package:${package.key} (currently at version ${package.value})');
-      }
-      _logger.stderr('');
-      _logger.stderr('For the best migration experience, please update to null '
-          'safe versions of these packages before migrating your code. You can '
-          'use \'dart pub outdated --mode=null-safety\' to check the status of '
-          'dependencies.');
-      _logger.stderr('');
-      _logger.stderr('Visit https://dart.dev/tools/pub/cmd/pub-outdated for '
-          'more information.');
-      return false;
-    }
-    return true;
-  }
+      {@required
+          this.applyChanges,
+      @required
+          this.directory,
+      @required
+          this.ignoreErrors,
+      @required
+          this.ignoreExceptions,
+      @required
+          this.previewHostname,
+      @required
+          this.previewPort,
+      @required
+          this.sdkPath,
+      // TODO(paulberry): make this parameter required once internal sources
+      // have been updated.
+      bool skipImportCheck,
+      // TODO(paulberry): remove this flag once internal sources have been
+      // updated.
+      @Deprecated('The migration tool no longer performs "pub outdated" checks')
+          bool skipPubOutdated = false,
+      @required
+          this.summary,
+      @required
+          this.webPreview})
+      // `skipImportCheck` has replaced `skipPubOutdated`, so if the caller
+      // specifies the latter but not the former, carry it over.
+      // TODO(paulberry): remove this logic once internal sources have been
+      // updated.
+      : skipImportCheck = skipImportCheck ?? skipPubOutdated;
 }
 
 // TODO(devoncarew): Refactor so this class extends DartdevCommand.
@@ -312,15 +252,13 @@
                   'analysis errors.',
             )),
     MigrationCliOption(
-        CommandLineOptions.skipPubOutdatedFlag,
+        CommandLineOptions.skipImportCheckFlag,
         (parser, hide) => parser.addFlag(
-              CommandLineOptions.skipPubOutdatedFlag,
+              CommandLineOptions.skipImportCheckFlag,
               defaultsTo: false,
               negatable: false,
-              help:
-                  'Skip the `pub outdated --mode=null-safety` check. This allows a '
-                  'migration to proceed even if some package dependencies have not yet '
-                  'been migrated.',
+              help: 'Go ahead with migration even if some imported files have '
+                  'not yet been migrated.',
             )),
     MigrationCliOption.separator('Web interface options:'),
     MigrationCliOption(
@@ -393,11 +331,6 @@
   /// user.  Used in testing to allow user feedback messages to be tested.
   final Logger Function(bool isVerbose) loggerFactory;
 
-  /// Process manager that should be used to run processes. Used in testing to
-  /// redirect to mock processes.
-  @visibleForTesting
-  final ProcessManager processManager;
-
   /// Resource provider that should be used to access the filesystem.  Used in
   /// testing to redirect to an in-memory filesystem.
   final ResourceProvider resourceProvider;
@@ -414,7 +347,6 @@
     @visibleForTesting this.loggerFactory = _defaultLoggerFactory,
     @visibleForTesting this.defaultSdkPathOverride,
     @visibleForTesting ResourceProvider resourceProvider,
-    @visibleForTesting this.processManager = const ProcessManager.system(),
     @visibleForTesting Map<String, String> environmentVariables,
   })  : logger = loggerFactory(false),
         resourceProvider =
@@ -491,8 +423,8 @@
           sdkPath: argResults[CommandLineOptions.sdkPathOption] as String ??
               defaultSdkPathOverride ??
               getSdkPath(),
-          skipPubOutdated:
-              argResults[CommandLineOptions.skipPubOutdatedFlag] as bool,
+          skipImportCheck:
+              argResults[CommandLineOptions.skipImportCheckFlag] as bool,
           summary: argResults[CommandLineOptions.summaryOption] as String,
           webPreview: webPreview);
       return MigrationCliRunner(this, options,
@@ -695,7 +627,8 @@
       int preferredPort,
       String summaryPath,
       @required String sdkPath}) {
-    return NonNullableFix(listener, resourceProvider, getLineInfo, bindAddress,
+    return NonNullableFix(
+        listener, resourceProvider, getLineInfo, bindAddress, logger,
         included: included,
         preferredPort: preferredPort,
         summaryPath: summaryPath,
@@ -707,10 +640,6 @@
   /// If something goes wrong, a message is printed using the logger configured
   /// in the constructor, and [MigrationExit] is thrown.
   Future<void> run() async {
-    if (!options.skipPubOutdated) {
-      _checkDependencies();
-    }
-
     logger.stdout('Migrating ${options.directory}');
     logger.stdout('');
 
@@ -862,15 +791,6 @@
     applyHook();
   }
 
-  void _checkDependencies() {
-    var successful = DependencyChecker(
-            options.directory, pathContext, logger, cli.processManager)
-        .check();
-    if (!successful) {
-      throw MigrationExit(1);
-    }
-  }
-
   void _displayChangeDiff(DartFixListener migrationResults) {
     Map<String, List<DartFixSuggestion>> fileSuggestions = {};
     for (DartFixSuggestion suggestion in migrationResults.suggestions) {
@@ -1064,7 +984,7 @@
 
   Set<String> pathsToProcess;
 
-  _ProgressBar _progressBar;
+  ProgressBar _progressBar;
 
   final MigrationCliRunner _migrationCli;
 
@@ -1131,7 +1051,7 @@
     var analysisErrors = <AnalysisError>[];
 
     // All tasks should be registered; [numPhases] should be finalized.
-    _progressBar = _ProgressBar(_migrationCli.logger, pathsToProcess.length);
+    _progressBar = ProgressBar(_migrationCli.logger, pathsToProcess.length);
 
     // Process each source file.
     await processResources((ResolvedUnitResult result) async {
@@ -1148,12 +1068,22 @@
       }
     });
 
+    var unmigratedDependencies = _task.migration.unmigratedDependencies;
+    if (unmigratedDependencies.isNotEmpty) {
+      if (_migrationCli.options.skipImportCheck) {
+        _migrationCli.logger.stdout(unmigratedDependenciesWarning);
+      } else {
+        throw ExperimentStatusException.unmigratedDependencies(
+            unmigratedDependencies);
+      }
+    }
+
     return AnalysisResult(analysisErrors, _migrationCli.lineInfo,
         _migrationCli.pathContext, _migrationCli.options.directory);
   }
 
   Future<MigrationState> runLaterPhases() async {
-    _progressBar = _ProgressBar(
+    _progressBar = ProgressBar(
         _migrationCli.logger, pathsToProcess.length * (numPhases - 1));
 
     await processResources((ResolvedUnitResult result) async {
@@ -1166,11 +1096,13 @@
         await _task.finalizeUnit(result);
       }
     });
+    _progressBar.complete();
+    _migrationCli.logger.stdout(_migrationCli.ansi
+        .emphasized('Compiling instrumentation information...'));
     var state = await _task.finish();
     if (_migrationCli.options.webPreview) {
       await _task.startPreviewServer(state, _migrationCli.applyHook);
     }
-    _progressBar.complete();
     state.previewUrls = _task.previewUrls;
 
     return state;
@@ -1204,83 +1136,6 @@
   }
 }
 
-/// A facility for drawing a progress bar in the terminal.
-///
-/// The bar is instantiated with the total number of "ticks" to be completed,
-/// and progress is made by calling [tick]. The bar is drawn across one entire
-/// line, like so:
-///
-///     [----------                                                   ]
-///
-/// The hyphens represent completed progress, and the whitespace represents
-/// remaining progress.
-///
-/// If there is no terminal, the progress bar will not be drawn.
-class _ProgressBar {
-  /// Whether the progress bar should be drawn.
-  /*late*/ bool _shouldDrawProgress;
-
-  /// The width of the terminal, in terms of characters.
-  /*late*/
-  int _width;
-
-  final Logger _logger;
-
-  /// The inner width of the terminal, in terms of characters.
-  ///
-  /// This represents the number of characters available for drawing progress.
-  /*late*/
-  int _innerWidth;
-
-  final int _totalTickCount;
-
-  int _tickCount = 0;
-
-  _ProgressBar(this._logger, this._totalTickCount) {
-    if (!stdout.hasTerminal) {
-      _shouldDrawProgress = false;
-    } else {
-      _shouldDrawProgress = true;
-      _width = stdout.terminalColumns;
-      _innerWidth = stdout.terminalColumns - 2;
-      _logger.write('[' + ' ' * _innerWidth + ']');
-    }
-  }
-
-  /// Clear the progress bar from the terminal, allowing other logging to be
-  /// printed.
-  void clear() {
-    if (!_shouldDrawProgress) {
-      return;
-    }
-    _logger.write('\r' + ' ' * _width + '\r');
-  }
-
-  /// Draw the progress bar as complete, and print two newlines.
-  void complete() {
-    if (!_shouldDrawProgress) {
-      return;
-    }
-    _logger.write('\r[' + '-' * _innerWidth + ']\n\n');
-  }
-
-  /// Progress the bar by one tick.
-  void tick() {
-    if (!_shouldDrawProgress) {
-      return;
-    }
-    _tickCount++;
-    var fractionComplete =
-        max(0, _tickCount * _innerWidth ~/ _totalTickCount - 1);
-    var remaining = _innerWidth - fractionComplete - 1;
-    _logger.write('\r[' + // Bring cursor back to the start of the line.
-        '-' * fractionComplete + // Print complete work.
-        AnsiProgress.kAnimationItems[_tickCount % 4] + // Print spinner.
-        ' ' * remaining + // Print remaining work.
-        ']');
-  }
-}
-
 extension on Severity {
   /// Returns the simple name of the Severity, as a String.
   String get name {
diff --git a/pkg/nnbd_migration/lib/nnbd_migration.dart b/pkg/nnbd_migration/lib/nnbd_migration.dart
index 087685d..36027a0 100644
--- a/pkg/nnbd_migration/lib/nnbd_migration.dart
+++ b/pkg/nnbd_migration/lib/nnbd_migration.dart
@@ -334,6 +334,11 @@
 
   void finish();
 
+  /// Use this getter after any calls to [prepareInput] to obtain a list of URIs
+  /// of unmigrated dependencies.  Ideally, this list should be empty before the
+  /// user tries to migrate their package.
+  List<String> get unmigratedDependencies;
+
   void prepareInput(ResolvedUnitResult result);
 
   void processInput(ResolvedUnitResult result);
diff --git a/pkg/nnbd_migration/lib/src/exceptions.dart b/pkg/nnbd_migration/lib/src/exceptions.dart
index 15e26ed..580fcda 100644
--- a/pkg/nnbd_migration/lib/src/exceptions.dart
+++ b/pkg/nnbd_migration/lib/src/exceptions.dart
@@ -13,6 +13,10 @@
   /// The SDK does not contain the NNBD sources, it is the pre-unfork copy.
   ExperimentStatusException.sdkPreforkSources() : super(sdkNnbdOff);
 
+  /// The user's code imports unmigrated dependencies.
+  ExperimentStatusException.unmigratedDependencies(List<String> uris)
+      : super(unmigratedDependenciesError(uris));
+
   /// Throw an [ExperimentStatusException] if the [result] seems to have
   /// incorrectly configured experiment flags/nnbd sources.
   static void sanityCheck(ResolvedUnitResult result) {
diff --git a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
index b1d3f84..b5b8e56 100644
--- a/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/info_builder.dart
@@ -12,6 +12,7 @@
 import 'package:analyzer_plugin/protocol/protocol_common.dart' as protocol;
 import 'package:analyzer_plugin/src/utilities/navigation/navigation.dart';
 import 'package:analyzer_plugin/utilities/navigation/navigation_dart.dart';
+import 'package:cli_util/cli_logging.dart';
 import 'package:meta/meta.dart';
 import 'package:nnbd_migration/fix_reason_target.dart';
 import 'package:nnbd_migration/instrumentation.dart';
@@ -22,12 +23,16 @@
 import 'package:nnbd_migration/src/front_end/instrumentation_information.dart';
 import 'package:nnbd_migration/src/front_end/migration_info.dart';
 import 'package:nnbd_migration/src/front_end/offset_mapper.dart';
+import 'package:nnbd_migration/src/utilities/progress_bar.dart';
 
 /// A builder used to build the migration information for a library.
 class InfoBuilder {
   /// The node mapper for the migration state.
   NodeMapper nodeMapper;
 
+  /// The logger to use for showing progress when explaining the migration.
+  final Logger _logger;
+
   /// The resource provider used to access the file system.
   ResourceProvider provider;
 
@@ -49,7 +54,7 @@
 
   /// Initialize a newly created builder.
   InfoBuilder(this.provider, this.includedPath, this.info, this.listener,
-      this.migration, this.nodeMapper);
+      this.migration, this.nodeMapper, this._logger);
 
   /// The provider used to get information about libraries.
   DriverProviderImpl get driverProvider => listener.server;
@@ -60,7 +65,10 @@
     var sourceInfoMap = info.sourceInformation;
     Set<UnitInfo> units =
         SplayTreeSet<UnitInfo>((u1, u2) => u1.path.compareTo(u2.path));
+    var progressBar = ProgressBar(_logger, sourceInfoMap.length);
+
     for (var source in sourceInfoMap.keys) {
+      progressBar.tick();
       var filePath = source.fullName;
       var session = driverProvider.getAnalysisSession(filePath);
       if (!session.getFile(filePath).isPart) {
@@ -86,6 +94,7 @@
         }
       }
     }
+    progressBar.complete();
     return units;
   }
 
diff --git a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
index 8593172..bfbf7cd 100644
--- a/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/migration_state.dart
@@ -2,6 +2,7 @@
 // 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.
 
+import 'package:cli_util/cli_logging.dart';
 import 'package:nnbd_migration/instrumentation.dart';
 import 'package:nnbd_migration/migration_cli.dart';
 import 'package:nnbd_migration/nnbd_migration.dart';
@@ -41,16 +42,18 @@
 
   final AnalysisResult analysisResult;
 
+  /*late*/ List<String> previewUrls;
+
   /// Initialize a newly created migration state with the given values.
   MigrationState(this.migration, this.includedRoot, this.listener,
       this.instrumentationListener,
       [this.analysisResult]);
 
-  bool get hasErrors => analysisResult?.hasErrors ?? false;
-
   /// If the migration has been applied to disk.
   bool get hasBeenApplied => _hasBeenApplied;
 
+  bool get hasErrors => analysisResult?.hasErrors ?? false;
+
   /// Mark that the migration has been applied to disk.
   void markApplied() {
     assert(!hasBeenApplied);
@@ -58,17 +61,15 @@
   }
 
   /// Refresh the state of the migration after the migration has been updated.
-  Future<void> refresh() async {
+  Future<void> refresh(Logger logger) async {
     assert(!hasBeenApplied);
     var provider = listener.server.resourceProvider;
     var infoBuilder = InfoBuilder(provider, includedRoot,
-        instrumentationListener.data, listener, migration, nodeMapper);
+        instrumentationListener.data, listener, migration, nodeMapper, logger);
     var unitInfos = await infoBuilder.explainMigration();
     var pathContext = provider.pathContext;
     migrationInfo = MigrationInfo(
         unitInfos, infoBuilder.unitMap, pathContext, includedRoot);
     pathMapper = PathMapper(provider);
   }
-
-  /*late*/ List<String> previewUrls;
 }
diff --git a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
index 6148a95..8a5d668 100644
--- a/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
+++ b/pkg/nnbd_migration/lib/src/front_end/non_nullable_fix.dart
@@ -10,6 +10,7 @@
 import 'package:analyzer/file_system/file_system.dart';
 import 'package:analyzer/src/generated/source.dart';
 import 'package:analyzer_plugin/protocol/protocol_common.dart';
+import 'package:cli_util/cli_logging.dart';
 import 'package:meta/meta.dart';
 import 'package:nnbd_migration/nnbd_migration.dart';
 import 'package:nnbd_migration/src/front_end/charcodes.dart';
@@ -36,6 +37,8 @@
   /// [InternetAddress].
   final Object bindAddress;
 
+  final Logger _logger;
+
   final int preferredPort;
 
   final DartFixListener listener;
@@ -76,8 +79,8 @@
   /// A list of the URLs corresponding to the included roots.
   List<String> previewUrls;
 
-  NonNullableFix(
-      this.listener, this.resourceProvider, this._getLineInfo, this.bindAddress,
+  NonNullableFix(this.listener, this.resourceProvider, this._getLineInfo,
+      this.bindAddress, this._logger,
       {List<String> included = const [],
       this.preferredPort,
       this.summaryPath,
@@ -113,7 +116,7 @@
     migration.finish();
     final state = MigrationState(
         migration, includedRoot, listener, instrumentationListener);
-    await state.refresh();
+    await state.refresh(_logger);
     return state;
   }
 
@@ -167,7 +170,7 @@
   Future<MigrationState> rerun() async {
     reset();
     var state = await rerunFunction();
-    await state.refresh();
+    await state.refresh(_logger);
     return state;
   }
 
diff --git a/pkg/nnbd_migration/lib/src/messages.dart b/pkg/nnbd_migration/lib/src/messages.dart
index 41497f2..1305da4 100644
--- a/pkg/nnbd_migration/lib/src/messages.dart
+++ b/pkg/nnbd_migration/lib/src/messages.dart
@@ -2,6 +2,8 @@
 // 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.
 
+import 'package:nnbd_migration/migration_cli.dart';
+
 const String migratedAlready =
     "Seem to be migrating code that's already migrated";
 const String nnbdExperimentOff =
@@ -10,3 +12,29 @@
 const String sdkPathEnvironmentVariableSet =
     r'Note: $SDK_PATH environment variable is set and may point to outdated '
     'dart:core sources';
+const String _skipImportCheckFlag =
+    '--${CommandLineOptions.skipImportCheckFlag}';
+const String unmigratedDependenciesWarning = '''
+Warning: package has unmigrated dependencies.
+
+Continuing due to the presence of `$_skipImportCheckFlag`.  To see a complete
+ list of these libraries, re-run without the `$_skipImportCheckFlag` flag.
+''';
+
+String unmigratedDependenciesError(List<String> uris) => '''
+Error: package has unmigrated dependencies.
+
+Before migrating your package, we recommend ensuring that every library it
+imports (either directly or indirectly) has been migrated to null safety, so
+that you will be able to run your unit tests in sound null checking mode.  You
+are currently importing the following non-null-safe libraries:
+
+  ${uris.join('\n  ')}
+
+Please upgrade the packages containing these libraries to null safe versions
+before continuing.  To see what null safe package versions are available, run
+the following command: `dart pub outdated --mode=null-safety --prereleases`.
+
+To skip this check and try to migrate anyway, re-run with the flag
+`$_skipImportCheckFlag`.
+''';
diff --git a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
index 3878f61..f80f4c4 100644
--- a/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
+++ b/pkg/nnbd_migration/lib/src/nullability_migration_impl.dart
@@ -4,6 +4,7 @@
 
 import 'package:analyzer/dart/analysis/features.dart';
 import 'package:analyzer/dart/analysis/results.dart';
+import 'package:analyzer/dart/element/element.dart';
 import 'package:analyzer/file_system/physical_file_system.dart';
 import 'package:analyzer/src/dart/analysis/session.dart';
 import 'package:analyzer/src/dart/element/type_system.dart';
@@ -63,6 +64,13 @@
   /// parameter into their "OrNull" equivalents if possible.
   final bool transformWhereOrNull;
 
+  /// Map from [Source] object to a boolean indicating whether the source is
+  /// opted in to null safety.
+  final Map<Source, bool> _libraryOptInStatus = {};
+
+  /// Indicates whether the client has used the [unmigratedDependencies] getter.
+  bool _queriedUnmigratedDependencies = false;
+
   /// Prepares to perform nullability migration.
   ///
   /// If [permissive] is `true`, exception handling logic will try to proceed
@@ -115,6 +123,23 @@
   bool get isPermissive => _permissive;
 
   @override
+  List<String> get unmigratedDependencies {
+    _queriedUnmigratedDependencies = true;
+    var unmigratedDependencies = <Source>[];
+    for (var entry in _libraryOptInStatus.entries) {
+      if (_graph.isBeingMigrated(entry.key)) continue;
+      if (!entry.value) {
+        unmigratedDependencies.add(entry.key);
+      }
+    }
+    var badUris = {
+      for (var dependency in unmigratedDependencies) dependency.uri.toString()
+    }.toList();
+    badUris.sort();
+    return badUris;
+  }
+
+  @override
   void finalizeInput(ResolvedUnitResult result) {
     if (result.unit.featureSet.isEnabled(Feature.non_nullable)) {
       // This library has already been migrated; nothing more to do.
@@ -183,11 +208,16 @@
   }
 
   void prepareInput(ResolvedUnitResult result) {
+    assert(
+        !_queriedUnmigratedDependencies,
+        'Should only query unmigratedDependencies after all calls to '
+        'prepareInput');
     if (result.unit.featureSet.isEnabled(Feature.non_nullable)) {
       // This library has already been migrated; nothing more to do.
       return;
     }
     ExperimentStatusException.sanityCheck(result);
+    _recordTransitiveImportOptInStatus(result.libraryElement.importedLibraries);
     if (_variables == null) {
       _variables = Variables(_graph, result.typeProvider, _getLineInfo,
           instrumentation: _instrumentation,
@@ -239,6 +269,18 @@
     _graph.update(_postmortemFileWriter);
   }
 
+  /// Records the opt in/out status of all libraries in [libraries], and any
+  /// libraries they transitively import, in [_libraryOptInStatus].
+  void _recordTransitiveImportOptInStatus(Iterable<LibraryElement> libraries) {
+    var librariesToCheck = libraries.toList();
+    while (librariesToCheck.isNotEmpty) {
+      var library = librariesToCheck.removeLast();
+      if (_libraryOptInStatus.containsKey(library.source)) continue;
+      _libraryOptInStatus[library.source] = library.isNonNullableByDefault;
+      librariesToCheck.addAll(library.importedLibraries);
+    }
+  }
+
   static Location _computeLocation(
       LineInfo lineInfo, SourceEdit edit, Source source) {
     final locationInfo = lineInfo.getLocation(edit.offset);
diff --git a/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart b/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart
new file mode 100644
index 0000000..e1b7931
--- /dev/null
+++ b/pkg/nnbd_migration/lib/src/utilities/progress_bar.dart
@@ -0,0 +1,83 @@
+// Copyright (c) 2020, 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.
+
+import 'dart:io' hide File;
+import 'dart:math';
+
+import 'package:cli_util/cli_logging.dart';
+
+/// A facility for drawing a progress bar in the terminal.
+///
+/// The bar is instantiated with the total number of "ticks" to be completed,
+/// and progress is made by calling [tick]. The bar is drawn across one entire
+/// line, like so:
+///
+///     [----------                                                   ]
+///
+/// The hyphens represent completed progress, and the whitespace represents
+/// remaining progress.
+///
+/// If there is no terminal, the progress bar will not be drawn.
+class ProgressBar {
+  /// Whether the progress bar should be drawn.
+  /*late*/ bool _shouldDrawProgress;
+
+  /// The width of the terminal, in terms of characters.
+  /*late*/ int _width;
+
+  final Logger _logger;
+
+  /// The inner width of the terminal, in terms of characters.
+  ///
+  /// This represents the number of characters available for drawing progress.
+  /*late*/ int _innerWidth;
+
+  final int _totalTickCount;
+
+  int _tickCount = 0;
+
+  ProgressBar(this._logger, this._totalTickCount) {
+    if (!stdout.hasTerminal) {
+      _shouldDrawProgress = false;
+    } else {
+      _shouldDrawProgress = true;
+      _width = stdout.terminalColumns;
+      _innerWidth = stdout.terminalColumns - 2;
+      _logger.write('[' + ' ' * _innerWidth + ']');
+    }
+  }
+
+  /// Clear the progress bar from the terminal, allowing other logging to be
+  /// printed.
+  void clear() {
+    if (!_shouldDrawProgress) {
+      return;
+    }
+    _logger.write('\r' + ' ' * _width + '\r');
+  }
+
+  /// Draw the progress bar as complete, and print two newlines.
+  void complete() {
+    if (!_shouldDrawProgress) {
+      return;
+    }
+    _logger.write('\r[' + '-' * _innerWidth + ']\n\n');
+  }
+
+  /// Progress the bar by one tick.
+  void tick() {
+    if (!_shouldDrawProgress) {
+      return;
+    }
+    _tickCount++;
+    var fractionComplete =
+        max(0, _tickCount * _innerWidth ~/ _totalTickCount - 1);
+    var remaining = _innerWidth - fractionComplete - 1;
+    _logger.write('\r[' + // Bring cursor back to the start of the line.
+        '-' * fractionComplete + // Print complete work.
+        AnsiProgress.kAnimationItems[_tickCount % 4] + // Print spinner.
+        ' ' * remaining + // Print remaining work.
+        ']');
+  }
+}
diff --git a/pkg/nnbd_migration/test/api_test.dart b/pkg/nnbd_migration/test/api_test.dart
index bfd7469..1e86235 100644
--- a/pkg/nnbd_migration/test/api_test.dart
+++ b/pkg/nnbd_migration/test/api_test.dart
@@ -69,6 +69,7 @@
         }
       }
     }
+    expect(migration.unmigratedDependencies, isEmpty);
     _betweenStages();
     for (var path in input.keys) {
       if (!(session.getFile(path)).isPart) {
diff --git a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
index 70cf296..6f87f89 100644
--- a/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
+++ b/pkg/nnbd_migration/test/front_end/nnbd_migration_test_base.dart
@@ -18,6 +18,7 @@
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
 import 'analysis_abstract.dart';
+import '../utilities/test_logger.dart';
 
 @reflectiveTest
 class NnbdMigrationTestBase extends AbstractAnalysisTest {
@@ -218,13 +219,15 @@
     }
 
     await _forEachPath(migration.prepareInput);
+    expect(migration.unmigratedDependencies, isEmpty);
     await _forEachPath(migration.processInput);
     await _forEachPath(migration.finalizeInput);
     migration.finish();
     // Build the migration info.
     var info = instrumentationListener.data;
-    var builder = InfoBuilder(
-        resourceProvider, includedRoot, info, listener, migration, nodeMapper);
+    var logger = TestLogger(false);
+    var builder = InfoBuilder(resourceProvider, includedRoot, info, listener,
+        migration, nodeMapper, logger);
     infos = await builder.explainMigration();
   }
 
diff --git a/pkg/nnbd_migration/test/instrumentation_test.dart b/pkg/nnbd_migration/test/instrumentation_test.dart
index d06a022..6e8cd37 100644
--- a/pkg/nnbd_migration/test/instrumentation_test.dart
+++ b/pkg/nnbd_migration/test/instrumentation_test.dart
@@ -151,6 +151,7 @@
     source = result.unit.declaredElement.source;
     findNode = FindNode(content, result.unit);
     migration.prepareInput(result);
+    expect(migration.unmigratedDependencies, isEmpty);
     migration.processInput(result);
     migration.finalizeInput(result);
     migration.finish();
diff --git a/pkg/nnbd_migration/test/migration_cli_test.dart b/pkg/nnbd_migration/test/migration_cli_test.dart
index 72acffb..c581b6b 100644
--- a/pkg/nnbd_migration/test/migration_cli_test.dart
+++ b/pkg/nnbd_migration/test/migration_cli_test.dart
@@ -33,6 +33,8 @@
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
+import 'utilities/test_logger.dart';
+
 main() {
   defineReflectiveSuite(() {
     defineReflectiveTests(_MigrationCliTestPosix);
@@ -65,11 +67,12 @@
       ResourceProvider resourceProvider,
       LineInfo Function(String) getLineInfo,
       Object bindAddress,
+      Logger logger,
       {List<String> included = const <String>[],
       int preferredPort,
       String summaryPath,
       @required String sdkPath})
-      : super(listener, resourceProvider, getLineInfo, bindAddress,
+      : super(listener, resourceProvider, getLineInfo, bindAddress, logger,
             included: included,
             preferredPort: preferredPort,
             summaryPath: summaryPath,
@@ -92,11 +95,10 @@
   _MigrationCli(this._test)
       : super(
             binaryName: 'nnbd_migration',
-            loggerFactory: (isVerbose) => _test.logger = _TestLogger(isVerbose),
+            loggerFactory: (isVerbose) => _test.logger = TestLogger(isVerbose),
             defaultSdkPathOverride:
                 _test.resourceProvider.convertPath(mock_sdk.sdkRoot),
             resourceProvider: _test.resourceProvider,
-            processManager: _test.processManager,
             environmentVariables: _test.environmentVariables);
 
   _MigrationCliRunner decodeCommandLineArgs(ArgResults argResults,
@@ -156,7 +158,7 @@
       @required String sdkPath}) {
     if (cli._test.injectArtificialException) {
       return _ExceptionGeneratingNonNullableFix(
-          listener, resourceProvider, getLineInfo, bindAddress,
+          listener, resourceProvider, getLineInfo, bindAddress, logger,
           included: included,
           preferredPort: preferredPort,
           summaryPath: summaryPath,
@@ -198,16 +200,14 @@
 
   bool Function(String) overrideShouldBeMigrated;
 
-  set logger(_TestLogger logger);
-
-  _MockProcessManager get processManager;
+  set logger(TestLogger logger);
 
   MemoryResourceProvider get resourceProvider;
 }
 
 mixin _MigrationCliTestMethods on _MigrationCliTestBase {
   @override
-  /*late*/ _TestLogger logger;
+  /*late*/ TestLogger logger;
 
   final hasVerboseHelpMessage = contains('for verbose help output');
 
@@ -302,36 +302,6 @@
     }
   }
 
-  void assertPubOutdatedFailure(
-      {int pubOutdatedExitCode = 0,
-      String pubOutdatedStdout = '',
-      String pubOutdatedStderr = ''}) {
-    processManager._mockResult = ProcessResult(123 /* pid */,
-        pubOutdatedExitCode, pubOutdatedStdout, pubOutdatedStderr);
-    logger = _TestLogger(true);
-    var projectContents = simpleProject(sourceText: 'int x;');
-    var projectDir = createProjectDir(projectContents);
-    var success = DependencyChecker(
-            projectDir, resourceProvider.pathContext, logger, processManager)
-        .check();
-    expect(success, isFalse);
-  }
-
-  void assertPubOutdatedSuccess(
-      {int pubOutdatedExitCode = 0,
-      String pubOutdatedStdout = '',
-      String pubOutdatedStderr = ''}) {
-    processManager._mockResult = ProcessResult(123 /* pid */,
-        pubOutdatedExitCode, pubOutdatedStdout, pubOutdatedStderr);
-    logger = _TestLogger(true);
-    var projectContents = simpleProject(sourceText: 'int x;');
-    var projectDir = createProjectDir(projectContents);
-    var success = DependencyChecker(
-            projectDir, resourceProvider.pathContext, logger, processManager)
-        .check();
-    expect(success, isTrue);
-  }
-
   Future<String> assertRunFailure(List<String> args,
       {MigrationCli cli,
       bool withUsage = false,
@@ -404,6 +374,9 @@
         http.post(url, headers: headers, body: body, encoding: encoding));
   }
 
+  String packagePath(String path) =>
+      resourceProvider.convertPath('/.pub-cache/$path');
+
   Future<void> runWithPreviewServer(_MigrationCli cli, List<String> args,
       Future<void> Function(String) callback) async {
     String url;
@@ -572,17 +545,17 @@
     expect(_getHelpText(verbose: true), contains(flagName));
   }
 
-  test_flag_skip_pub_outdated_default() {
-    expect(assertParseArgsSuccess([]).skipPubOutdated, isFalse);
+  test_flag_skip_import_check_default() {
+    expect(assertParseArgsSuccess([]).skipImportCheck, isFalse);
   }
 
-  test_flag_skip_pub_outdated_disable() async {
-    // "--no-skip-pub-outdated" is not an option.
-    await assertParseArgsFailure(['--no-skip-pub-outdated']);
+  test_flag_skip_import_check_disable() async {
+    // "--no-skip-import-check" is not an option.
+    await assertParseArgsFailure(['--no-skip-import_check']);
   }
 
-  test_flag_skip_pub_outdated_enable() {
-    expect(assertParseArgsSuccess(['--skip-pub-outdated']).skipPubOutdated,
+  test_flag_skip_import_check_enable() {
+    expect(assertParseArgsSuccess(['--skip-import-check']).skipImportCheck,
         isTrue);
   }
 
@@ -840,8 +813,12 @@
         .join(projectDir, 'lib', 'analyze_but_do_not_migrate.dart');
     overridePathsToProcess = {testPath, analyzeButDoNotMigratePath};
     overrideShouldBeMigrated = (path) => path == testPath;
-    var cliRunner = _createCli().decodeCommandLineArgs(
-        _parseArgs(['--no-web-preview', '--apply-changes', projectDir]));
+    var cliRunner = _createCli().decodeCommandLineArgs(_parseArgs([
+      '--no-web-preview',
+      '--apply-changes',
+      '--skip-import-check',
+      projectDir
+    ]));
     await cliRunner.run();
     assertNormalExit(cliRunner);
     // Check that a summary was printed
@@ -1309,47 +1286,54 @@
     });
   }
 
-  test_lifecycle_skip_pub_outdated_disable() async {
-    var projectContents = simpleProject(sourceText: '''
+  test_lifecycle_skip_import_check_disable() async {
+    var projectContents = simpleProject(
+        sourceText: '''
+import 'package:foo/foo.dart';
+import 'package:foo/bar.dart';
+
 int f() => null;
-''');
+''',
+        packageConfigText: _getPackageConfigText(
+            migrated: false, packagesMigrated: {'foo': false}));
     var projectDir = createProjectDir(projectContents);
-    processManager._mockResult = ProcessResult(
-        123 /* pid */,
-        0 /* exitCode */,
-        '''
-{ "packages":
-  [
-    { "package": "abc", "current": { "version": "1.0.0", "nullSafety": false } }
-  ]
-}
-''' /* stdout */,
-        '' /* stderr */);
-    var output = await assertRunFailure([projectDir], expectedExitCode: 1);
-    expect(output,
-        contains('Warning: not all current dependencies have migrated'));
+    resourceProvider.newFile(packagePath('foo/lib/foo.dart'), '');
+    resourceProvider.newFile(packagePath('foo/lib/bar.dart'), '');
+    await assertRunFailure([projectDir], expectedExitCode: 1);
+    var output = logger.stdoutBuffer.toString();
+    expect(output, contains('Error: package has unmigrated dependencies'));
+    // Output should contain an indented, sorted list of all unmigrated
+    // dependencies.
+    expect(
+        output, contains('\n  package:foo/bar.dart\n  package:foo/foo.dart'));
+    // Output should mention that the user can rerun with `--skip-import-check`.
+    expect(output, contains('`--${CommandLineOptions.skipImportCheckFlag}`'));
   }
 
-  test_lifecycle_skip_pub_outdated_enable() async {
-    var projectContents = simpleProject(sourceText: '''
+  test_lifecycle_skip_import_check_enable() async {
+    var projectContents = simpleProject(
+        sourceText: '''
+import 'package:foo/foo.dart';
+import 'package:foo/bar.dart';
+
 int f() => null;
-''');
+''',
+        packageConfigText: _getPackageConfigText(
+            migrated: false, packagesMigrated: {'foo': false}));
     var projectDir = createProjectDir(projectContents);
-    processManager._mockResult = ProcessResult(
-        123 /* pid */,
-        0 /* exitCode */,
-        '''
-{ "packages":
-  [
-    { "package": "abc", "current": { "version": "1.0.0", "nullSafety": false } }
-  ]
-}
-''' /* stdout */,
-        '' /* stderr */);
+    resourceProvider.newFile(packagePath('foo/lib/foo.dart'), '');
+    resourceProvider.newFile(packagePath('foo/lib/bar.dart'), '');
     var cli = _createCli();
-    await runWithPreviewServer(cli, ['--skip-pub-outdated', projectDir],
+    await runWithPreviewServer(cli, ['--skip-import-check', projectDir],
         (url) async {
       await assertPreviewServerResponsive(url);
+      var output = logger.stdoutBuffer.toString();
+      expect(output, contains('Warning: package has unmigrated dependencies'));
+      // Output should not mention the particular unmigrated dependencies.
+      expect(output, isNot(contains('package:foo')));
+      // Output should mention that the user can rerun without
+      // `--skip-import-check`.
+      expect(output, contains('`--${CommandLineOptions.skipImportCheckFlag}`'));
     });
   }
 
@@ -1639,134 +1623,6 @@
     }
   }
 
-  test_pub_outdated_has_malformed_json() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '{ "packages": }');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
-  test_pub_outdated_has_no_packages() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '{}');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
-  test_pub_outdated_has_no_pre_null_safety_packages() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "package": "abc",
-      "current": { "version": "1.0.0", "nullSafety": true }
-    },
-    {
-      "package": "def",
-      "current": { "version": "2.0.0", "nullSafety": true }
-    }
-  ]
-}
-''');
-  }
-
-  test_pub_outdated_has_one_pre_null_safety_package() {
-    assertPubOutdatedFailure(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "package": "abc",
-      "current": { "version": "1.0.0", "nullSafety": false }
-    },
-    {
-      "package": "def",
-      "current": { "version": "2.0.0", "nullSafety": true }
-    }
-  ]
-}
-''');
-    var stderrText = logger.stderrBuffer.toString();
-    expect(stderrText, contains('Warning:'));
-    expect(stderrText, contains('abc'));
-    expect(stderrText, contains('1.0.0'));
-  }
-
-  test_pub_outdated_has_package_with_missing_current() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "package": "abc"
-    }
-  ]
-}
-''');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
-  test_pub_outdated_has_package_with_missing_name() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "current": {
-        "version": "1.0.0",
-        "nullSafety": false
-      }
-    }
-  ]
-}
-''');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
-  test_pub_outdated_has_package_with_missing_nullSafety() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "package": "abc",
-      "current": {
-        "version": "1.0.0"
-      }
-    }
-  ]
-}
-''');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
-  test_pub_outdated_has_package_with_missing_version() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "package": "abc",
-      "current": {
-        "nullSafety": false
-      }
-    }
-  ]
-}
-''');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
-  test_pub_outdated_has_package_with_null_current() {
-    assertPubOutdatedSuccess(pubOutdatedStdout: '''
-{
-  "packages": [
-    {
-      "package": "abc",
-      "current": null
-    }
-  ]
-}
-''');
-    expect(logger.stderrBuffer.toString(), isEmpty);
-  }
-
-  test_pub_outdated_has_stderr() {
-    assertPubOutdatedSuccess(pubOutdatedStderr: 'anything');
-    expect(logger.stderrBuffer.toString(), startsWith('Warning:'));
-  }
-
   test_pubspec_does_not_exist() async {
     var projectContents = simpleProject()..remove('pubspec.yaml');
     var projectDir = createProjectDir(projectContents);
@@ -1979,23 +1835,34 @@
     return helpText;
   }
 
+  String _getPackageConfigText(
+      {@required bool migrated,
+      Map<String, bool> packagesMigrated = const {}}) {
+    Object makePackageEntry(String name, bool migrated, {String rootUri}) {
+      rootUri ??=
+          resourceProvider.pathContext.toUri(packagePath(name)).toString();
+      return {
+        'name': name,
+        'rootUri': rootUri,
+        'packageUri': 'lib/',
+        'languageVersion': migrated ? '2.12' : '2.6'
+      };
+    }
+
+    var json = {
+      'configVersion': 2,
+      'packages': [
+        makePackageEntry('test', migrated, rootUri: '../'),
+        for (var entry in packagesMigrated.entries)
+          makePackageEntry(entry.key, entry.value)
+      ]
+    };
+    return JsonEncoder.withIndent('  ').convert(json) + '\n';
+  }
+
   ArgResults _parseArgs(List<String> args) {
     return MigrationCli.createParser().parse(args);
   }
-
-  static String _getPackageConfigText({@required bool migrated}) => '''
-{
-  "configVersion": 2,
-  "packages": [
-    {
-      "name": "test",
-      "rootUri": "../",
-      "packageUri": "lib/",
-      "languageVersion": "${migrated ? '2.12' : '2.6'}"
-    }
-  ]
-}
-''';
 }
 
 @reflectiveTest
@@ -2004,16 +1871,12 @@
   @override
   final resourceProvider;
 
-  @override
-  final processManager;
-
   _MigrationCliTestPosix()
       : resourceProvider = MemoryResourceProvider(
             context: path.style == path.Style.posix
                 ? null
                 : path.Context(
-                    style: path.Style.posix, current: '/working_dir')),
-        processManager = _MockProcessManager();
+                    style: path.Style.posix, current: '/working_dir'));
 }
 
 @reflectiveTest
@@ -2022,79 +1885,10 @@
   @override
   final resourceProvider;
 
-  @override
-  final processManager;
-
   _MigrationCliTestWindows()
       : resourceProvider = MemoryResourceProvider(
             context: path.style == path.Style.windows
                 ? null
                 : path.Context(
-                    style: path.Style.windows, current: 'C:\\working_dir')),
-        processManager = _MockProcessManager();
-}
-
-class _MockProcessManager implements ProcessManager {
-  ProcessResult _mockResult;
-
-  dynamic noSuchMethod(Invocation invocation) {}
-
-  ProcessResult runSync(String executable, List<String> arguments,
-          {String workingDirectory}) =>
-      _mockResult ??
-      ProcessResult(
-        123 /* pid */,
-        0 /* exitCode */,
-        jsonEncode({'packages': []}) /* stdout */,
-        '' /* stderr */,
-      );
-}
-
-/// TODO(paulberry): move into cli_util
-class _TestLogger implements Logger {
-  final stderrBuffer = StringBuffer();
-
-  final stdoutBuffer = StringBuffer();
-
-  final bool isVerbose;
-
-  _TestLogger(this.isVerbose);
-
-  @override
-  Ansi get ansi => Ansi(false);
-
-  @override
-  void flush() {
-    throw UnimplementedError('TODO(paulberry)');
-  }
-
-  @override
-  Progress progress(String message) {
-    return SimpleProgress(this, message);
-  }
-
-  @override
-  void stderr(String message) {
-    stderrBuffer.writeln(message);
-  }
-
-  @override
-  void stdout(String message) {
-    stdoutBuffer.writeln(message);
-  }
-
-  @override
-  void trace(String message) {
-    throw UnimplementedError('TODO(paulberry)');
-  }
-
-  @override
-  void write(String message) {
-    stdoutBuffer.write(message);
-  }
-
-  @override
-  void writeCharCode(int charCode) {
-    stdoutBuffer.writeCharCode(charCode);
-  }
+                    style: path.Style.windows, current: 'C:\\working_dir'));
 }
diff --git a/pkg/nnbd_migration/test/utilities/test_logger.dart b/pkg/nnbd_migration/test/utilities/test_logger.dart
new file mode 100644
index 0000000..5d997c3
--- /dev/null
+++ b/pkg/nnbd_migration/test/utilities/test_logger.dart
@@ -0,0 +1,54 @@
+// Copyright (c) 2020, 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.
+
+import 'package:cli_util/cli_logging.dart';
+
+/// TODO(paulberry): move into cli_util
+class TestLogger implements Logger {
+  final stderrBuffer = StringBuffer();
+
+  final stdoutBuffer = StringBuffer();
+
+  final bool isVerbose;
+
+  TestLogger(this.isVerbose);
+
+  @override
+  Ansi get ansi => Ansi(false);
+
+  @override
+  void flush() {
+    throw UnimplementedError('TODO(paulberry)');
+  }
+
+  @override
+  Progress progress(String message) {
+    return SimpleProgress(this, message);
+  }
+
+  @override
+  void stderr(String message) {
+    stderrBuffer.writeln(message);
+  }
+
+  @override
+  void stdout(String message) {
+    stdoutBuffer.writeln(message);
+  }
+
+  @override
+  void trace(String message) {
+    throw UnimplementedError('TODO(paulberry)');
+  }
+
+  @override
+  void write(String message) {
+    stdoutBuffer.write(message);
+  }
+
+  @override
+  void writeCharCode(int charCode) {
+    stdoutBuffer.writeCharCode(charCode);
+  }
+}
diff --git a/tools/VERSION b/tools/VERSION
index 27b1b1d..47a9824 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 25
+PRERELEASE 26
 PRERELEASE_PATCH 0
\ No newline at end of file