Replace MockUsage with Usage.test in build tests (#67670)

diff --git a/packages/flutter_tools/lib/src/reporting/usage.dart b/packages/flutter_tools/lib/src/reporting/usage.dart
index e1f7755..cdff5ae 100644
--- a/packages/flutter_tools/lib/src/reporting/usage.dart
+++ b/packages/flutter_tools/lib/src/reporting/usage.dart
@@ -198,6 +198,7 @@
     final bool usingLogFile = logFilePath != null && logFilePath.isNotEmpty;
 
     analyticsIOFactory ??= _defaultAnalyticsIOFactory;
+    _clock = globals.systemClock;
 
     if (// To support testing, only allow other signals to supress analytics
         // when analytics are not being shunted to a file.
@@ -272,13 +273,15 @@
   }
 
   _DefaultUsage.test() :
-      _suppressAnalytics = true,
-      _analytics = AnalyticsMock();
+      _suppressAnalytics = false,
+      _analytics = AnalyticsMock(true),
+      _clock = SystemClock.fixed(DateTime(2020, 10, 8));
 
   Analytics _analytics;
 
   bool _printedWelcome = false;
   bool _suppressAnalytics = false;
+  SystemClock _clock;
 
   @override
   bool get isFirstRun => _analytics.firstRun;
@@ -310,7 +313,7 @@
 
     final Map<String, String> paramsWithLocalTime = <String, String>{
       ...?parameters,
-      cdKey(CustomDimensions.localTime): formatDateTime(globals.systemClock.now()),
+      cdKey(CustomDimensions.localTime): formatDateTime(_clock.now()),
     };
     _analytics.sendScreenView(command, parameters: paramsWithLocalTime);
   }
@@ -329,7 +332,7 @@
 
     final Map<String, String> paramsWithLocalTime = <String, String>{
       ...?parameters,
-      cdKey(CustomDimensions.localTime): formatDateTime(globals.systemClock.now()),
+      cdKey(CustomDimensions.localTime): formatDateTime(_clock.now()),
     };
 
     _analytics.sendEvent(
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart
index 97f6d15..f6e6173 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_linux_test.dart
@@ -15,7 +15,6 @@
 import 'package:flutter_tools/src/features.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/reporting/reporting.dart';
-import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
 
 import '../../src/common.dart';
@@ -45,12 +44,12 @@
 
   FileSystem fileSystem;
   ProcessManager processManager;
-  MockUsage usage;
+  Usage usage;
 
   setUp(() {
     fileSystem = MemoryFileSystem.test();
     Cache.flutterRoot = _kTestFlutterRoot;
-    usage = MockUsage();
+    usage = Usage.test();
   });
 
   // Creates the mock files necessary to look like a Flutter project.
@@ -399,11 +398,15 @@
       ..createSync(recursive: true)
       ..writeAsBytesSync(List<int>.filled(10000, 0));
 
-    await createTestCommandRunner(command).run(
-      const <String>['build', 'linux', '--no-pub', '--analyze-size']
+    // Capture Usage.test() events.
+    final StringBuffer buffer = await capturedConsolePrint(() =>
+      createTestCommandRunner(command).run(
+        const <String>['build', 'linux', '--no-pub', '--analyze-size']
+      )
     );
+
     expect(testLogger.statusText, contains('A summary of your Linux bundle analysis can be found at'));
-    verify(usage.sendEvent('code-size-analysis', 'linux')).called(1);
+    expect(buffer.toString(), contains('event {category: code-size-analysis, action: linux, label: null, value: null, cd33:'));
   }, overrides: <Type, Generator>{
     FileSystem: () => fileSystem,
     ProcessManager: () => processManager,
@@ -412,5 +415,3 @@
     Usage: () => usage,
   });
 }
-
-class MockUsage extends Mock implements Usage {}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart
index a6533b1..57d4b1e 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_macos_test.dart
@@ -2,6 +2,8 @@
 // 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:args/command_runner.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/base/file_system.dart';
@@ -15,7 +17,6 @@
 import 'package:flutter_tools/src/ios/xcodeproj.dart';
 import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/reporting/reporting.dart';
-import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
 
 import '../../src/common.dart';
@@ -49,7 +50,7 @@
 
 void main() {
   FileSystem fileSystem;
-  MockUsage usage;
+  Usage usage;
 
   setUpAll(() {
     Cache.disableLocking();
@@ -57,7 +58,7 @@
 
   setUp(() {
     fileSystem = MemoryFileSystem.test();
-    usage = MockUsage();
+    usage = Usage.test();
   });
 
   // Sets up the minimal mock project files necessary to look like a Flutter project.
@@ -329,12 +330,15 @@
       ..createSync(recursive: true)
       ..writeAsBytesSync(List<int>.generate(10000, (int index) => 0));
 
-    await createTestCommandRunner(command).run(
-      const <String>['build', 'macos', '--no-pub', '--analyze-size']
+    // Capture Usage.test() events.
+    final StringBuffer buffer = await capturedConsolePrint(() =>
+      createTestCommandRunner(command).run(
+        const <String>['build', 'macos', '--no-pub', '--analyze-size']
+      )
     );
 
     expect(testLogger.statusText, contains('A summary of your macOS bundle analysis can be found at'));
-    verify(usage.sendEvent('code-size-analysis', 'macos')).called(1);
+    expect(buffer.toString(), contains('event {category: code-size-analysis, action: macos, label: null, value: null, cd33:'));
   }, overrides: <Type, Generator>{
     FileSystem: () => fileSystem,
     ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
@@ -360,5 +364,3 @@
     Usage: () => usage,
   });
 }
-
-class MockUsage extends Mock implements Usage {}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart
index 4688017..0426939 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/build_windows_test.dart
@@ -43,7 +43,7 @@
 
   ProcessManager processManager;
   MockVisualStudio mockVisualStudio;
-  MockUsage usage;
+  Usage usage;
 
   setUpAll(() {
     Cache.disableLocking();
@@ -53,7 +53,7 @@
     fileSystem = MemoryFileSystem.test(style: FileSystemStyle.windows);
     Cache.flutterRoot = flutterRoot;
     mockVisualStudio = MockVisualStudio();
-    usage = MockUsage();
+    usage = Usage.test();
   });
 
   // Creates the mock files necessary to look like a Flutter project.
@@ -403,12 +403,15 @@
       }),
     ]);
 
-    await createTestCommandRunner(command).run(
-      const <String>['windows', '--no-pub', '--analyze-size']
+    // Capture Usage.test() events.
+    final StringBuffer buffer = await capturedConsolePrint(() =>
+      createTestCommandRunner(command).run(
+        const <String>['windows', '--no-pub', '--analyze-size']
+      )
     );
 
     expect(testLogger.statusText, contains('A summary of your Windows bundle analysis can be found at'));
-    verify(usage.sendEvent('code-size-analysis', 'windows')).called(1);
+    expect(buffer.toString(), contains('event {category: code-size-analysis, action: windows, label: null, value: null, cd33:'));
   }, overrides: <Type, Generator>{
     FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
     FileSystem: () => fileSystem,
@@ -420,4 +423,3 @@
 }
 
 class MockVisualStudio extends Mock implements VisualStudio {}
-class MockUsage extends Mock implements Usage {}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart
index 96c9cb3..8eb5342 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart
@@ -81,9 +81,7 @@
     });
 
     testUsingContext('printStatus should log to stdout when logToStdout is enabled', () async {
-      final StringBuffer buffer = StringBuffer();
-
-      await runZoned<Future<void>>(() async {
+      final StringBuffer buffer = await capturedConsolePrint(() {
         final StreamController<Map<String, dynamic>> commands = StreamController<Map<String, dynamic>>();
         final StreamController<Map<String, dynamic>> responses = StreamController<Map<String, dynamic>>();
         daemon = Daemon(
@@ -93,11 +91,8 @@
           logToStdout: true,
         );
         globals.printStatus('daemon.logMessage test');
-        // Service the event loop.
-        await Future<void>.value();
-      }, zoneSpecification: ZoneSpecification(print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
-        buffer.writeln(line);
-      }));
+        return Future<void>.value();
+      });
 
       expect(buffer.toString().trim(), 'daemon.logMessage test');
     }, overrides: <Type, Generator>{
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index b6c9af3..3a3ecc2 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -2,6 +2,8 @@
 // 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:file/file.dart';
 import 'package:file/memory.dart';
 import 'package:flutter_tools/src/application_package.dart';
@@ -143,13 +145,13 @@
       Artifacts artifacts;
       MockCache mockCache;
       MockProcessManager mockProcessManager;
-      MockUsage mockUsage;
+      Usage usage;
       Directory tempDir;
 
       setUp(() {
         artifacts = Artifacts.test();
         mockCache = MockCache();
-        mockUsage = MockUsage();
+        usage = Usage.test();
         fs = MemoryFileSystem.test();
         mockProcessManager = MockProcessManager();
 
@@ -374,33 +376,19 @@
           ..writeAsStringSync('# Hello, World');
         globals.fs.currentDirectory = tempDir;
 
-        try {
-          await createTestCommandRunner(command).run(<String>[
+        // Capture Usage.test() events.
+        final StringBuffer buffer = await capturedConsolePrint(() =>
+          expectToolExitLater(createTestCommandRunner(command).run(<String>[
             'run',
             '--no-pub',
             '--no-hot',
-          ]);
-          fail('Exception expected');
-        } on ToolExit catch (e) {
-          // We expect a ToolExit because app does not start
-          expect(e.message, null);
-        } on Exception catch (e) {
-          fail('ToolExit expected, got $e');
-        }
-        final List<dynamic> captures = verify(mockUsage.sendCommand(
-          captureAny,
-          parameters: captureAnyNamed('parameters'),
-        )).captured;
-        expect(captures[0], 'run');
-        final Map<String, String> parameters = captures[1] as Map<String, String>;
-
-        expect(parameters[cdKey(CustomDimensions.commandRunIsEmulator)], 'false');
-        expect(parameters[cdKey(CustomDimensions.commandRunTargetName)], 'ios');
-        expect(parameters[cdKey(CustomDimensions.commandRunProjectHostLanguage)], 'swift');
-        expect(parameters[cdKey(CustomDimensions.commandRunTargetOsVersion)], 'iOS 13');
-        expect(parameters[cdKey(CustomDimensions.commandRunModeName)], 'debug');
-        expect(parameters[cdKey(CustomDimensions.commandRunProjectModule)], 'false');
-        expect(parameters.containsKey(cdKey(CustomDimensions.commandRunAndroidEmbeddingVersion)), false);
+          ]), isNull)
+        );
+        // Allow any CustomDimensions.localTime (cd33) timestamp.
+        final RegExp usageRegexp = RegExp(
+          'screenView {cd3: false, cd4: ios, cd22: iOS 13, cd23: debug, cd18: false, cd15: swift, cd31: false, cd47: false, cd33: .*, viewName: run'
+        );
+        expect(buffer.toString(), matches(usageRegexp));
       }, overrides: <Type, Generator>{
         ApplicationPackageFactory: () => mockApplicationPackageFactory,
         Artifacts: () => artifacts,
@@ -408,7 +396,7 @@
         DeviceManager: () => mockDeviceManager,
         FileSystem: () => fs,
         ProcessManager: () => mockProcessManager,
-        Usage: () => mockUsage,
+        Usage: () => usage,
       });
     });
 
diff --git a/packages/flutter_tools/test/src/common.dart b/packages/flutter_tools/test/src/common.dart
index c63ebd0..61f3630 100644
--- a/packages/flutter_tools/test/src/common.dart
+++ b/packages/flutter_tools/test/src/common.dart
@@ -92,6 +92,18 @@
   return runner;
 }
 
+/// Capture console print events into a string buffer.
+Future<StringBuffer> capturedConsolePrint(Future<void> Function() body) async {
+  final StringBuffer buffer = StringBuffer();
+  await runZoned<Future<void>>(() async {
+    // Service the event loop.
+    await body();
+  }, zoneSpecification: ZoneSpecification(print: (Zone self, ZoneDelegate parent, Zone zone, String line) {
+    buffer.writeln(line);
+  }));
+  return buffer;
+}
+
 /// Matcher for functions that throw [AssertionError].
 final Matcher throwsAssertionError = throwsA(isA<AssertionError>());