Unverified Commit 2b218fd1 authored by Elias Yishak's avatar Elias Yishak Committed by GitHub

Migrate command usage values (#139383)

Related to the tracker issue:
- https://github.com/flutter/flutter/issues/128251

This PR migrates the `Usage.command` static method that sent custom dimensions for each command (if applicable). The screenshot below shows the different places where the `usageValues` getter is overwritten to return the necessary custom dimensions for that command.

<img width="285" alt="image" src="https://github.com/flutter/flutter/assets/42216813/e32d5100-0e17-4a4d-8f21-327a8c113a19">
parent 2c22944d
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../artifacts.dart'; import '../artifacts.dart';
import '../base/common.dart'; import '../base/common.dart';
...@@ -126,6 +127,8 @@ class AssembleCommand extends FlutterCommand { ...@@ -126,6 +127,8 @@ class AssembleCommand extends FlutterCommand {
final BuildSystem _buildSystem; final BuildSystem _buildSystem;
late final FlutterProject _flutterProject = FlutterProject.current();
@override @override
String get description => 'Assemble and build Flutter resources.'; String get description => 'Assemble and build Flutter resources.';
...@@ -136,18 +139,18 @@ class AssembleCommand extends FlutterCommand { ...@@ -136,18 +139,18 @@ class AssembleCommand extends FlutterCommand {
String get category => FlutterCommandCategory.project; String get category => FlutterCommandCategory.project;
@override @override
Future<CustomDimensions> get usageValues async { Future<CustomDimensions> get usageValues async => CustomDimensions(
final FlutterProject flutterProject = FlutterProject.current(); commandBuildBundleTargetPlatform: _environment.defines[kTargetPlatform],
try { commandBuildBundleIsModule: _flutterProject.isModule,
return CustomDimensions( );
commandBuildBundleTargetPlatform: _environment.defines[kTargetPlatform],
commandBuildBundleIsModule: flutterProject.isModule, @override
); Future<Event> unifiedAnalyticsUsageValues(String commandPath) async => Event.commandUsageValues(
} on Exception { workflow: commandPath,
// We've failed to send usage. commandHasTerminal: hasTerminal,
} buildBundleTargetPlatform: _environment.defines[kTargetPlatform],
return const CustomDimensions(); buildBundleIsModule: _flutterProject.isModule,
} );
@override @override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async { Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
...@@ -208,22 +211,21 @@ class AssembleCommand extends FlutterCommand { ...@@ -208,22 +211,21 @@ class AssembleCommand extends FlutterCommand {
/// The environmental configuration for a build invocation. /// The environmental configuration for a build invocation.
Environment _createEnvironment() { Environment _createEnvironment() {
final FlutterProject flutterProject = FlutterProject.current();
String? output = stringArg('output'); String? output = stringArg('output');
if (output == null) { if (output == null) {
throwToolExit('--output directory is required for assemble.'); throwToolExit('--output directory is required for assemble.');
} }
// If path is relative, make it absolute from flutter project. // If path is relative, make it absolute from flutter project.
if (globals.fs.path.isRelative(output)) { if (globals.fs.path.isRelative(output)) {
output = globals.fs.path.join(flutterProject.directory.path, output); output = globals.fs.path.join(_flutterProject.directory.path, output);
} }
final Artifacts artifacts = globals.artifacts!; final Artifacts artifacts = globals.artifacts!;
final Environment result = Environment( final Environment result = Environment(
outputDir: globals.fs.directory(output), outputDir: globals.fs.directory(output),
buildDir: flutterProject.directory buildDir: _flutterProject.directory
.childDirectory('.dart_tool') .childDirectory('.dart_tool')
.childDirectory('flutter_build'), .childDirectory('flutter_build'),
projectDir: flutterProject.directory, projectDir: _flutterProject.directory,
defines: _parseDefines(stringsArg('define')), defines: _parseDefines(stringsArg('define')),
inputs: _parseDefines(stringsArg('input')), inputs: _parseDefines(stringsArg('input')),
cacheDir: globals.cache.getRoot(), cacheDir: globals.cache.getRoot(),
...@@ -266,7 +268,7 @@ class AssembleCommand extends FlutterCommand { ...@@ -266,7 +268,7 @@ class AssembleCommand extends FlutterCommand {
} }
results[kDeferredComponents] = 'false'; results[kDeferredComponents] = 'false';
if (FlutterProject.current().manifest.deferredComponents != null && isDeferredComponentsTargets() && !isDebug()) { if (_flutterProject.manifest.deferredComponents != null && isDeferredComponentsTargets() && !isDebug()) {
results[kDeferredComponents] = 'true'; results[kDeferredComponents] = 'true';
} }
if (argumentResults.wasParsed(FlutterOptions.kExtraFrontEndOptions)) { if (argumentResults.wasParsed(FlutterOptions.kExtraFrontEndOptions)) {
...@@ -297,7 +299,7 @@ class AssembleCommand extends FlutterCommand { ...@@ -297,7 +299,7 @@ class AssembleCommand extends FlutterCommand {
"Try re-running 'flutter build ios' or the appropriate build command." "Try re-running 'flutter build ios' or the appropriate build command."
); );
} }
if (FlutterProject.current().manifest.deferredComponents != null if (_flutterProject.manifest.deferredComponents != null
&& decodedDefines.contains('validate-deferred-components=true') && decodedDefines.contains('validate-deferred-components=true')
&& deferredTargets.isNotEmpty && deferredTargets.isNotEmpty
&& !isDebug()) { && !isDebug()) {
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:unified_analytics/unified_analytics.dart';
import '../android/android_builder.dart'; import '../android/android_builder.dart';
import '../android/android_sdk.dart'; import '../android/android_sdk.dart';
import '../android/gradle_utils.dart'; import '../android/gradle_utils.dart';
...@@ -94,6 +96,25 @@ class BuildAarCommand extends BuildSubCommand { ...@@ -94,6 +96,25 @@ class BuildAarCommand extends BuildSubCommand {
); );
} }
@override
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async {
final String projectType;
if (project.manifest.isModule) {
projectType = 'module';
} else if (project.manifest.isPlugin) {
projectType = 'plugin';
} else {
projectType = 'app';
}
return Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
buildAarProjectType: projectType,
buildAarTargetPlatform: stringsArg('target-platform').join(','),
);
}
@override @override
final String description = 'Build a repository containing an AAR and a POM file.\n\n' final String description = 'Build a repository containing an AAR and a POM file.\n\n'
'By default, AARs are built for `release`, `debug` and `profile`.\n' 'By default, AARs are built for `release`, `debug` and `profile`.\n'
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:unified_analytics/unified_analytics.dart';
import '../android/android_builder.dart'; import '../android/android_builder.dart';
import '../android/build_validation.dart'; import '../android/build_validation.dart';
import '../android/gradle_utils.dart'; import '../android/gradle_utils.dart';
...@@ -98,6 +100,30 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -98,6 +100,30 @@ class BuildApkCommand extends BuildSubCommand {
); );
} }
@override
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async {
final String buildMode;
if (boolArg('release')) {
buildMode = 'release';
} else if (boolArg('debug')) {
buildMode = 'debug';
} else if (boolArg('profile')) {
buildMode = 'profile';
} else {
// The build defaults to release.
buildMode = 'release';
}
return Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
buildApkTargetPlatform: stringsArg('target-platform').join(','),
buildApkBuildMode: buildMode,
buildApkSplitPerAbi: boolArg('split-per-abi'),
);
}
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (globals.androidSdk == null) { if (globals.androidSdk == null) {
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:unified_analytics/unified_analytics.dart';
import '../android/android_builder.dart'; import '../android/android_builder.dart';
import '../android/build_validation.dart'; import '../android/build_validation.dart';
import '../android/deferred_components_prebuild_validator.dart'; import '../android/deferred_components_prebuild_validator.dart';
...@@ -105,6 +107,29 @@ class BuildAppBundleCommand extends BuildSubCommand { ...@@ -105,6 +107,29 @@ class BuildAppBundleCommand extends BuildSubCommand {
); );
} }
@override
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async {
final String buildMode;
if (boolArg('release')) {
buildMode = 'release';
} else if (boolArg('debug')) {
buildMode = 'debug';
} else if (boolArg('profile')) {
buildMode = 'profile';
} else {
// The build defaults to release.
buildMode = 'release';
}
return Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
buildAppBundleTargetPlatform: stringsArg('target-platform').join(','),
buildAppBundleBuildMode: buildMode,
);
}
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (globals.androidSdk == null) { if (globals.androidSdk == null) {
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:unified_analytics/unified_analytics.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart'; import '../bundle.dart';
...@@ -83,6 +85,18 @@ class BuildBundleCommand extends BuildSubCommand { ...@@ -83,6 +85,18 @@ class BuildBundleCommand extends BuildSubCommand {
); );
} }
@override
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async {
final String projectDir = globals.fs.file(targetFile).parent.parent.path;
final FlutterProject flutterProject = FlutterProject.fromDirectory(globals.fs.directory(projectDir));
return Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
buildBundleTargetPlatform: stringArg('target-platform'),
buildBundleIsModule: flutterProject.isModule,
);
}
@override @override
Future<void> validateCommand() async { Future<void> validateCommand() async {
if (boolArg('tree-shake-icons')) { if (boolArg('tree-shake-icons')) {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../android/gradle_utils.dart' as gradle; import '../android/gradle_utils.dart' as gradle;
import '../base/common.dart'; import '../base/common.dart';
...@@ -91,6 +92,15 @@ class CreateCommand extends CreateBase { ...@@ -91,6 +92,15 @@ class CreateCommand extends CreateBase {
); );
} }
@override
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async => Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
createProjectType: stringArg('template'),
createAndroidLanguage: stringArg('android-language'),
createIosLanguage: stringArg('ios-language'),
);
// Lazy-initialize the net utilities with values from the context. // Lazy-initialize the net utilities with values from the context.
late final Net _net = Net( late final Net _net = Net(
httpClientFactory: context.get<HttpClientFactory>(), httpClientFactory: context.get<HttpClientFactory>(),
......
...@@ -389,6 +389,24 @@ class PackagesGetCommand extends FlutterCommand { ...@@ -389,6 +389,24 @@ class PackagesGetCommand extends FlutterCommand {
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
late final Future<List<Plugin>> _pluginsFound = (() async {
final FlutterProject? rootProject = _rootProject;
if (rootProject == null) {
return <Plugin>[];
}
return findPlugins(rootProject, throwOnError: false);
})();
late final String? _androidEmbeddingVersion = (() {
final FlutterProject? rootProject = _rootProject;
if (rootProject == null) {
return null;
}
return rootProject.android.getEmbeddingVersion().toString().split('.').last;
})();
/// The pub packages usage values are incorrect since these are calculated/sent /// The pub packages usage values are incorrect since these are calculated/sent
/// before pub get completes. This needs to be performed after dependency resolution. /// before pub get completes. This needs to be performed after dependency resolution.
@override @override
...@@ -405,7 +423,7 @@ class PackagesGetCommand extends FlutterCommand { ...@@ -405,7 +423,7 @@ class PackagesGetCommand extends FlutterCommand {
if (hasPlugins) { if (hasPlugins) {
// Do not fail pub get if package config files are invalid before pub has // Do not fail pub get if package config files are invalid before pub has
// had a chance to run. // had a chance to run.
final List<Plugin> plugins = await findPlugins(rootProject, throwOnError: false); final List<Plugin> plugins = await _pluginsFound;
numberPlugins = plugins.length; numberPlugins = plugins.length;
} else { } else {
numberPlugins = 0; numberPlugins = 0;
...@@ -414,7 +432,38 @@ class PackagesGetCommand extends FlutterCommand { ...@@ -414,7 +432,38 @@ class PackagesGetCommand extends FlutterCommand {
return CustomDimensions( return CustomDimensions(
commandPackagesNumberPlugins: numberPlugins, commandPackagesNumberPlugins: numberPlugins,
commandPackagesProjectModule: rootProject.isModule, commandPackagesProjectModule: rootProject.isModule,
commandPackagesAndroidEmbeddingVersion: rootProject.android.getEmbeddingVersion().toString().split('.').last, commandPackagesAndroidEmbeddingVersion: _androidEmbeddingVersion,
);
}
/// The pub packages usage values are incorrect since these are calculated/sent
/// before pub get completes. This needs to be performed after dependency resolution.
@override
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async {
final FlutterProject? rootProject = _rootProject;
if (rootProject == null) {
return Event.commandUsageValues(workflow: commandPath, commandHasTerminal: hasTerminal);
}
final int numberPlugins;
// Do not send plugin analytics if pub has not run before.
final bool hasPlugins = rootProject.flutterPluginsDependenciesFile.existsSync()
&& rootProject.packageConfigFile.existsSync();
if (hasPlugins) {
// Do not fail pub get if package config files are invalid before pub has
// had a chance to run.
final List<Plugin> plugins = await _pluginsFound;
numberPlugins = plugins.length;
} else {
numberPlugins = 0;
}
return Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
packagesNumberPlugins: numberPlugins,
packagesProjectModule: rootProject.isModule,
packagesAndroidEmbeddingVersion: _androidEmbeddingVersion,
); );
} }
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'dart:async'; import 'dart:async';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:unified_analytics/unified_analytics.dart' as analytics;
import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service.dart';
import '../android/android_device.dart'; import '../android/android_device.dart';
...@@ -447,6 +448,43 @@ class RunCommand extends RunCommandBase { ...@@ -447,6 +448,43 @@ class RunCommand extends RunCommandBase {
@override @override
Future<CustomDimensions> get usageValues async { Future<CustomDimensions> get usageValues async {
final AnalyticsUsageValuesRecord record = await _sharedAnalyticsUsageValues;
return CustomDimensions(
commandRunIsEmulator: record.runIsEmulator,
commandRunTargetName: record.runTargetName,
commandRunTargetOsVersion: record.runTargetOsVersion,
commandRunModeName: record.runModeName,
commandRunProjectModule: record.runProjectModule,
commandRunProjectHostLanguage: record.runProjectHostLanguage,
commandRunAndroidEmbeddingVersion: record.runAndroidEmbeddingVersion,
commandRunEnableImpeller: record.runEnableImpeller,
commandRunIOSInterfaceType: record.runIOSInterfaceType,
commandRunIsTest: record.runIsTest,
);
}
@override
Future<analytics.Event> unifiedAnalyticsUsageValues(String commandPath) async {
final AnalyticsUsageValuesRecord record = await _sharedAnalyticsUsageValues;
return analytics.Event.commandUsageValues(
workflow: commandPath,
commandHasTerminal: hasTerminal,
runIsEmulator: record.runIsEmulator,
runTargetName: record.runTargetName,
runTargetOsVersion: record.runTargetOsVersion,
runModeName: record.runModeName,
runProjectModule: record.runProjectModule,
runProjectHostLanguage: record.runProjectHostLanguage,
runAndroidEmbeddingVersion: record.runAndroidEmbeddingVersion,
runEnableImpeller: record.runEnableImpeller,
runIOSInterfaceType: record.runIOSInterfaceType,
runIsTest: record.runIsTest,
);
}
late final Future<AnalyticsUsageValuesRecord> _sharedAnalyticsUsageValues = (() async {
String deviceType, deviceOsVersion; String deviceType, deviceOsVersion;
bool isEmulator; bool isEmulator;
bool anyAndroidDevices = false; bool anyAndroidDevices = false;
...@@ -512,19 +550,19 @@ class RunCommand extends RunCommandBase { ...@@ -512,19 +550,19 @@ class RunCommand extends RunCommandBase {
final BuildInfo buildInfo = await getBuildInfo(); final BuildInfo buildInfo = await getBuildInfo();
final String modeName = buildInfo.modeName; final String modeName = buildInfo.modeName;
return CustomDimensions( return (
commandRunIsEmulator: isEmulator, runIsEmulator: isEmulator,
commandRunTargetName: deviceType, runTargetName: deviceType,
commandRunTargetOsVersion: deviceOsVersion, runTargetOsVersion: deviceOsVersion,
commandRunModeName: modeName, runModeName: modeName,
commandRunProjectModule: FlutterProject.current().isModule, runProjectModule: FlutterProject.current().isModule,
commandRunProjectHostLanguage: hostLanguage.join(','), runProjectHostLanguage: hostLanguage.join(','),
commandRunAndroidEmbeddingVersion: androidEmbeddingVersion, runAndroidEmbeddingVersion: androidEmbeddingVersion,
commandRunEnableImpeller: enableImpeller.asBool, runEnableImpeller: enableImpeller.asBool,
commandRunIOSInterfaceType: iOSInterfaceType, runIOSInterfaceType: iOSInterfaceType,
commandRunIsTest: targetFile.endsWith('_test.dart'), runIsTest: targetFile.endsWith('_test.dart'),
); );
} })();
@override @override
bool get shouldRunPub { bool get shouldRunPub {
...@@ -801,3 +839,17 @@ class RunCommand extends RunCommandBase { ...@@ -801,3 +839,17 @@ class RunCommand extends RunCommandBase {
); );
} }
} }
/// Schema for the usage values to send for analytics reporting.
typedef AnalyticsUsageValuesRecord = ({
String? runAndroidEmbeddingVersion,
bool? runEnableImpeller,
String? runIOSInterfaceType,
bool runIsEmulator,
bool runIsTest,
String runModeName,
String runProjectHostLanguage,
bool runProjectModule,
String runTargetName,
String runTargetOsVersion,
});
...@@ -366,6 +366,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -366,6 +366,9 @@ abstract class FlutterCommand extends Command<void> {
return bundle.defaultMainPath; return bundle.defaultMainPath;
} }
/// Indicates if the currenet command running has a terminal attached.
bool get hasTerminal => globals.stdio.hasTerminal;
/// Path to the Dart's package config file. /// Path to the Dart's package config file.
/// ///
/// This can be overridden by some of its subclasses. /// This can be overridden by some of its subclasses.
...@@ -1359,6 +1362,14 @@ abstract class FlutterCommand extends Command<void> { ...@@ -1359,6 +1362,14 @@ abstract class FlutterCommand extends Command<void> {
/// Additional usage values to be sent with the usage ping. /// Additional usage values to be sent with the usage ping.
Future<CustomDimensions> get usageValues async => const CustomDimensions(); Future<CustomDimensions> get usageValues async => const CustomDimensions();
/// Additional usage values to be sent with the usage ping for
/// package:unified_analytics.
///
/// Implementations of [FlutterCommand] can override this getter in order
/// to add additional parameters in the [Event.commandUsageValues] constructor.
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async =>
Event.commandUsageValues(workflow: commandPath, commandHasTerminal: hasTerminal);
/// Runs this command. /// Runs this command.
/// ///
/// Rather than overriding this method, subclasses should override /// Rather than overriding this method, subclasses should override
...@@ -1621,7 +1632,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -1621,7 +1632,7 @@ abstract class FlutterCommand extends Command<void> {
commandPath: commandPath, commandPath: commandPath,
result: commandResult.toString(), result: commandResult.toString(),
maxRss: maxRss, maxRss: maxRss,
commandHasTerminal: globals.stdio.hasTerminal, commandHasTerminal: hasTerminal,
)); ));
// Send timing. // Send timing.
...@@ -1748,9 +1759,17 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and ...@@ -1748,9 +1759,17 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
setupApplicationPackages(); setupApplicationPackages();
if (commandPath != null) { if (commandPath != null) {
// Until the GA4 migration is complete, we will continue to send to the GA3 instance
// as well as GA4. Once migration is complete, we will only make a call for GA4 values
final List<Object> pairOfUsageValues = await Future.wait<Object>(<Future<Object>>[
usageValues,
unifiedAnalyticsUsageValues(commandPath),
]);
Usage.command(commandPath, parameters: CustomDimensions( Usage.command(commandPath, parameters: CustomDimensions(
commandHasTerminal: globals.stdio.hasTerminal, commandHasTerminal: hasTerminal,
).merge(await usageValues)); ).merge(pairOfUsageValues[0] as CustomDimensions));
analytics.send(pairOfUsageValues[1] as Event);
} }
return runCommand(); return runCommand();
......
...@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/commands/assemble.dart'; ...@@ -13,6 +13,7 @@ import 'package:flutter_tools/src/commands/assemble.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
...@@ -24,6 +25,14 @@ void main() { ...@@ -24,6 +25,14 @@ void main() {
Cache.disableLocking(); Cache.disableLocking();
Cache.flutterRoot = ''; Cache.flutterRoot = '';
final StackTrace stackTrace = StackTrace.current; final StackTrace stackTrace = StackTrace.current;
late FakeAnalytics fakeAnalytics;
setUp(() {
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: MemoryFileSystem.test(),
fakeFlutterVersion: FakeFlutterVersion(),
);
});
testUsingContext('flutter assemble can run a build', () async { testUsingContext('flutter assemble can run a build', () async {
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand( final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand(
...@@ -85,6 +94,31 @@ void main() { ...@@ -85,6 +94,31 @@ void main() {
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
}); });
testUsingContext('flutter assemble sends usage values correctly with platform', () async {
final AssembleCommand command = AssembleCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)));
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
await commandRunner.run(<String>['assemble', '-o Output', '-dTargetPlatform=darwin', '-dDarwinArchs=x86_64', 'debug_macos_bundle_flutter_assets']);
expect(
fakeAnalytics.sentEvents,
contains(
Event.commandUsageValues(
workflow: 'assemble',
commandHasTerminal: false,
buildBundleTargetPlatform: 'darwin',
buildBundleIsModule: false,
),
),
);
}, overrides: <Type, Generator>{
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
Analytics: () => fakeAnalytics,
});
testUsingContext('flutter assemble throws ToolExit if not provided with output', () async { testUsingContext('flutter assemble throws ToolExit if not provided with output', () async {
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand( final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand(
buildSystem: TestBuildSystem.all(BuildResult(success: true)), buildSystem: TestBuildSystem.all(BuildResult(success: true)),
......
...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/process.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/build.dart'; import 'package:flutter_tools/src/commands/build.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
...@@ -26,6 +27,7 @@ void main() { ...@@ -26,6 +27,7 @@ void main() {
late FakeProcessManager processManager; late FakeProcessManager processManager;
late Platform platform; late Platform platform;
late Cache cache; late Cache cache;
late FakeAnalytics fakeAnalytics;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
...@@ -44,6 +46,10 @@ void main() { ...@@ -44,6 +46,10 @@ void main() {
logger: logger, logger: logger,
processManager: processManager, processManager: processManager,
); );
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: fs,
fakeFlutterVersion: FakeFlutterVersion(),
);
}); });
testUsingContext('will not build an AAR for a plugin', () async { testUsingContext('will not build an AAR for a plugin', () async {
...@@ -126,9 +132,19 @@ flutter: ...@@ -126,9 +132,19 @@ flutter:
await createTestCommandRunner(command).run(const <String>['build', 'aar', '--no-pub']); await createTestCommandRunner(command).run(const <String>['build', 'aar', '--no-pub']);
expect(processManager, hasNoRemainingExpectations); expect(processManager, hasNoRemainingExpectations);
expect(
fakeAnalytics.sentEvents,
contains(Event.commandUsageValues(
workflow: 'build/aar',
commandHasTerminal: false,
buildAarProjectType: 'module',
buildAarTargetPlatform: 'android-arm,android-arm64,android-x64',
)),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
Platform: () => platform, Platform: () => platform,
ProcessManager: () => processManager, ProcessManager: () => processManager,
Analytics: () => fakeAnalytics,
}); });
} }
...@@ -33,6 +33,7 @@ import 'package:flutter_tools/src/runner/flutter_command.dart'; ...@@ -33,6 +33,7 @@ import 'package:flutter_tools/src/runner/flutter_command.dart';
import 'package:flutter_tools/src/vmservice.dart'; import 'package:flutter_tools/src/vmservice.dart';
import 'package:flutter_tools/src/web/compile.dart'; import 'package:flutter_tools/src/web/compile.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart' as analytics;
import 'package:vm_service/vm_service.dart'; import 'package:vm_service/vm_service.dart';
import '../../src/common.dart'; import '../../src/common.dart';
...@@ -192,6 +193,7 @@ void main() { ...@@ -192,6 +193,7 @@ void main() {
late Artifacts artifacts; late Artifacts artifacts;
late TestUsage usage; late TestUsage usage;
late FakeAnsiTerminal fakeTerminal; late FakeAnsiTerminal fakeTerminal;
late analytics.FakeAnalytics fakeAnalytics;
setUpAll(() { setUpAll(() {
Cache.disableLocking(); Cache.disableLocking();
...@@ -211,6 +213,10 @@ void main() { ...@@ -211,6 +213,10 @@ void main() {
libDir.createSync(); libDir.createSync();
final File mainFile = libDir.childFile('main.dart'); final File mainFile = libDir.childFile('main.dart');
mainFile.writeAsStringSync('void main() {}'); mainFile.writeAsStringSync('void main() {}');
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: fs,
fakeFlutterVersion: FakeFlutterVersion(),
);
}); });
testUsingContext('exits with a user message when no supported devices attached', () async { testUsingContext('exits with a user message when no supported devices attached', () async {
...@@ -478,6 +484,23 @@ void main() { ...@@ -478,6 +484,23 @@ void main() {
'cd58': 'false', 'cd58': 'false',
}) })
))); )));
expect(
fakeAnalytics.sentEvents,
contains(
analytics.Event.commandUsageValues(
workflow: 'run',
commandHasTerminal: globals.stdio.hasTerminal,
runIsEmulator: false,
runTargetName: 'ios',
runTargetOsVersion: 'iOS 13',
runModeName: 'debug',
runProjectModule: false,
runProjectHostLanguage: 'swift',
runIOSInterfaceType: 'usb',
runIsTest: false,
),
),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AnsiTerminal: () => fakeTerminal, AnsiTerminal: () => fakeTerminal,
Artifacts: () => artifacts, Artifacts: () => artifacts,
...@@ -487,6 +510,7 @@ void main() { ...@@ -487,6 +510,7 @@ void main() {
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
Usage: () => usage, Usage: () => usage,
analytics.Analytics: () => fakeAnalytics,
}); });
testUsingContext('correctly reports tests to usage', () async { testUsingContext('correctly reports tests to usage', () async {
...@@ -513,6 +537,23 @@ void main() { ...@@ -513,6 +537,23 @@ void main() {
'cd58': 'true', 'cd58': 'true',
})), })),
)); ));
expect(
fakeAnalytics.sentEvents,
contains(
analytics.Event.commandUsageValues(
workflow: 'run',
commandHasTerminal: globals.stdio.hasTerminal,
runIsEmulator: false,
runTargetName: 'ios',
runTargetOsVersion: 'iOS 13',
runModeName: 'debug',
runProjectModule: false,
runProjectHostLanguage: 'swift',
runIOSInterfaceType: 'usb',
runIsTest: true,
),
),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AnsiTerminal: () => fakeTerminal, AnsiTerminal: () => fakeTerminal,
Artifacts: () => artifacts, Artifacts: () => artifacts,
...@@ -522,6 +563,7 @@ void main() { ...@@ -522,6 +563,7 @@ void main() {
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
Usage: () => usage, Usage: () => usage,
analytics.Analytics: () => fakeAnalytics,
}); });
group('--machine', () { group('--machine', () {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_builder.dart'; import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/android/android_studio.dart'; import 'package:flutter_tools/src/android/android_studio.dart';
...@@ -16,11 +17,13 @@ import 'package:flutter_tools/src/globals.dart' as globals; ...@@ -16,11 +17,13 @@ import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/android_common.dart'; import '../../src/android_common.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fake_process_manager.dart'; import '../../src/fake_process_manager.dart';
import '../../src/fakes.dart' show FakeFlutterVersion;
import '../../src/test_flutter_command_runner.dart'; import '../../src/test_flutter_command_runner.dart';
void main() { void main() {
...@@ -29,10 +32,15 @@ void main() { ...@@ -29,10 +32,15 @@ void main() {
group('Usage', () { group('Usage', () {
late Directory tempDir; late Directory tempDir;
late TestUsage testUsage; late TestUsage testUsage;
late FakeAnalytics fakeAnalytics;
setUp(() { setUp(() {
testUsage = TestUsage(); testUsage = TestUsage();
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.'); tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: MemoryFileSystem.test(),
fakeFlutterVersion: FakeFlutterVersion(),
);
}); });
tearDown(() { tearDown(() {
...@@ -46,8 +54,21 @@ void main() { ...@@ -46,8 +54,21 @@ void main() {
expect((await command.usageValues).commandBuildApkTargetPlatform, 'android-arm,android-arm64,android-x64'); expect((await command.usageValues).commandBuildApkTargetPlatform, 'android-arm,android-arm64,android-x64');
expect(
fakeAnalytics.sentEvents,
contains(
Event.commandUsageValues(
workflow: 'apk',
commandHasTerminal: false,
buildApkTargetPlatform: 'android-arm,android-arm64,android-x64',
buildApkBuildMode: 'release',
buildApkSplitPerAbi: false,
),
),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(), AndroidBuilder: () => FakeAndroidBuilder(),
Analytics: () => fakeAnalytics,
}); });
testUsingContext('split per abi', () async { testUsingContext('split per abi', () async {
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_builder.dart'; import 'package:flutter_tools/src/android/android_builder.dart';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
...@@ -13,10 +14,12 @@ import 'package:flutter_tools/src/globals.dart' as globals; ...@@ -13,10 +14,12 @@ import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/android_common.dart'; import '../../src/android_common.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fakes.dart' show FakeFlutterVersion;
import '../../src/test_flutter_command_runner.dart'; import '../../src/test_flutter_command_runner.dart';
void main() { void main() {
...@@ -25,10 +28,15 @@ void main() { ...@@ -25,10 +28,15 @@ void main() {
group('Usage', () { group('Usage', () {
late Directory tempDir; late Directory tempDir;
late TestUsage testUsage; late TestUsage testUsage;
late FakeAnalytics fakeAnalytics;
setUp(() { setUp(() {
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.'); tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
testUsage = TestUsage(); testUsage = TestUsage();
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: MemoryFileSystem.test(),
fakeFlutterVersion: FakeFlutterVersion(),
);
}); });
tearDown(() { tearDown(() {
...@@ -42,8 +50,18 @@ void main() { ...@@ -42,8 +50,18 @@ void main() {
expect((await command.usageValues).commandBuildAppBundleTargetPlatform, 'android-arm,android-arm64,android-x64'); expect((await command.usageValues).commandBuildAppBundleTargetPlatform, 'android-arm,android-arm64,android-x64');
expect(
fakeAnalytics.sentEvents,
contains(Event.commandUsageValues(
workflow: 'appbundle',
commandHasTerminal: false,
buildAppBundleTargetPlatform: 'android-arm,android-arm64,android-x64',
buildAppBundleBuildMode: 'release',
)),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidBuilder: () => FakeAndroidBuilder(), AndroidBuilder: () => FakeAndroidBuilder(),
Analytics: () => fakeAnalytics,
}); });
testUsingContext('build type', () async { testUsingContext('build type', () async {
......
...@@ -17,6 +17,7 @@ import 'package:flutter_tools/src/globals.dart' as globals; ...@@ -17,6 +17,7 @@ import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import 'package:unified_analytics/unified_analytics.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
...@@ -30,21 +31,26 @@ void main() { ...@@ -30,21 +31,26 @@ void main() {
late FakeBundleBuilder fakeBundleBuilder; late FakeBundleBuilder fakeBundleBuilder;
final FileSystemStyle fileSystemStyle = globals.fs.path.separator == '/' ? final FileSystemStyle fileSystemStyle = globals.fs.path.separator == '/' ?
FileSystemStyle.posix : FileSystemStyle.windows; FileSystemStyle.posix : FileSystemStyle.windows;
late FakeAnalytics fakeAnalytics;
MemoryFileSystem fsFactory() {
return MemoryFileSystem.test(style: fileSystemStyle);
}
setUp(() { setUp(() {
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.'); tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
fakeBundleBuilder = FakeBundleBuilder(); fakeBundleBuilder = FakeBundleBuilder();
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: fsFactory(),
fakeFlutterVersion: FakeFlutterVersion(),
);
}); });
tearDown(() { tearDown(() {
tryToDelete(tempDir); tryToDelete(tempDir);
}); });
MemoryFileSystem fsFactory() {
return MemoryFileSystem.test(style: fileSystemStyle);
}
Future<BuildBundleCommand> runCommandIn(String projectPath, { List<String>? arguments }) async { Future<BuildBundleCommand> runCommandIn(String projectPath, { List<String>? arguments }) async {
final BuildBundleCommand command = BuildBundleCommand( final BuildBundleCommand command = BuildBundleCommand(
logger: BufferLogger.test(), logger: BufferLogger.test(),
...@@ -67,6 +73,19 @@ void main() { ...@@ -67,6 +73,19 @@ void main() {
final BuildBundleCommand command = await runCommandIn(projectPath); final BuildBundleCommand command = await runCommandIn(projectPath);
expect((await command.usageValues).commandBuildBundleIsModule, true); expect((await command.usageValues).commandBuildBundleIsModule, true);
expect(
fakeAnalytics.sentEvents,
contains(
Event.commandUsageValues(
workflow: 'bundle',
commandHasTerminal: false,
buildBundleTargetPlatform: 'android-arm',
buildBundleIsModule: true,
),
),
);
}, overrides: <Type, Generator>{
Analytics: () => fakeAnalytics,
}); });
testUsingContext('bundle getUsage indicate that project is not a module', () async { testUsingContext('bundle getUsage indicate that project is not a module', () async {
...@@ -76,6 +95,19 @@ void main() { ...@@ -76,6 +95,19 @@ void main() {
final BuildBundleCommand command = await runCommandIn(projectPath); final BuildBundleCommand command = await runCommandIn(projectPath);
expect((await command.usageValues).commandBuildBundleIsModule, false); expect((await command.usageValues).commandBuildBundleIsModule, false);
expect(
fakeAnalytics.sentEvents,
contains(
Event.commandUsageValues(
workflow: 'bundle',
commandHasTerminal: false,
buildBundleTargetPlatform: 'android-arm',
buildBundleIsModule: false,
),
),
);
}, overrides: <Type, Generator>{
Analytics: () => fakeAnalytics,
}); });
testUsingContext('bundle getUsage indicate the target platform', () async { testUsingContext('bundle getUsage indicate the target platform', () async {
......
...@@ -7,6 +7,7 @@ import 'dart:convert'; ...@@ -7,6 +7,7 @@ import 'dart:convert';
import 'dart:io' as io; import 'dart:io' as io;
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/memory.dart';
import 'package:file_testing/file_testing.dart'; import 'package:file_testing/file_testing.dart';
import 'package:flutter_tools/src/android/gradle_utils.dart' show templateAndroidGradlePluginVersion, templateAndroidGradlePluginVersionForModule, templateDefaultGradleVersion; import 'package:flutter_tools/src/android/gradle_utils.dart' show templateAndroidGradlePluginVersion, templateAndroidGradlePluginVersionForModule, templateDefaultGradleVersion;
import 'package:flutter_tools/src/android/java.dart'; import 'package:flutter_tools/src/android/java.dart';
...@@ -30,6 +31,7 @@ import 'package:flutter_tools/src/version.dart'; ...@@ -30,6 +31,7 @@ import 'package:flutter_tools/src/version.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:pub_semver/pub_semver.dart'; import 'package:pub_semver/pub_semver.dart';
import 'package:pubspec_parse/pubspec_parse.dart'; import 'package:pubspec_parse/pubspec_parse.dart';
import 'package:unified_analytics/unified_analytics.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
import 'package:yaml/yaml.dart'; import 'package:yaml/yaml.dart';
...@@ -71,6 +73,7 @@ void main() { ...@@ -71,6 +73,7 @@ void main() {
late FakeProcessManager fakeProcessManager; late FakeProcessManager fakeProcessManager;
late BufferLogger logger; late BufferLogger logger;
late FakeStdio mockStdio; late FakeStdio mockStdio;
late FakeAnalytics fakeAnalytics;
setUpAll(() async { setUpAll(() async {
Cache.disableLocking(); Cache.disableLocking();
...@@ -88,6 +91,10 @@ void main() { ...@@ -88,6 +91,10 @@ void main() {
); );
fakeProcessManager = FakeProcessManager.empty(); fakeProcessManager = FakeProcessManager.empty();
mockStdio = FakeStdio(); mockStdio = FakeStdio();
fakeAnalytics = getInitializedFakeAnalyticsInstance(
fs: MemoryFileSystem.test(),
fakeFlutterVersion: fakeFlutterVersion,
);
}); });
tearDown(() { tearDown(() {
...@@ -171,10 +178,24 @@ void main() { ...@@ -171,10 +178,24 @@ void main() {
], ],
); );
expect(logger.statusText, contains('In order to run your application, type:')); expect(logger.statusText, contains('In order to run your application, type:'));
// check that we're telling them about documentation // Check that we're telling them about documentation
expect(logger.statusText, contains('https://docs.flutter.dev/')); expect(logger.statusText, contains('https://docs.flutter.dev/'));
expect(logger.statusText, contains('https://api.flutter.dev/')); expect(logger.statusText, contains('https://api.flutter.dev/'));
// check that the tests run clean
// Check for usage values sent in analytics
expect(
fakeAnalytics.sentEvents,
contains(
Event.commandUsageValues(
workflow: 'create',
commandHasTerminal: false,
createAndroidLanguage: 'java',
createIosLanguage: 'objc',
),
),
);
// Check that the tests run clean
return _runFlutterTest(projectDir); return _runFlutterTest(projectDir);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Pub: () => Pub.test( Pub: () => Pub.test(
...@@ -187,6 +208,7 @@ void main() { ...@@ -187,6 +208,7 @@ void main() {
stdio: mockStdio, stdio: mockStdio,
), ),
Logger: () => logger, Logger: () => logger,
Analytics: () => fakeAnalytics,
}); });
testUsingContext('can create a skeleton (list/detail) app', () async { testUsingContext('can create a skeleton (list/detail) app', () async {
......
...@@ -355,6 +355,11 @@ flutter: ...@@ -355,6 +355,11 @@ flutter:
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand; final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
expect((await getCommand.usageValues).commandPackagesNumberPlugins, 0); expect((await getCommand.usageValues).commandPackagesNumberPlugins, 0);
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesNumberPlugins'],
0,
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Stdio: () => mockStdio, Stdio: () => mockStdio,
Pub: () => Pub.test( Pub: () => Pub.test(
...@@ -380,6 +385,11 @@ flutter: ...@@ -380,6 +385,11 @@ flutter:
// A plugin example depends on the plugin itself, and integration_test. // A plugin example depends on the plugin itself, and integration_test.
expect((await getCommand.usageValues).commandPackagesNumberPlugins, 2); expect((await getCommand.usageValues).commandPackagesNumberPlugins, 2);
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesNumberPlugins'],
2,
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Stdio: () => mockStdio, Stdio: () => mockStdio,
Pub: () => Pub.test( Pub: () => Pub.test(
...@@ -402,6 +412,11 @@ flutter: ...@@ -402,6 +412,11 @@ flutter:
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand; final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
expect((await getCommand.usageValues).commandPackagesProjectModule, false); expect((await getCommand.usageValues).commandPackagesProjectModule, false);
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesProjectModule'],
false,
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Stdio: () => mockStdio, Stdio: () => mockStdio,
Pub: () => Pub.test( Pub: () => Pub.test(
...@@ -424,6 +439,11 @@ flutter: ...@@ -424,6 +439,11 @@ flutter:
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand; final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
expect((await getCommand.usageValues).commandPackagesProjectModule, true); expect((await getCommand.usageValues).commandPackagesProjectModule, true);
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesProjectModule'],
true,
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Stdio: () => mockStdio, Stdio: () => mockStdio,
Pub: () => Pub.test( Pub: () => Pub.test(
...@@ -455,6 +475,11 @@ flutter: ...@@ -455,6 +475,11 @@ flutter:
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand; final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v1'); expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v1');
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesAndroidEmbeddingVersion'],
'v1',
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Stdio: () => mockStdio, Stdio: () => mockStdio,
Pub: () => Pub.test( Pub: () => Pub.test(
...@@ -477,6 +502,11 @@ flutter: ...@@ -477,6 +502,11 @@ flutter:
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand; final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v2'); expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v2');
expect(
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
.eventData['packagesAndroidEmbeddingVersion'],
'v2',
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Stdio: () => mockStdio, Stdio: () => mockStdio,
Pub: () => Pub.test( Pub: () => Pub.test(
......
...@@ -373,7 +373,7 @@ bool analyticsTimingEventExists({ ...@@ -373,7 +373,7 @@ bool analyticsTimingEventExists({
}; };
for (final Event e in sentEvents) { for (final Event e in sentEvents) {
final Map<String, Object?> eventData = e.eventData; final Map<String, Object?> eventData = <String, Object?>{...e.eventData};
eventData.remove('elapsedMilliseconds'); eventData.remove('elapsedMilliseconds');
if (const DeepCollectionEquality().equals(lookup, eventData)) { if (const DeepCollectionEquality().equals(lookup, eventData)) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment