[flutter_tools] Reduce context usage in analyze command and tests (#49589)

diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index 6382cbf..5640105 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -67,7 +67,14 @@
   final bool verboseHelp = help && verbose;
 
   await runner.run(args, <FlutterCommand>[
-    AnalyzeCommand(verboseHelp: verboseHelp),
+    AnalyzeCommand(
+      verboseHelp: verboseHelp,
+      fileSystem: globals.fs,
+      platform: globals.platform,
+      processManager: globals.processManager,
+      logger: globals.logger,
+      terminal: globals.terminal,
+    ),
     AssembleCommand(),
     AttachCommand(verboseHelp: verboseHelp),
     BuildCommand(verboseHelp: verboseHelp),
diff --git a/packages/flutter_tools/lib/src/commands/analyze.dart b/packages/flutter_tools/lib/src/commands/analyze.dart
index 4fb8815..d2690a5 100644
--- a/packages/flutter_tools/lib/src/commands/analyze.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze.dart
@@ -4,14 +4,32 @@
 
 import 'dart:async';
 
+import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
+
 import '../base/file_system.dart';
+import '../base/logger.dart';
+import '../base/terminal.dart';
 import '../globals.dart' as globals;
 import '../runner/flutter_command.dart';
 import 'analyze_continuously.dart';
 import 'analyze_once.dart';
 
 class AnalyzeCommand extends FlutterCommand {
-  AnalyzeCommand({bool verboseHelp = false, this.workingDirectory}) {
+  AnalyzeCommand({
+    bool verboseHelp = false,
+    this.workingDirectory,
+    @required FileSystem fileSystem,
+    @required Platform platform,
+    @required AnsiTerminal terminal,
+    @required Logger logger,
+    @required ProcessManager processManager,
+  }) : _fileSystem = fileSystem,
+       _processManager = processManager,
+       _logger = logger,
+       _terminal = terminal,
+       _platform = platform {
     argParser.addFlag('flutter-repo',
         negatable: false,
         help: 'Include all the examples and tests from the Flutter repository.',
@@ -59,6 +77,12 @@
   /// The working directory for testing analysis using dartanalyzer.
   final Directory workingDirectory;
 
+  final FileSystem _fileSystem;
+  final Logger _logger;
+  final AnsiTerminal _terminal;
+  final ProcessManager _processManager;
+  final Platform _platform;
+
   @override
   String get name => 'analyze';
 
@@ -73,7 +97,7 @@
     }
 
     // Or we're not in a project directory.
-    if (!globals.fs.file('pubspec.yaml').existsSync()) {
+    if (!_fileSystem.file('pubspec.yaml').existsSync()) {
       return false;
     }
 
@@ -87,6 +111,13 @@
         argResults,
         runner.getRepoRoots(),
         runner.getRepoPackages(),
+        fileSystem: _fileSystem,
+        // TODO(jonahwilliams): determine a better way to inject the logger,
+        // since it is constructed on-demand.
+        logger: _logger ?? globals.logger,
+        platform: _platform,
+        processManager: _processManager,
+        terminal: _terminal,
       ).analyze();
     } else {
       await AnalyzeOnce(
@@ -94,6 +125,13 @@
         runner.getRepoRoots(),
         runner.getRepoPackages(),
         workingDirectory: workingDirectory,
+        fileSystem: _fileSystem,
+        // TODO(jonahwilliams): determine a better way to inject the logger,
+        // since it is constructed on-demand.
+        logger: _logger ?? globals.logger,
+        platform: _platform,
+        processManager: _processManager,
+        terminal: _terminal,
       ).analyze();
     }
     return FlutterCommandResult.success();
diff --git a/packages/flutter_tools/lib/src/commands/analyze_base.dart b/packages/flutter_tools/lib/src/commands/analyze_base.dart
index 63fb3dc..35d2def 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_base.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_base.dart
@@ -5,20 +5,47 @@
 import 'dart:async';
 
 import 'package:args/args.dart';
+import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
 import 'package:yaml/yaml.dart' as yaml;
 
 import '../base/common.dart';
 import '../base/file_system.dart';
+import '../base/logger.dart';
+import '../base/terminal.dart';
 import '../base/utils.dart';
 import '../cache.dart';
 import '../globals.dart' as globals;
 
 /// Common behavior for `flutter analyze` and `flutter analyze --watch`
 abstract class AnalyzeBase {
-  AnalyzeBase(this.argResults);
+  AnalyzeBase(this.argResults, {
+    @required this.repoRoots,
+    @required this.repoPackages,
+    @required this.fileSystem,
+    @required this.logger,
+    @required this.platform,
+    @required this.processManager,
+    @required this.terminal,
+  });
 
   /// The parsed argument results for execution.
   final ArgResults argResults;
+  @protected
+  final List<String> repoRoots;
+  @protected
+  final List<Directory> repoPackages;
+  @protected
+  final FileSystem fileSystem;
+  @protected
+  final Logger logger;
+  @protected
+  final ProcessManager processManager;
+  @protected
+  final Platform platform;
+  @protected
+  final AnsiTerminal terminal;
 
   /// Called by [AnalyzeCommand] to start the analysis process.
   Future<void> analyze();
@@ -26,7 +53,7 @@
   void dumpErrors(Iterable<String> errors) {
     if (argResults['write'] != null) {
       try {
-        final RandomAccessFile resultsFile = globals.fs.file(argResults['write']).openSync(mode: FileMode.write);
+        final RandomAccessFile resultsFile = fileSystem.file(argResults['write']).openSync(mode: FileMode.write);
         try {
           resultsFile.lockSync();
           resultsFile.writeStringSync(errors.join('\n'));
@@ -34,7 +61,7 @@
           resultsFile.close();
         }
       } catch (e) {
-        globals.printError('Failed to save output to "${argResults['write']}": $e');
+        logger.printError('Failed to save output to "${argResults['write']}": $e');
       }
     }
   }
@@ -46,30 +73,13 @@
       'issues': errorCount,
       'missingDartDocs': membersMissingDocumentation,
     };
-    globals.fs.file(benchmarkOut).writeAsStringSync(toPrettyJson(data));
-    globals.printStatus('Analysis benchmark written to $benchmarkOut ($data).');
+    fileSystem.file(benchmarkOut).writeAsStringSync(toPrettyJson(data));
+    logger.printStatus('Analysis benchmark written to $benchmarkOut ($data).');
   }
 
   bool get isBenchmarking => argResults['benchmark'] as bool;
 }
 
-/// Return true if [fileList] contains a path that resides inside the Flutter repository.
-/// If [fileList] is empty, then return true if the current directory resides inside the Flutter repository.
-bool inRepo(List<String> fileList) {
-  if (fileList == null || fileList.isEmpty) {
-    fileList = <String>[globals.fs.path.current];
-  }
-  final String root = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot));
-  final String prefix = root + globals.fs.path.separator;
-  for (String file in fileList) {
-    file = globals.fs.path.normalize(globals.fs.path.absolute(file));
-    if (file == root || file.startsWith(prefix)) {
-      return true;
-    }
-  }
-  return false;
-}
-
 class PackageDependency {
   // This is a map from dependency targets (lib directories) to a list
   // of places that ask for that target (.packages or pubspec.yaml files)
diff --git a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
index c6271d5..506b44c 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_continuously.dart
@@ -5,23 +5,38 @@
 import 'dart:async';
 
 import 'package:args/args.dart';
+import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
 
 import '../base/common.dart';
 import '../base/file_system.dart';
 import '../base/io.dart';
 import '../base/logger.dart';
+import '../base/terminal.dart';
 import '../base/utils.dart';
 import '../cache.dart';
 import '../dart/analysis.dart';
 import '../dart/sdk.dart' as sdk;
-import '../globals.dart' as globals;
 import 'analyze_base.dart';
 
 class AnalyzeContinuously extends AnalyzeBase {
-  AnalyzeContinuously(ArgResults argResults, this.repoRoots, this.repoPackages) : super(argResults);
-
-  final List<String> repoRoots;
-  final List<Directory> repoPackages;
+  AnalyzeContinuously(ArgResults argResults, List<String> repoRoots, List<Directory> repoPackages, {
+    @required FileSystem fileSystem,
+    @required Logger logger,
+    @required AnsiTerminal terminal,
+    @required Platform platform,
+    @required ProcessManager processManager,
+  }) : super(
+        argResults,
+        repoPackages: repoPackages,
+        repoRoots: repoRoots,
+        fileSystem: fileSystem,
+        logger: logger,
+        platform: platform,
+        terminal: terminal,
+        processManager: processManager,
+      );
 
   String analysisTarget;
   bool firstAnalysis = true;
@@ -42,18 +57,24 @@
       directories = repoRoots;
       analysisTarget = 'Flutter repository';
 
-      globals.printTrace('Analyzing Flutter repository:');
+      logger.printTrace('Analyzing Flutter repository:');
       for (final String projectPath in repoRoots) {
-        globals.printTrace('  ${globals.fs.path.relative(projectPath)}');
+        logger.printTrace('  ${fileSystem.path.relative(projectPath)}');
       }
     } else {
-      directories = <String>[globals.fs.currentDirectory.path];
-      analysisTarget = globals.fs.currentDirectory.path;
+      directories = <String>[fileSystem.currentDirectory.path];
+      analysisTarget = fileSystem.currentDirectory.path;
     }
 
     final String sdkPath = argResults['dart-sdk'] as String ?? sdk.dartSdkPath;
 
-    final AnalysisServer server = AnalysisServer(sdkPath, directories);
+    final AnalysisServer server = AnalysisServer(sdkPath, directories,
+      fileSystem: fileSystem,
+      logger: logger,
+      platform: platform,
+      processManager: processManager,
+      terminal: terminal,
+    );
     server.onAnalyzing.listen((bool isAnalyzing) => _handleAnalysisStatus(server, isAnalyzing));
     server.onErrors.listen(_handleAnalysisErrors);
 
@@ -66,7 +87,7 @@
     if (exitCode != 0) {
       throwToolExit(message, exitCode: exitCode);
     }
-    globals.printStatus(message);
+    logger.printStatus(message);
 
     if (server.didServerErrorOccur) {
       throwToolExit('Server error(s) occurred.');
@@ -77,9 +98,9 @@
     if (isAnalyzing) {
       analysisStatus?.cancel();
       if (!firstAnalysis) {
-        globals.printStatus('\n');
+        logger.printStatus('\n');
       }
-      analysisStatus = globals.logger.startProgress('Analyzing $analysisTarget...', timeout: timeoutConfiguration.slowOperation);
+      analysisStatus = logger.startProgress('Analyzing $analysisTarget...', timeout: timeoutConfiguration.slowOperation);
       analyzedPaths.clear();
       analysisTimer = Stopwatch()..start();
     } else {
@@ -87,12 +108,12 @@
       analysisStatus = null;
       analysisTimer.stop();
 
-      globals.logger.printStatus(globals.terminal.clearScreen(), newline: false);
+      logger.printStatus(terminal.clearScreen(), newline: false);
 
       // Remove errors for deleted files, sort, and print errors.
       final List<AnalysisError> errors = <AnalysisError>[];
       for (final String path in analysisErrors.keys.toList()) {
-        if (globals.fs.isFileSync(path)) {
+        if (fileSystem.isFileSync(path)) {
           errors.addAll(analysisErrors[path]);
         } else {
           analysisErrors.remove(path);
@@ -113,9 +134,9 @@
       errors.sort();
 
       for (final AnalysisError error in errors) {
-        globals.printStatus(error.toString());
+        logger.printStatus(error.toString());
         if (error.code != null) {
-          globals.printTrace('error code: ${error.code}');
+          logger.printTrace('error code: ${error.code}');
         }
       }
 
@@ -148,9 +169,9 @@
       final String files = '${analyzedPaths.length} ${pluralize('file', analyzedPaths.length)}';
       final String seconds = (analysisTimer.elapsedMilliseconds / 1000.0).toStringAsFixed(2);
       if (undocumentedMembers > 0) {
-        globals.printStatus('$errorsMessage • $dartdocMessage • analyzed $files in $seconds seconds');
+        logger.printStatus('$errorsMessage • $dartdocMessage • analyzed $files in $seconds seconds');
       } else {
-        globals.printStatus('$errorsMessage • analyzed $files in $seconds seconds');
+        logger.printStatus('$errorsMessage • analyzed $files in $seconds seconds');
       }
 
       if (firstAnalysis && isBenchmarking) {
diff --git a/packages/flutter_tools/lib/src/commands/analyze_once.dart b/packages/flutter_tools/lib/src/commands/analyze_once.dart
index db2b724..a186edf 100644
--- a/packages/flutter_tools/lib/src/commands/analyze_once.dart
+++ b/packages/flutter_tools/lib/src/commands/analyze_once.dart
@@ -5,15 +5,18 @@
 import 'dart:async';
 
 import 'package:args/args.dart';
+import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
 
 import '../base/common.dart';
 import '../base/file_system.dart';
 import '../base/logger.dart';
+import '../base/terminal.dart';
 import '../base/utils.dart';
 import '../cache.dart';
 import '../dart/analysis.dart';
 import '../dart/sdk.dart' as sdk;
-import '../globals.dart' as globals;
 import 'analyze.dart';
 import 'analyze_base.dart';
 
@@ -21,13 +24,24 @@
 class AnalyzeOnce extends AnalyzeBase {
   AnalyzeOnce(
     ArgResults argResults,
-    this.repoRoots,
-    this.repoPackages, {
+    List<String> repoRoots,
+    List<Directory> repoPackages, {
+    @required FileSystem fileSystem,
+    @required Logger logger,
+    @required Platform platform,
+    @required ProcessManager processManager,
+    @required AnsiTerminal terminal,
     this.workingDirectory,
-  }) : super(argResults);
-
-  final List<String> repoRoots;
-  final List<Directory> repoPackages;
+  }) : super(
+        argResults,
+        repoRoots: repoRoots,
+        repoPackages: repoPackages,
+        fileSystem: fileSystem,
+        logger: logger,
+        platform: platform,
+        processManager: processManager,
+        terminal: terminal,
+      );
 
   /// The working directory for testing analysis using dartanalyzer.
   final Directory workingDirectory;
@@ -35,14 +49,14 @@
   @override
   Future<void> analyze() async {
     final String currentDirectory =
-        (workingDirectory ?? globals.fs.currentDirectory).path;
+        (workingDirectory ?? fileSystem.currentDirectory).path;
 
     // find directories from argResults.rest
     final Set<String> directories = Set<String>.from(argResults.rest
-        .map<String>((String path) => globals.fs.path.canonicalize(path)));
+        .map<String>((String path) => fileSystem.path.canonicalize(path)));
     if (directories.isNotEmpty) {
       for (final String directory in directories) {
-        final FileSystemEntityType type = globals.fs.typeSync(directory);
+        final FileSystemEntityType type = fileSystem.typeSync(directory);
 
         if (type == FileSystemEntityType.notFound) {
           throwToolExit("'$directory' does not exist");
@@ -79,6 +93,11 @@
     final AnalysisServer server = AnalysisServer(
       sdkPath,
       directories.toList(),
+      fileSystem: fileSystem,
+      platform: platform,
+      logger: logger,
+      processManager: processManager,
+      terminal: terminal,
     );
 
     StreamSubscription<bool> subscription;
@@ -108,9 +127,9 @@
     final Stopwatch timer = Stopwatch()..start();
     final String message = directories.length > 1
         ? '${directories.length} ${directories.length == 1 ? 'directory' : 'directories'}'
-        : globals.fs.path.basename(directories.first);
+        : fileSystem.path.basename(directories.first);
     final Status progress = argResults['preamble'] as bool
-        ? globals.logger.startProgress('Analyzing $message...', timeout: timeoutConfiguration.slowOperation)
+        ? logger.startProgress('Analyzing $message...', timeout: timeoutConfiguration.slowOperation)
         : null;
 
     await analysisCompleter.future;
@@ -135,11 +154,11 @@
 
     // report errors
     if (errors.isNotEmpty && (argResults['preamble'] as bool)) {
-      globals.printStatus('');
+      logger.printStatus('');
     }
     errors.sort();
     for (final AnalysisError error in errors) {
-      globals.printStatus(error.toString(), hangingIndent: 7);
+      logger.printStatus(error.toString(), hangingIndent: 7);
     }
 
     final String seconds = (timer.elapsedMilliseconds / 1000.0).toStringAsFixed(1);
@@ -154,7 +173,7 @@
     // We consider any level of error to be an error exit (we don't report different levels).
     if (errors.isNotEmpty) {
       final int errorCount = errors.length;
-      globals.printStatus('');
+      logger.printStatus('');
       if (undocumentedMembers > 0) {
         throwToolExit('$errorCount ${pluralize('issue', errorCount)} found. (ran in ${seconds}s; $dartdocMessage)');
       } else {
@@ -168,9 +187,9 @@
 
     if (argResults['congratulate'] as bool) {
       if (undocumentedMembers > 0) {
-        globals.printStatus('No issues found! (ran in ${seconds}s; $dartdocMessage)');
+        logger.printStatus('No issues found! (ran in ${seconds}s; $dartdocMessage)');
       } else {
-        globals.printStatus('No issues found! (ran in ${seconds}s)');
+        logger.printStatus('No issues found! (ran in ${seconds}s)');
       }
     }
   }
diff --git a/packages/flutter_tools/lib/src/dart/analysis.dart b/packages/flutter_tools/lib/src/dart/analysis.dart
index 85b77fe..8714c50 100644
--- a/packages/flutter_tools/lib/src/dart/analysis.dart
+++ b/packages/flutter_tools/lib/src/dart/analysis.dart
@@ -5,18 +5,39 @@
 import 'dart:async';
 import 'dart:math' as math;
 
+import 'package:meta/meta.dart';
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
+
 import '../base/common.dart';
+import '../base/file_system.dart';
 import '../base/io.dart';
+import '../base/logger.dart';
 import '../base/terminal.dart';
 import '../base/utils.dart';
 import '../convert.dart';
-import '../globals.dart' as globals;
 
+/// An interface to the Dart analysis server.
 class AnalysisServer {
-  AnalysisServer(this.sdkPath, this.directories);
+  AnalysisServer(this.sdkPath, this.directories, {
+    @required FileSystem fileSystem,
+    @required ProcessManager processManager,
+    @required Logger logger,
+    @required Platform platform,
+    @required AnsiTerminal terminal,
+  }) : _fileSystem = fileSystem,
+       _processManager = processManager,
+       _logger = logger,
+       _platform = platform,
+       _terminal = terminal;
 
   final String sdkPath;
   final List<String> directories;
+  final FileSystem _fileSystem;
+  final ProcessManager _processManager;
+  final Logger _logger;
+  final Platform _platform;
+  final AnsiTerminal _terminal;
 
   Process _process;
   final StreamController<bool> _analyzingController =
@@ -29,9 +50,9 @@
 
   Future<void> start() async {
     final String snapshot =
-        globals.fs.path.join(sdkPath, 'bin/snapshots/analysis_server.dart.snapshot');
+        _fileSystem.path.join(sdkPath, 'bin/snapshots/analysis_server.dart.snapshot');
     final List<String> command = <String>[
-      globals.fs.path.join(sdkPath, 'bin', 'dart'),
+      _fileSystem.path.join(sdkPath, 'bin', 'dart'),
       snapshot,
       '--disable-server-feature-completion',
       '--disable-server-feature-search',
@@ -39,14 +60,14 @@
       sdkPath,
     ];
 
-    globals.printTrace('dart ${command.skip(1).join(' ')}');
-    _process = await globals.processManager.start(command);
+    _logger.printTrace('dart ${command.skip(1).join(' ')}');
+    _process = await _processManager.start(command);
     // This callback hookup can't throw.
     unawaited(_process.exitCode.whenComplete(() => _process = null));
 
     final Stream<String> errorStream =
         _process.stderr.transform<String>(utf8.decoder).transform<String>(const LineSplitter());
-    errorStream.listen(globals.printError);
+    errorStream.listen(_logger.printError);
 
     final Stream<String> inStream =
         _process.stdout.transform<String>(utf8.decoder).transform<String>(const LineSplitter());
@@ -73,11 +94,11 @@
       'params': params,
     });
     _process.stdin.writeln(message);
-    globals.printTrace('==> $message');
+    _logger.printTrace('==> $message');
   }
 
   void _handleServerResponse(String line) {
-    globals.printTrace('<== $line');
+    _logger.printTrace('<== $line');
 
     final dynamic response = json.decode(line);
 
@@ -98,10 +119,10 @@
       } else if (response['error'] != null) {
         // Fields are 'code', 'message', and 'stackTrace'.
         final Map<String, dynamic> error = castStringKeyedMap(response['error']);
-        globals.printError(
+        _logger.printError(
             'Error response from the server: ${error['code']} ${error['message']}');
         if (error['stackTrace'] != null) {
-          globals.printError(error['stackTrace'] as String);
+          _logger.printError(error['stackTrace'] as String);
         }
       }
     }
@@ -117,9 +138,9 @@
 
   void _handleServerError(Map<String, dynamic> error) {
     // Fields are 'isFatal', 'message', and 'stackTrace'.
-    globals.printError('Error from the analysis server: ${error['message']}');
+    _logger.printError('Error from the analysis server: ${error['message']}');
     if (error['stackTrace'] != null) {
-      globals.printError(error['stackTrace'] as String);
+      _logger.printError(error['stackTrace'] as String);
     }
     _didServerErrorOccur = true;
   }
@@ -130,7 +151,13 @@
     final List<dynamic> errorsList = issueInfo['errors'] as List<dynamic>;
     final List<AnalysisError> errors = errorsList
         .map<Map<String, dynamic>>(castStringKeyedMap)
-        .map<AnalysisError>((Map<String, dynamic> json) => AnalysisError(json))
+        .map<AnalysisError>((Map<String, dynamic> json) {
+          return AnalysisError(json,
+            fileSystem: _fileSystem,
+            platform: _platform,
+            terminal: _terminal,
+          );
+        })
         .toList();
     if (!_errorsController.isClosed) {
       _errorsController.add(FileAnalysisErrors(file, errors));
@@ -152,7 +179,17 @@
 }
 
 class AnalysisError implements Comparable<AnalysisError> {
-  AnalysisError(this.json);
+  AnalysisError(this.json, {
+    @required Platform platform,
+    @required AnsiTerminal terminal,
+    @required FileSystem fileSystem,
+  }) : _platform = platform,
+       _terminal = terminal,
+       _fileSystem = fileSystem;
+
+  final Platform _platform;
+  final AnsiTerminal _terminal;
+  final FileSystem _fileSystem;
 
   static final Map<String, _AnalysisSeverity> _severityMap = <String, _AnalysisSeverity>{
     'INFO': _AnalysisSeverity.info,
@@ -160,7 +197,7 @@
     'ERROR': _AnalysisSeverity.error,
   };
 
-  static final String _separator = globals.platform.isWindows ? '-' : '•';
+  String  get _separator => _platform.isWindows ? '-' : '•';
 
   // "severity":"INFO","type":"TODO","location":{
   //   "file":"/Users/.../lib/test.dart","offset":362,"length":72,"startLine":15,"startColumn":4
@@ -171,9 +208,9 @@
   String get colorSeverity {
     switch(_severityLevel) {
       case _AnalysisSeverity.error:
-        return globals.terminal.color(severity, TerminalColor.red);
+        return _terminal.color(severity, TerminalColor.red);
       case _AnalysisSeverity.warning:
-        return globals.terminal.color(severity, TerminalColor.yellow);
+        return _terminal.color(severity, TerminalColor.yellow);
       case _AnalysisSeverity.info:
       case _AnalysisSeverity.none:
         return severity;
@@ -224,7 +261,7 @@
     final String padding = ' ' * math.max(0, 7 - severity.length);
     return '$padding${colorSeverity.toLowerCase()} $_separator '
         '$messageSentenceFragment $_separator '
-        '${globals.fs.path.relative(file)}:$startLine:$startColumn $_separator '
+        '${_fileSystem.path.relative(file)}:$startLine:$startColumn $_separator '
         '$code';
   }
 
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
index 1d23c04..d8ee436 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart
@@ -5,12 +5,15 @@
 import 'dart:async';
 
 import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/base/os.dart';
+import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/dart/analysis.dart';
 import 'package:flutter_tools/src/dart/pub.dart';
 import 'package:flutter_tools/src/dart/sdk.dart';
 import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
-import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:platform/platform.dart';
+import 'package:process/process.dart';
 
 import '../../src/common.dart';
 import '../../src/context.dart';
@@ -18,10 +21,21 @@
 void main() {
   AnalysisServer server;
   Directory tempDir;
+  FileSystem fileSystem;
+  Platform platform;
+  ProcessManager processManager;
+  AnsiTerminal terminal;
+  Logger logger;
 
   setUp(() {
+    platform = const LocalPlatform();
+    fileSystem = const LocalFileSystem();
+    platform = const LocalPlatform();
+    processManager = const LocalProcessManager();
+    terminal = AnsiTerminal(platform: platform, stdio: Stdio());
+    logger = BufferLogger(outputPreferences: OutputPreferences.test(), terminal: terminal);
     FlutterCommandRunner.initFlutterRoot();
-    tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_analysis_test.');
+    tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analysis_test.');
   });
 
   tearDown(() {
@@ -29,13 +43,36 @@
     return server?.dispose();
   });
 
+
+  void _createSampleProject(Directory directory, { bool brokenCode = false }) {
+    final File pubspecFile = fileSystem.file(fileSystem.path.join(directory.path, 'pubspec.yaml'));
+    pubspecFile.writeAsStringSync('''
+  name: foo_project
+  ''');
+
+    final File dartFile = fileSystem.file(fileSystem.path.join(directory.path, 'lib', 'main.dart'));
+    dartFile.parent.createSync();
+    dartFile.writeAsStringSync('''
+  void main() {
+    print('hello world');
+    ${brokenCode ? 'prints("hello world");' : ''}
+  }
+  ''');
+  }
+
   group('analyze --watch', () {
     testUsingContext('AnalysisServer success', () async {
       _createSampleProject(tempDir);
 
-      await pub.get(context: PubContext.flutterTests, directory: tempDir.path);
+      await const Pub().get(context: PubContext.flutterTests, directory: tempDir.path);
 
-      server = AnalysisServer(dartSdkPath, <String>[tempDir.path]);
+      server = AnalysisServer(dartSdkPath, <String>[tempDir.path],
+        fileSystem: fileSystem,
+        platform: platform,
+        processManager: processManager,
+        logger: logger,
+        terminal: terminal,
+      );
 
       int errorCount = 0;
       final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
@@ -45,18 +82,21 @@
       await onDone;
 
       expect(errorCount, 0);
-    }, overrides: <Type, Generator>{
-      OperatingSystemUtils: () => globals.os,
-      Pub: () => const Pub(),
     });
   });
 
   testUsingContext('AnalysisServer errors', () async {
     _createSampleProject(tempDir, brokenCode: true);
 
-    await pub.get(context: PubContext.flutterTests, directory: tempDir.path);
+    await const Pub().get(context: PubContext.flutterTests, directory: tempDir.path);
 
-    server = AnalysisServer(dartSdkPath, <String>[tempDir.path]);
+    server = AnalysisServer(dartSdkPath, <String>[tempDir.path],
+      fileSystem: fileSystem,
+      platform: platform,
+      processManager: processManager,
+      logger: logger,
+      terminal: terminal,
+    );
 
     int errorCount = 0;
     final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
@@ -68,15 +108,18 @@
     await onDone;
 
     expect(errorCount, greaterThan(0));
-  }, overrides: <Type, Generator>{
-    OperatingSystemUtils: () => globals.os,
-    Pub: () => const Pub(),
   });
 
   testUsingContext('Returns no errors when source is error-free', () async {
     const String contents = "StringBuffer bar = StringBuffer('baz');";
     tempDir.childFile('main.dart').writeAsStringSync(contents);
-    server = AnalysisServer(dartSdkPath, <String>[tempDir.path]);
+    server = AnalysisServer(dartSdkPath, <String>[tempDir.path],
+      fileSystem: fileSystem,
+      platform: platform,
+      processManager: processManager,
+      logger: logger,
+      terminal: terminal,
+    );
 
     int errorCount = 0;
     final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
@@ -86,24 +129,5 @@
     await server.start();
     await onDone;
     expect(errorCount, 0);
-  }, overrides: <Type, Generator>{
-    OperatingSystemUtils: () => globals.os,
-    Pub: () => const Pub(),
   });
 }
-
-void _createSampleProject(Directory directory, { bool brokenCode = false }) {
-  final File pubspecFile = globals.fs.file(globals.fs.path.join(directory.path, 'pubspec.yaml'));
-  pubspecFile.writeAsStringSync('''
-name: foo_project
-''');
-
-  final File dartFile = globals.fs.file(globals.fs.path.join(directory.path, 'lib', 'main.dart'));
-  dartFile.parent.createSync();
-  dartFile.writeAsStringSync('''
-void main() {
-  print('hello world');
-  ${brokenCode ? 'prints("hello world");' : ''}
-}
-''');
-}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart
index a2c11be..3ab094a 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_test.dart
@@ -5,7 +5,6 @@
 import 'package:file/file.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/cache.dart';
-import 'package:flutter_tools/src/commands/analyze_base.dart';
 import 'package:flutter_tools/src/globals.dart' as globals;
 
 import '../../src/common.dart';
@@ -13,6 +12,23 @@
 
 const String _kFlutterRoot = '/data/flutter';
 
+/// Return true if [fileList] contains a path that resides inside the Flutter repository.
+/// If [fileList] is empty, then return true if the current directory resides inside the Flutter repository.
+bool inRepo(List<String> fileList) {
+  if (fileList == null || fileList.isEmpty) {
+    fileList = <String>[globals.fs.path.current];
+  }
+  final String root = globals.fs.path.normalize(globals.fs.path.absolute(Cache.flutterRoot));
+  final String prefix = root + globals.fs.path.separator;
+  for (String file in fileList) {
+    file = globals.fs.path.normalize(globals.fs.path.absolute(file));
+    if (file == root || file.startsWith(prefix)) {
+      return true;
+    }
+  }
+  return false;
+}
+
 void main() {
   FileSystem fs;
   Directory tempDir;
diff --git a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart
index bdf7884..3b30266 100644
--- a/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart
+++ b/packages/flutter_tools/test/commands.shard/permeable/analyze_once_test.dart
@@ -8,250 +8,326 @@
 
 import 'package:flutter_tools/src/base/common.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
+import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/base/logger.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
 import 'package:flutter_tools/src/cache.dart';
 import 'package:flutter_tools/src/commands/analyze.dart';
 import 'package:flutter_tools/src/runner/flutter_command.dart';
 import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
-import 'package:flutter_tools/src/globals.dart' as globals;
+import 'package:process/process.dart';
 
 import '../../src/common.dart';
 import '../../src/context.dart';
 
-final Generator _kNoColorTerminalPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
-final Map<Type, Generator> noColorTerminalOverride = <Type, Generator>{
-  Platform: _kNoColorTerminalPlatform,
-};
+final Platform _kNoColorTerminalPlatform = FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false;
 
 void main() {
-  final String analyzerSeparator = globals.platform.isWindows ? '-' : '•';
+  String analyzerSeparator;
+  FileSystem fileSystem;
+  Platform platform;
+  BufferLogger logger;
+  AnsiTerminal terminal;
+  ProcessManager processManager;
+  Directory tempDir;
+  String projectPath;
+  File libMain;
 
-  group('analyze once', () {
-    setUpAll(() {
-      Cache.disableLocking();
-      Cache.flutterRoot = FlutterCommandRunner.defaultFlutterRoot;
-    });
-
-    void _createDotPackages(String projectPath) {
-      final StringBuffer flutterRootUri = StringBuffer('file://');
-      final String canonicalizedFlutterRootPath = globals.fs.path.canonicalize(Cache.flutterRoot);
-      if (globals.platform.isWindows) {
-        flutterRootUri
-            ..write('/')
-            ..write(canonicalizedFlutterRootPath.replaceAll(r'\', '/'));
-      } else {
-        flutterRootUri.write(canonicalizedFlutterRootPath);
+  Future<void> runCommand({
+    FlutterCommand command,
+    List<String> arguments,
+    List<String> statusTextContains,
+    List<String> errorTextContains,
+    bool toolExit = false,
+    String exitMessageContains,
+  }) async {
+    try {
+      arguments.insert(0, '--flutter-root=${Cache.flutterRoot}');
+      await createTestCommandRunner(command).run(arguments);
+      expect(toolExit, isFalse, reason: 'Expected ToolExit exception');
+    } on ToolExit catch (e) {
+      if (!toolExit) {
+        testLogger.clear();
+        rethrow;
       }
-      final String dotPackagesSrc = '''
-# Generated
+      if (exitMessageContains != null) {
+        expect(e.message, contains(exitMessageContains));
+      }
+    }
+    assertContains(logger.statusText, statusTextContains);
+    assertContains(logger.errorText, errorTextContains);
+
+    logger.clear();
+  }
+
+  void _createDotPackages(String projectPath) {
+    final StringBuffer flutterRootUri = StringBuffer('file://');
+    final String canonicalizedFlutterRootPath = fileSystem.path.canonicalize(Cache.flutterRoot);
+    if (platform.isWindows) {
+      flutterRootUri
+          ..write('/')
+          ..write(canonicalizedFlutterRootPath.replaceAll('\\', '/'));
+    } else {
+      flutterRootUri.write(canonicalizedFlutterRootPath);
+    }
+    final String dotPackagesSrc = '''# Generated
 flutter:$flutterRootUri/packages/flutter/lib/
 sky_engine:$flutterRootUri/bin/cache/pkg/sky_engine/lib/
 flutter_project:lib/
 ''';
-      globals.fs.file(globals.fs.path.join(projectPath, '.packages'))
-          ..createSync(recursive: true)
-          ..writeAsStringSync(dotPackagesSrc);
+    fileSystem.file(fileSystem.path.join(projectPath, '.packages'))
+        ..createSync(recursive: true)
+        ..writeAsStringSync(dotPackagesSrc);
+  }
+
+  setUpAll(() {
+    Cache.disableLocking();
+    Cache.flutterRoot = FlutterCommandRunner.defaultFlutterRoot;
+    processManager = const LocalProcessManager();
+    terminal = AnsiTerminal(platform: platform, stdio: Stdio());
+    fileSystem = const LocalFileSystem();
+    platform = const LocalPlatform();
+    logger = BufferLogger(
+      outputPreferences: OutputPreferences.test(),
+      terminal: terminal,
+    );
+    analyzerSeparator = platform.isWindows ? '-' : '•';
+    tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_1.').absolute;
+    projectPath = fileSystem.path.join(tempDir.path, 'flutter_project');
+    fileSystem.file(fileSystem.path.join(projectPath, 'pubspec.yaml'))
+        ..createSync(recursive: true)
+        ..writeAsStringSync(pubspecYamlSrc);
+    _createDotPackages(projectPath);
+  });
+
+  setUp(() {
+    libMain = fileSystem.file(fileSystem.path.join(projectPath, 'lib', 'main.dart'))
+        ..createSync(recursive: true)
+        ..writeAsStringSync(mainDartSrc);
+  });
+
+  tearDownAll(() {
+    tryToDelete(tempDir);
+  });
+
+  // Analyze in the current directory - no arguments
+  testUsingContext('working directory', () async {
+    await runCommand(
+      command: AnalyzeCommand(
+        workingDirectory: fileSystem.directory(projectPath),
+        fileSystem: fileSystem,
+        logger: logger,
+        platform: platform,
+        processManager: processManager,
+        terminal: terminal,
+      ),
+      arguments: <String>['analyze', '--no-pub'],
+      statusTextContains: <String>['No issues found!'],
+    );
+  });
+
+  // Analyze a specific file outside the current directory
+  testUsingContext('passing one file throws', () async {
+    await runCommand(
+      command: AnalyzeCommand(
+        platform: platform,
+        fileSystem: fileSystem,
+        logger: logger,
+        processManager: processManager,
+        terminal: terminal,
+      ),
+      arguments: <String>['analyze', '--no-pub', libMain.path],
+      toolExit: true,
+      exitMessageContains: 'is not a directory',
+    );
+  });
+
+  // Analyze in the current directory - no arguments
+  testUsingContext('working directory with errors', () async {
+    // Break the code to produce the "Avoid empty else" hint
+    // that is upgraded to a warning in package:flutter/analysis_options_user.yaml
+    // to assert that we are using the default Flutter analysis options.
+    // Also insert a statement that should not trigger a lint here
+    // but will trigger a lint later on when an analysis_options.yaml is added.
+    String source = await libMain.readAsString();
+    source = source.replaceFirst(
+      'return MaterialApp(',
+      'if (debugPrintRebuildDirtyWidgets) {} else ; return MaterialApp(',
+    );
+    source = source.replaceFirst(
+      'onPressed: _incrementCounter,',
+      '// onPressed: _incrementCounter,',
+    );
+    source = source.replaceFirst(
+        '_counter++;',
+        '_counter++; throw "an error message";',
+      );
+    libMain.writeAsStringSync(source);
+
+    // Analyze in the current directory - no arguments
+    await runCommand(
+      command: AnalyzeCommand(
+        workingDirectory: fileSystem.directory(projectPath),
+        platform: platform,
+        fileSystem: fileSystem,
+        logger: logger,
+        processManager: processManager,
+        terminal: terminal,
+      ),
+      arguments: <String>['analyze', '--no-pub'],
+      statusTextContains: <String>[
+        'Analyzing',
+        'info $analyzerSeparator Avoid empty else statements',
+        'info $analyzerSeparator Avoid empty statements',
+        'info $analyzerSeparator The declaration \'_incrementCounter\' isn\'t',
+      ],
+      exitMessageContains: '3 issues found.',
+      toolExit: true,
+    );
+  });
+
+  // Analyze in the current directory - no arguments
+  testUsingContext('working directory with local options', () async {
+    // Insert an analysis_options.yaml file in the project
+    // which will trigger a lint for broken code that was inserted earlier
+    final File optionsFile = fileSystem.file(fileSystem.path.join(projectPath, 'analysis_options.yaml'));
+    try {
+      optionsFile.writeAsStringSync('''
+  include: package:flutter/analysis_options_user.yaml
+  linter:
+    rules:
+      - only_throw_errors
+  ''');
+      String source = libMain.readAsStringSync();
+      source = source.replaceFirst(
+        'onPressed: _incrementCounter,',
+        '// onPressed: _incrementCounter,',
+      );
+      source = source.replaceFirst(
+        '_counter++;',
+        '_counter++; throw "an error message";',
+      );
+      libMain.writeAsStringSync(source);
+
+      // Analyze in the current directory - no arguments
+      await runCommand(
+        command: AnalyzeCommand(
+          workingDirectory: fileSystem.directory(projectPath),
+          platform: platform,
+          fileSystem: fileSystem,
+          logger: logger,
+          processManager: processManager,
+          terminal: terminal,
+        ),
+        arguments: <String>['analyze', '--no-pub'],
+        statusTextContains: <String>[
+          'Analyzing',
+          'info $analyzerSeparator The declaration \'_incrementCounter\' isn\'t',
+          'info $analyzerSeparator Only throw instances of classes extending either Exception or Error',
+        ],
+        exitMessageContains: '2 issues found.',
+        toolExit: true,
+      );
+    } finally {
+      if (optionsFile.existsSync()) {
+        optionsFile.deleteSync();
+      }
     }
+  });
 
-    group('default libMain', () {
-      Directory tempDir;
-      String projectPath;
-      File libMain;
+  testUsingContext('analyze once no duplicate issues', () async {
+    final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_2.').absolute;
+    _createDotPackages(tempDir.path);
 
-      setUpAll(() async {
-        tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_analyze_once_test_1.').absolute;
-        projectPath = globals.fs.path.join(tempDir.path, 'flutter_project');
-        globals.fs.file(globals.fs.path.join(projectPath, 'pubspec.yaml'))
-            ..createSync(recursive: true)
-            ..writeAsStringSync(pubspecYamlSrc);
-        _createDotPackages(projectPath);
-      });
-
-      setUp(() {
-        libMain = globals.fs.file(globals.fs.path.join(projectPath, 'lib', 'main.dart'))
-            ..createSync(recursive: true)
-            ..writeAsStringSync(mainDartSrc);
-      });
-
-      tearDownAll(() {
-        tryToDelete(tempDir);
-      });
-
-      // Analyze in the current directory - no arguments
-      testUsingContext('working directory', () async {
-        await runCommand(
-          command: AnalyzeCommand(workingDirectory: globals.fs.directory(projectPath)),
-          arguments: <String>['analyze', '--no-pub'],
-          statusTextContains: <String>['No issues found!'],
-        );
-      });
-
-      // Analyze a specific file outside the current directory
-      testUsingContext('passing one file throws', () async {
-        await runCommand(
-          command: AnalyzeCommand(),
-          arguments: <String>['analyze', '--no-pub', libMain.path],
-          toolExit: true,
-          exitMessageContains: 'is not a directory',
-        );
-      });
-
-      // Analyze in the current directory - no arguments
-      testUsingContext('working directory with errors', () async {
-        // Break the code to produce the "Avoid empty else" hint
-        // that is upgraded to a warning in package:flutter/analysis_options_user.yaml
-        // to assert that we are using the default Flutter analysis options.
-        // Also insert a statement that should not trigger a lint here
-        // but will trigger a lint later on when an analysis_options.yaml is added.
-        String source = await libMain.readAsString();
-        source = source.replaceFirst(
-          'return MaterialApp(',
-          'if (debugPrintRebuildDirtyWidgets) {} else ; return MaterialApp(',
-        );
-        source = source.replaceFirst(
-          'onPressed: _incrementCounter,',
-          '// onPressed: _incrementCounter,',
-        );
-        source = source.replaceFirst(
-            '_counter++;',
-            '_counter++; throw "an error message";',
-          );
-        libMain.writeAsStringSync(source);
-
-        // Analyze in the current directory - no arguments
-        await runCommand(
-          command: AnalyzeCommand(workingDirectory: globals.fs.directory(projectPath)),
-          arguments: <String>['analyze', '--no-pub'],
-          statusTextContains: <String>[
-            'Analyzing',
-            'info $analyzerSeparator Avoid empty else statements',
-            'info $analyzerSeparator Avoid empty statements',
-            "info $analyzerSeparator The declaration '_incrementCounter' isn't",
-          ],
-          exitMessageContains: '3 issues found.',
-          toolExit: true,
-        );
-      });
-
-      // Analyze in the current directory - no arguments
-      testUsingContext('working directory with local options', () async {
-        // Insert an analysis_options.yaml file in the project
-        // which will trigger a lint for broken code that was inserted earlier
-        final File optionsFile = globals.fs.file(globals.fs.path.join(projectPath, 'analysis_options.yaml'));
-        try {
-          optionsFile.writeAsStringSync('''
-      include: package:flutter/analysis_options_user.yaml
-      linter:
-        rules:
-          - only_throw_errors
-      ''');
-          String source = libMain.readAsStringSync();
-          source = source.replaceFirst(
-            'onPressed: _incrementCounter,',
-            '// onPressed: _incrementCounter,',
-          );
-          source = source.replaceFirst(
-            '_counter++;',
-            '_counter++; throw "an error message";',
-          );
-          libMain.writeAsStringSync(source);
-
-          // Analyze in the current directory - no arguments
-          await runCommand(
-            command: AnalyzeCommand(workingDirectory: globals.fs.directory(projectPath)),
-            arguments: <String>['analyze', '--no-pub'],
-            statusTextContains: <String>[
-              'Analyzing',
-              "info $analyzerSeparator The declaration '_incrementCounter' isn't",
-              'info $analyzerSeparator Only throw instances of classes extending either Exception or Error',
-            ],
-            exitMessageContains: '2 issues found.',
-            toolExit: true,
-          );
-        } finally {
-          if (optionsFile.existsSync()) {
-            optionsFile.deleteSync();
-          }
-        }
-      });
-    });
-
-    testUsingContext('no duplicate issues', () async {
-      final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_analyze_once_test_2.').absolute;
-      _createDotPackages(tempDir.path);
-
-      try {
-        final File foo = globals.fs.file(globals.fs.path.join(tempDir.path, 'foo.dart'));
-        foo.writeAsStringSync('''
+    try {
+      final File foo = fileSystem.file(fileSystem.path.join(tempDir.path, 'foo.dart'));
+      foo.writeAsStringSync('''
 import 'bar.dart';
 
 void foo() => bar();
 ''');
 
-        final File bar = globals.fs.file(globals.fs.path.join(tempDir.path, 'bar.dart'));
-        bar.writeAsStringSync('''
+      final File bar = fileSystem.file(fileSystem.path.join(tempDir.path, 'bar.dart'));
+      bar.writeAsStringSync('''
 import 'dart:async'; // unused
 
 void bar() {
 }
 ''');
 
-        // Analyze in the current directory - no arguments
-        await runCommand(
-          command: AnalyzeCommand(workingDirectory: tempDir),
-          arguments: <String>['analyze', '--no-pub'],
-          statusTextContains: <String>[
-            'Analyzing',
-          ],
-          exitMessageContains: '1 issue found.',
-          toolExit: true,
-        );
-      } finally {
-        tryToDelete(tempDir);
-      }
-    });
+      // Analyze in the current directory - no arguments
+      await runCommand(
+        command: AnalyzeCommand(
+          workingDirectory: tempDir,
+          platform: platform,
+          fileSystem: fileSystem,
+          logger: logger,
+          processManager: processManager,
+          terminal: terminal,
+        ),
+        arguments: <String>['analyze', '--no-pub'],
+        statusTextContains: <String>[
+          'Analyzing',
+        ],
+        exitMessageContains: '1 issue found.',
+        toolExit: true,
+      );
+    } finally {
+      tryToDelete(tempDir);
+    }
+  });
 
-    testUsingContext('returns no issues when source is error-free', () async {
-      const String contents = '''
+  testUsingContext('analyze once returns no issues when source is error-free', () async {
+    const String contents = '''
 StringBuffer bar = StringBuffer('baz');
 ''';
-      final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_analyze_once_test_3.');
-      _createDotPackages(tempDir.path);
+    final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_3.');
+    _createDotPackages(tempDir.path);
 
-      tempDir.childFile('main.dart').writeAsStringSync(contents);
-      try {
-        await runCommand(
-          command: AnalyzeCommand(workingDirectory: globals.fs.directory(tempDir)),
-          arguments: <String>['analyze', '--no-pub'],
-          statusTextContains: <String>['No issues found!'],
-        );
-      } finally {
-        tryToDelete(tempDir);
-      }
-    }, overrides: <Type, Generator>{
-      ...noColorTerminalOverride
-    });
+    tempDir.childFile('main.dart').writeAsStringSync(contents);
+    try {
+      await runCommand(
+        command: AnalyzeCommand(
+          workingDirectory: fileSystem.directory(tempDir),
+          platform: _kNoColorTerminalPlatform,
+          fileSystem: fileSystem,
+          logger: logger,
+          processManager: processManager,
+          terminal: terminal,
+        ),
+        arguments: <String>['analyze', '--no-pub'],
+        statusTextContains: <String>['No issues found!'],
+      );
+    } finally {
+      tryToDelete(tempDir);
+    }
+  });
 
-    testUsingContext('returns no issues for todo comments', () async {
-      const String contents = '''
+  testUsingContext('analyze once returns no issues for todo comments', () async {
+    const String contents = '''
 // TODO(foobar):
 StringBuffer bar = StringBuffer('baz');
 ''';
-      final Directory tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_analyze_once_test_4.');
-      _createDotPackages(tempDir.path);
+    final Directory tempDir = fileSystem.systemTempDirectory.createTempSync('flutter_analyze_once_test_4.');
+    _createDotPackages(tempDir.path);
 
-      tempDir.childFile('main.dart').writeAsStringSync(contents);
-      try {
-        await runCommand(
-          command: AnalyzeCommand(workingDirectory: globals.fs.directory(tempDir)),
-          arguments: <String>['analyze', '--no-pub'],
-          statusTextContains: <String>['No issues found!'],
-        );
-      } finally {
-        tryToDelete(tempDir);
-      }
-    }, overrides: <Type, Generator>{
-      ...noColorTerminalOverride
-    });
+    tempDir.childFile('main.dart').writeAsStringSync(contents);
+    try {
+      await runCommand(
+        command: AnalyzeCommand(
+          workingDirectory: fileSystem.directory(tempDir),
+          platform: _kNoColorTerminalPlatform,
+          terminal: terminal,
+          processManager: processManager,
+          logger: logger,
+          fileSystem: fileSystem,
+        ),
+        arguments: <String>['analyze', '--no-pub'],
+        statusTextContains: <String>['No issues found!'],
+      );
+    } finally {
+      tryToDelete(tempDir);
+    }
   });
 }
 
@@ -265,33 +341,6 @@
   }
 }
 
-Future<void> runCommand({
-  FlutterCommand command,
-  List<String> arguments,
-  List<String> statusTextContains,
-  List<String> errorTextContains,
-  bool toolExit = false,
-  String exitMessageContains,
-}) async {
-  try {
-    arguments.insert(0, '--flutter-root=${Cache.flutterRoot}');
-    await createTestCommandRunner(command).run(arguments);
-    expect(toolExit, isFalse, reason: 'Expected ToolExit exception');
-  } on ToolExit catch (e) {
-    if (!toolExit) {
-      testLogger.clear();
-      rethrow;
-    }
-    if (exitMessageContains != null) {
-      expect(e.message, contains(exitMessageContains));
-    }
-  }
-  assertContains(testLogger.statusText, statusTextContains);
-  assertContains(testLogger.errorText, errorTextContains);
-
-  testLogger.clear();
-}
-
 const String mainDartSrc = r'''
 import 'package:flutter/material.dart';
 
diff --git a/packages/flutter_tools/test/general.shard/dart/sdk_validation_test.dart b/packages/flutter_tools/test/general.shard/dart/sdk_validation_test.dart
deleted file mode 100644
index e91b4cc..0000000
--- a/packages/flutter_tools/test/general.shard/dart/sdk_validation_test.dart
+++ /dev/null
@@ -1,77 +0,0 @@
-// Copyright 2014 The Flutter Authors. 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:async';
-
-import 'package:flutter_tools/src/base/file_system.dart';
-import 'package:flutter_tools/src/cache.dart';
-import 'package:flutter_tools/src/dart/analysis.dart';
-import 'package:flutter_tools/src/dart/pub.dart';
-import 'package:flutter_tools/src/dart/sdk.dart';
-import 'package:flutter_tools/src/globals.dart' as globals;
-
-import '../../src/common.dart';
-import '../../src/context.dart';
-
-void main() {
-  testSampleProject('ui', 'Window');
-  testSampleProject('html', 'HttpStatus');
-  testSampleProject('js', 'allowInterop');
-  testSampleProject('js_util', 'jsify');
-}
-
-void testSampleProject(String lib, String member) {
-  testUsingContext('contains dart:$lib', () async {
-    Cache.disableLocking();
-    final Directory projectDirectory = globals.fs.systemTempDirectory.createTempSync('flutter_sdk_validation_${lib}_test.').absolute;
-
-    try {
-      final File pubspecFile = globals.fs.file(globals.fs.path.join(projectDirectory.path, 'pubspec.yaml'));
-      pubspecFile.writeAsStringSync('''
-name: ${lib}_project
-dependencies:
-  flutter:
-    sdk: flutter
-''');
-
-      final File dartFile = globals.fs.file(globals.fs.path.join(projectDirectory.path, 'lib', 'main.dart'));
-      dartFile.parent.createSync();
-      dartFile.writeAsStringSync('''
-import 'dart:$lib' as $lib;
-void main() {
-  // ignore: unnecessary_statements
-  $lib.$member;
-}
-''');
-
-      await pub.get(context: PubContext.flutterTests, directory: projectDirectory.path);
-      final AnalysisServer server = AnalysisServer(dartSdkPath, <String>[projectDirectory.path]);
-      try {
-        final int errorCount = await analyze(server);
-        expect(errorCount, 0);
-      } finally {
-        await server.dispose();
-      }
-    } finally {
-      tryToDelete(projectDirectory);
-      Cache.enableLocking();
-    }
-  }, skip: true);
-}
-
-Future<int> analyze(AnalysisServer server) async {
-  int errorCount = 0;
-  final Future<bool> onDone = server.onAnalyzing.where((bool analyzing) => analyzing == false).first;
-  server.onErrors.listen((FileAnalysisErrors result) {
-    for (final AnalysisError error in result.errors) {
-      print(error.toString().trim());
-    }
-    errorCount += result.errors.length;
-  });
-
-  await server.start();
-  await onDone;
-
-  return errorCount;
-}