Improve flutter test startup time (#29404)

diff --git a/packages/flutter_tools/lib/src/commands/test.dart b/packages/flutter_tools/lib/src/commands/test.dart
index 3fa235f..85b40e8 100644
--- a/packages/flutter_tools/lib/src/commands/test.dart
+++ b/packages/flutter_tools/lib/src/commands/test.dart
@@ -10,6 +10,7 @@
 import '../base/platform.dart';
 import '../cache.dart';
 import '../codegen.dart';
+import '../dart/pub.dart';
 import '../project.dart';
 import '../runner/flutter_command.dart';
 import '../test/coverage_collector.dart';
@@ -17,7 +18,7 @@
 import '../test/runner.dart';
 import '../test/watcher.dart';
 
-class TestCommand extends FlutterCommand {
+class TestCommand extends FastFlutterCommand {
   TestCommand({ bool verboseHelp = false }) {
     requiresPubspecYaml();
     usesPubOption();
@@ -91,8 +92,7 @@
   String get description => 'Run Flutter unit tests for the current project.';
 
   @override
-  Future<void> validateCommand() async {
-    await super.validateCommand();
+  Future<FlutterCommandResult> runCommand() async {
     if (!fs.isFileSync('pubspec.yaml')) {
       throwToolExit(
         'Error: No pubspec.yaml file found in the current working directory.\n'
@@ -100,10 +100,9 @@
         'called *_test.dart and must reside in the package\'s \'test\' '
         'directory (or one of its subdirectories).');
     }
-  }
-
-  @override
-  Future<FlutterCommandResult> runCommand() async {
+    if (shouldRunPub) {
+      await pubGet(context: PubContext.getVerifyContext(name), skipPubspecYamlCheck: true);
+    }
     final List<String> names = argResults['name'];
     final List<String> plainNames = argResults['plain-name'];
     final FlutterProject flutterProject = await FlutterProject.current();
diff --git a/packages/flutter_tools/lib/src/dart/pub.dart b/packages/flutter_tools/lib/src/dart/pub.dart
index 2f1957b..60bcc8e 100644
--- a/packages/flutter_tools/lib/src/dart/pub.dart
+++ b/packages/flutter_tools/lib/src/dart/pub.dart
@@ -76,13 +76,14 @@
   bool upgrade = false,
   bool offline = false,
   bool checkLastModified = true,
+  bool skipPubspecYamlCheck = false
 }) async {
   directory ??= fs.currentDirectory.path;
 
   final File pubSpecYaml = fs.file(fs.path.join(directory, 'pubspec.yaml'));
   final File dotPackages = fs.file(fs.path.join(directory, '.packages'));
 
-  if (!pubSpecYaml.existsSync()) {
+  if (!skipPubspecYamlCheck && !pubSpecYaml.existsSync()) {
     if (!skipIfAbsent)
       throwToolExit('$directory: no pubspec.yaml found');
     return;
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 2d93bfb..4c80af5 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -662,3 +662,15 @@
 
   ApplicationPackageStore applicationPackages;
 }
+
+/// A command which runs less analytics and checks to speed up startup time.
+abstract class FastFlutterCommand extends FlutterCommand {
+  @override
+  Future<void> run() {
+    return context.run<void>(
+      name: 'command',
+      overrides: <Type, Generator>{FlutterCommand: () => this},
+      body: runCommand,
+    );
+  }
+}
diff --git a/packages/flutter_tools/lib/src/test/flutter_platform.dart b/packages/flutter_tools/lib/src/test/flutter_platform.dart
index 22aa734..d265aac 100644
--- a/packages/flutter_tools/lib/src/test/flutter_platform.dart
+++ b/packages/flutter_tools/lib/src/test/flutter_platform.dart
@@ -275,6 +275,7 @@
           flutterProject: flutterProject,
           trackWidgetCreation: trackWidgetCreation,
           compilerMessageConsumer: reportCompilerMessage,
+          initializeFromDill: testFilePath,
           // We already ran codegen once at the start, we only need to
           // configure builders.
           runCold: true,
diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart
index e1b9923..3df1df0 100644
--- a/packages/flutter_tools/lib/src/version.dart
+++ b/packages/flutter_tools/lib/src/version.dart
@@ -20,28 +20,17 @@
 class FlutterVersion {
   @visibleForTesting
   FlutterVersion([this._clock = const SystemClock()]) {
-    _channel = _runGit('git rev-parse --abbrev-ref --symbolic @{u}');
-    final String branch = _runGit('git rev-parse --abbrev-ref HEAD');
-    _branch = branch == 'HEAD' ? _channel : branch;
-
-    final int slash = _channel.indexOf('/');
-    if (slash != -1) {
-      final String remote = _channel.substring(0, slash);
-      _repositoryUrl = _runGit('git ls-remote --get-url $remote');
-      _channel = _channel.substring(slash + 1);
-    } else if (_channel.isEmpty) {
-      _channel = 'unknown';
-    }
-
     _frameworkRevision = _runGit('git log -n 1 --pretty=format:%H');
-    _frameworkAge = _runGit('git log -n 1 --pretty=format:%ar');
     _frameworkVersion = GitTagVersion.determine().frameworkVersionFor(_frameworkRevision);
   }
 
   final SystemClock _clock;
 
   String _repositoryUrl;
-  String get repositoryUrl => _repositoryUrl;
+  String get repositoryUrl {
+    final String _ = channel;
+    return _repositoryUrl;
+  }
 
   static const Set<String> officialChannels = <String>{
     'master',
@@ -64,7 +53,22 @@
   String _channel;
   /// The channel is the upstream branch.
   /// `master`, `dev`, `beta`, `stable`; or old ones, like `alpha`, `hackathon`, ...
-  String get channel => _channel;
+  String get channel {
+    if (_channel == null) {
+      final String channel = _runGit('git rev-parse --abbrev-ref --symbolic @{u}');
+      final int slash = channel.indexOf('/');
+      if (slash != -1) {
+        final String remote = channel.substring(0, slash);
+        _repositoryUrl = _runGit('git ls-remote --get-url $remote');
+        _channel = channel.substring(slash + 1);
+       } else if (channel.isEmpty) {
+        _channel = 'unknown';
+      } else {
+        _channel = channel;
+      }
+    }
+    return _channel;
+  }
 
   /// The name of the local branch.
   /// Use getBranchName() to read this.
@@ -75,7 +79,9 @@
   String get frameworkRevisionShort => _shortGitRevision(frameworkRevision);
 
   String _frameworkAge;
-  String get frameworkAge => _frameworkAge;
+  String get frameworkAge {
+    return _frameworkAge ??= _runGit('git log -n 1 --pretty=format:%ar');
+  }
 
   String _frameworkVersion;
   String get frameworkVersion => _frameworkVersion;
@@ -182,6 +188,10 @@
   /// If [redactUnknownBranches] is true and the branch is unknown,
   /// the branch name will be returned as `'[user-branch]'`.
   String getBranchName({ bool redactUnknownBranches = false }) {
+    _branch ??= () {
+      final String branch = _runGit('git rev-parse --abbrev-ref HEAD');
+      return branch == 'HEAD' ? channel : branch;
+    }();
     if (redactUnknownBranches || _branch.isEmpty) {
       // Only return the branch names we know about; arbitrary branch names might contain PII.
       if (!officialChannels.contains(_branch) && !obsoleteBranches.containsKey(_branch))
@@ -266,7 +276,7 @@
   /// writes shared cache files.
   Future<void> checkFlutterVersionFreshness() async {
     // Don't perform update checks if we're not on an official channel.
-    if (!officialChannels.contains(_channel)) {
+    if (!officialChannels.contains(channel)) {
       return;
     }
 
@@ -359,7 +369,7 @@
 
     // Cache is empty or it's been a while since the last server ping. Ping the server.
     try {
-      final DateTime remoteFrameworkCommitDate = DateTime.parse(await FlutterVersion.fetchRemoteFrameworkCommitDate(_channel));
+      final DateTime remoteFrameworkCommitDate = DateTime.parse(await FlutterVersion.fetchRemoteFrameworkCommitDate(channel));
       await versionCheckStamp.store(
         newTimeVersionWasChecked: _clock.now(),
         newKnownRemoteVersion: remoteFrameworkCommitDate,