mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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">
This commit is contained in:
parent
2c22944dd5
commit
2b218fd1fc
@ -4,6 +4,7 @@
|
||||
|
||||
import 'package:args/args.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../base/common.dart';
|
||||
@ -126,6 +127,8 @@ class AssembleCommand extends FlutterCommand {
|
||||
|
||||
final BuildSystem _buildSystem;
|
||||
|
||||
late final FlutterProject _flutterProject = FlutterProject.current();
|
||||
|
||||
@override
|
||||
String get description => 'Assemble and build Flutter resources.';
|
||||
|
||||
@ -136,18 +139,18 @@ class AssembleCommand extends FlutterCommand {
|
||||
String get category => FlutterCommandCategory.project;
|
||||
|
||||
@override
|
||||
Future<CustomDimensions> get usageValues async {
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
try {
|
||||
return CustomDimensions(
|
||||
commandBuildBundleTargetPlatform: _environment.defines[kTargetPlatform],
|
||||
commandBuildBundleIsModule: flutterProject.isModule,
|
||||
);
|
||||
} on Exception {
|
||||
// We've failed to send usage.
|
||||
}
|
||||
return const CustomDimensions();
|
||||
}
|
||||
Future<CustomDimensions> get usageValues async => CustomDimensions(
|
||||
commandBuildBundleTargetPlatform: _environment.defines[kTargetPlatform],
|
||||
commandBuildBundleIsModule: _flutterProject.isModule,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async => Event.commandUsageValues(
|
||||
workflow: commandPath,
|
||||
commandHasTerminal: hasTerminal,
|
||||
buildBundleTargetPlatform: _environment.defines[kTargetPlatform],
|
||||
buildBundleIsModule: _flutterProject.isModule,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
|
||||
@ -208,22 +211,21 @@ class AssembleCommand extends FlutterCommand {
|
||||
|
||||
/// The environmental configuration for a build invocation.
|
||||
Environment _createEnvironment() {
|
||||
final FlutterProject flutterProject = FlutterProject.current();
|
||||
String? output = stringArg('output');
|
||||
if (output == null) {
|
||||
throwToolExit('--output directory is required for assemble.');
|
||||
}
|
||||
// If path is relative, make it absolute from flutter project.
|
||||
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 Environment result = Environment(
|
||||
outputDir: globals.fs.directory(output),
|
||||
buildDir: flutterProject.directory
|
||||
buildDir: _flutterProject.directory
|
||||
.childDirectory('.dart_tool')
|
||||
.childDirectory('flutter_build'),
|
||||
projectDir: flutterProject.directory,
|
||||
projectDir: _flutterProject.directory,
|
||||
defines: _parseDefines(stringsArg('define')),
|
||||
inputs: _parseDefines(stringsArg('input')),
|
||||
cacheDir: globals.cache.getRoot(),
|
||||
@ -266,7 +268,7 @@ class AssembleCommand extends FlutterCommand {
|
||||
}
|
||||
|
||||
results[kDeferredComponents] = 'false';
|
||||
if (FlutterProject.current().manifest.deferredComponents != null && isDeferredComponentsTargets() && !isDebug()) {
|
||||
if (_flutterProject.manifest.deferredComponents != null && isDeferredComponentsTargets() && !isDebug()) {
|
||||
results[kDeferredComponents] = 'true';
|
||||
}
|
||||
if (argumentResults.wasParsed(FlutterOptions.kExtraFrontEndOptions)) {
|
||||
@ -297,7 +299,7 @@ class AssembleCommand extends FlutterCommand {
|
||||
"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')
|
||||
&& deferredTargets.isNotEmpty
|
||||
&& !isDebug()) {
|
||||
|
@ -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 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../android/android_builder.dart';
|
||||
import '../android/android_sdk.dart';
|
||||
import '../android/gradle_utils.dart';
|
||||
@ -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
|
||||
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'
|
||||
|
@ -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 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../android/android_builder.dart';
|
||||
import '../android/build_validation.dart';
|
||||
import '../android/gradle_utils.dart';
|
||||
@ -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
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
if (globals.androidSdk == null) {
|
||||
|
@ -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 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../android/android_builder.dart';
|
||||
import '../android/build_validation.dart';
|
||||
import '../android/deferred_components_prebuild_validator.dart';
|
||||
@ -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
|
||||
Future<FlutterCommandResult> runCommand() async {
|
||||
if (globals.androidSdk == null) {
|
||||
|
@ -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 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../base/common.dart';
|
||||
import '../build_info.dart';
|
||||
import '../bundle.dart';
|
||||
@ -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
|
||||
Future<void> validateCommand() async {
|
||||
if (boolArg('tree-shake-icons')) {
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../android/gradle_utils.dart' as gradle;
|
||||
import '../base/common.dart';
|
||||
@ -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.
|
||||
late final Net _net = Net(
|
||||
httpClientFactory: context.get<HttpClientFactory>(),
|
||||
|
@ -389,6 +389,24 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
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
|
||||
/// before pub get completes. This needs to be performed after dependency resolution.
|
||||
@override
|
||||
@ -405,7 +423,7 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
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 findPlugins(rootProject, throwOnError: false);
|
||||
final List<Plugin> plugins = await _pluginsFound;
|
||||
numberPlugins = plugins.length;
|
||||
} else {
|
||||
numberPlugins = 0;
|
||||
@ -414,7 +432,38 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
return CustomDimensions(
|
||||
commandPackagesNumberPlugins: numberPlugins,
|
||||
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 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart' as analytics;
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import '../android/android_device.dart';
|
||||
@ -447,6 +448,43 @@ class RunCommand extends RunCommandBase {
|
||||
|
||||
@override
|
||||
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;
|
||||
bool isEmulator;
|
||||
bool anyAndroidDevices = false;
|
||||
@ -512,19 +550,19 @@ class RunCommand extends RunCommandBase {
|
||||
|
||||
final BuildInfo buildInfo = await getBuildInfo();
|
||||
final String modeName = buildInfo.modeName;
|
||||
return CustomDimensions(
|
||||
commandRunIsEmulator: isEmulator,
|
||||
commandRunTargetName: deviceType,
|
||||
commandRunTargetOsVersion: deviceOsVersion,
|
||||
commandRunModeName: modeName,
|
||||
commandRunProjectModule: FlutterProject.current().isModule,
|
||||
commandRunProjectHostLanguage: hostLanguage.join(','),
|
||||
commandRunAndroidEmbeddingVersion: androidEmbeddingVersion,
|
||||
commandRunEnableImpeller: enableImpeller.asBool,
|
||||
commandRunIOSInterfaceType: iOSInterfaceType,
|
||||
commandRunIsTest: targetFile.endsWith('_test.dart'),
|
||||
return (
|
||||
runIsEmulator: isEmulator,
|
||||
runTargetName: deviceType,
|
||||
runTargetOsVersion: deviceOsVersion,
|
||||
runModeName: modeName,
|
||||
runProjectModule: FlutterProject.current().isModule,
|
||||
runProjectHostLanguage: hostLanguage.join(','),
|
||||
runAndroidEmbeddingVersion: androidEmbeddingVersion,
|
||||
runEnableImpeller: enableImpeller.asBool,
|
||||
runIOSInterfaceType: iOSInterfaceType,
|
||||
runIsTest: targetFile.endsWith('_test.dart'),
|
||||
);
|
||||
}
|
||||
})();
|
||||
|
||||
@override
|
||||
bool get shouldRunPub {
|
||||
@ -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> {
|
||||
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.
|
||||
///
|
||||
/// This can be overridden by some of its subclasses.
|
||||
@ -1359,6 +1362,14 @@ abstract class FlutterCommand extends Command<void> {
|
||||
/// Additional usage values to be sent with the usage ping.
|
||||
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.
|
||||
///
|
||||
/// Rather than overriding this method, subclasses should override
|
||||
@ -1621,7 +1632,7 @@ abstract class FlutterCommand extends Command<void> {
|
||||
commandPath: commandPath,
|
||||
result: commandResult.toString(),
|
||||
maxRss: maxRss,
|
||||
commandHasTerminal: globals.stdio.hasTerminal,
|
||||
commandHasTerminal: hasTerminal,
|
||||
));
|
||||
|
||||
// Send timing.
|
||||
@ -1748,9 +1759,17 @@ Run 'flutter -h' (or 'flutter <command> -h') for available flutter commands and
|
||||
setupApplicationPackages();
|
||||
|
||||
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(
|
||||
commandHasTerminal: globals.stdio.hasTerminal,
|
||||
).merge(await usageValues));
|
||||
commandHasTerminal: hasTerminal,
|
||||
).merge(pairOfUsageValues[0] as CustomDimensions));
|
||||
analytics.send(pairOfUsageValues[1] as Event);
|
||||
}
|
||||
|
||||
return runCommand();
|
||||
|
@ -13,6 +13,7 @@ import 'package:flutter_tools/src/commands/assemble.dart';
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -24,6 +25,14 @@ void main() {
|
||||
Cache.disableLocking();
|
||||
Cache.flutterRoot = '';
|
||||
final StackTrace stackTrace = StackTrace.current;
|
||||
late FakeAnalytics fakeAnalytics;
|
||||
|
||||
setUp(() {
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: MemoryFileSystem.test(),
|
||||
fakeFlutterVersion: FakeFlutterVersion(),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('flutter assemble can run a build', () async {
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand(
|
||||
@ -85,6 +94,31 @@ void main() {
|
||||
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 {
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(AssembleCommand(
|
||||
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
|
||||
|
@ -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/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/build.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -26,6 +27,7 @@ void main() {
|
||||
late FakeProcessManager processManager;
|
||||
late Platform platform;
|
||||
late Cache cache;
|
||||
late FakeAnalytics fakeAnalytics;
|
||||
|
||||
setUpAll(() {
|
||||
Cache.disableLocking();
|
||||
@ -44,6 +46,10 @@ void main() {
|
||||
logger: logger,
|
||||
processManager: processManager,
|
||||
);
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: fs,
|
||||
fakeFlutterVersion: FakeFlutterVersion(),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('will not build an AAR for a plugin', () async {
|
||||
@ -126,9 +132,19 @@ flutter:
|
||||
|
||||
await createTestCommandRunner(command).run(const <String>['build', 'aar', '--no-pub']);
|
||||
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>{
|
||||
FileSystem: () => fs,
|
||||
Platform: () => platform,
|
||||
ProcessManager: () => processManager,
|
||||
Analytics: () => fakeAnalytics,
|
||||
});
|
||||
}
|
||||
|
@ -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/web/compile.dart';
|
||||
import 'package:test/fake.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart' as analytics;
|
||||
import 'package:vm_service/vm_service.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -192,6 +193,7 @@ void main() {
|
||||
late Artifacts artifacts;
|
||||
late TestUsage usage;
|
||||
late FakeAnsiTerminal fakeTerminal;
|
||||
late analytics.FakeAnalytics fakeAnalytics;
|
||||
|
||||
setUpAll(() {
|
||||
Cache.disableLocking();
|
||||
@ -211,6 +213,10 @@ void main() {
|
||||
libDir.createSync();
|
||||
final File mainFile = libDir.childFile('main.dart');
|
||||
mainFile.writeAsStringSync('void main() {}');
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: fs,
|
||||
fakeFlutterVersion: FakeFlutterVersion(),
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('exits with a user message when no supported devices attached', () async {
|
||||
@ -478,6 +484,23 @@ void main() {
|
||||
'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>{
|
||||
AnsiTerminal: () => fakeTerminal,
|
||||
Artifacts: () => artifacts,
|
||||
@ -487,6 +510,7 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Stdio: () => FakeStdio(),
|
||||
Usage: () => usage,
|
||||
analytics.Analytics: () => fakeAnalytics,
|
||||
});
|
||||
|
||||
testUsingContext('correctly reports tests to usage', () async {
|
||||
@ -513,6 +537,23 @@ void main() {
|
||||
'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>{
|
||||
AnsiTerminal: () => fakeTerminal,
|
||||
Artifacts: () => artifacts,
|
||||
@ -522,6 +563,7 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.any(),
|
||||
Stdio: () => FakeStdio(),
|
||||
Usage: () => usage,
|
||||
analytics.Analytics: () => fakeAnalytics,
|
||||
});
|
||||
|
||||
group('--machine', () {
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
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_sdk.dart';
|
||||
import 'package:flutter_tools/src/android/android_studio.dart';
|
||||
@ -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/reporting/reporting.dart';
|
||||
import 'package:test/fake.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../../src/android_common.dart';
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fake_process_manager.dart';
|
||||
import '../../src/fakes.dart' show FakeFlutterVersion;
|
||||
import '../../src/test_flutter_command_runner.dart';
|
||||
|
||||
void main() {
|
||||
@ -29,10 +32,15 @@ void main() {
|
||||
group('Usage', () {
|
||||
late Directory tempDir;
|
||||
late TestUsage testUsage;
|
||||
late FakeAnalytics fakeAnalytics;
|
||||
|
||||
setUp(() {
|
||||
testUsage = TestUsage();
|
||||
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: MemoryFileSystem.test(),
|
||||
fakeFlutterVersion: FakeFlutterVersion(),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
@ -46,8 +54,21 @@ void main() {
|
||||
|
||||
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>{
|
||||
AndroidBuilder: () => FakeAndroidBuilder(),
|
||||
Analytics: () => fakeAnalytics,
|
||||
});
|
||||
|
||||
testUsingContext('split per abi', () async {
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
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_sdk.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
@ -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/reporting/reporting.dart';
|
||||
import 'package:test/fake.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../../src/android_common.dart';
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fakes.dart' show FakeFlutterVersion;
|
||||
import '../../src/test_flutter_command_runner.dart';
|
||||
|
||||
void main() {
|
||||
@ -25,10 +28,15 @@ void main() {
|
||||
group('Usage', () {
|
||||
late Directory tempDir;
|
||||
late TestUsage testUsage;
|
||||
late FakeAnalytics fakeAnalytics;
|
||||
|
||||
setUp(() {
|
||||
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
|
||||
testUsage = TestUsage();
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: MemoryFileSystem.test(),
|
||||
fakeFlutterVersion: FakeFlutterVersion(),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
@ -42,8 +50,18 @@ void main() {
|
||||
|
||||
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>{
|
||||
AndroidBuilder: () => FakeAndroidBuilder(),
|
||||
Analytics: () => fakeAnalytics,
|
||||
});
|
||||
|
||||
testUsingContext('build type', () async {
|
||||
|
@ -17,6 +17,7 @@ import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:test/fake.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -30,21 +31,26 @@ void main() {
|
||||
late FakeBundleBuilder fakeBundleBuilder;
|
||||
final FileSystemStyle fileSystemStyle = globals.fs.path.separator == '/' ?
|
||||
FileSystemStyle.posix : FileSystemStyle.windows;
|
||||
late FakeAnalytics fakeAnalytics;
|
||||
|
||||
MemoryFileSystem fsFactory() {
|
||||
return MemoryFileSystem.test(style: fileSystemStyle);
|
||||
}
|
||||
|
||||
setUp(() {
|
||||
tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.');
|
||||
|
||||
fakeBundleBuilder = FakeBundleBuilder();
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: fsFactory(),
|
||||
fakeFlutterVersion: FakeFlutterVersion(),
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
tryToDelete(tempDir);
|
||||
});
|
||||
|
||||
MemoryFileSystem fsFactory() {
|
||||
return MemoryFileSystem.test(style: fileSystemStyle);
|
||||
}
|
||||
|
||||
Future<BuildBundleCommand> runCommandIn(String projectPath, { List<String>? arguments }) async {
|
||||
final BuildBundleCommand command = BuildBundleCommand(
|
||||
logger: BufferLogger.test(),
|
||||
@ -67,6 +73,19 @@ void main() {
|
||||
final BuildBundleCommand command = await runCommandIn(projectPath);
|
||||
|
||||
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 {
|
||||
@ -76,6 +95,19 @@ void main() {
|
||||
final BuildBundleCommand command = await runCommandIn(projectPath);
|
||||
|
||||
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 {
|
||||
|
@ -7,6 +7,7 @@ import 'dart:convert';
|
||||
import 'dart:io' as io;
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/memory.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/java.dart';
|
||||
@ -30,6 +31,7 @@ import 'package:flutter_tools/src/version.dart';
|
||||
import 'package:process/process.dart';
|
||||
import 'package:pub_semver/pub_semver.dart';
|
||||
import 'package:pubspec_parse/pubspec_parse.dart';
|
||||
import 'package:unified_analytics/unified_analytics.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
@ -71,6 +73,7 @@ void main() {
|
||||
late FakeProcessManager fakeProcessManager;
|
||||
late BufferLogger logger;
|
||||
late FakeStdio mockStdio;
|
||||
late FakeAnalytics fakeAnalytics;
|
||||
|
||||
setUpAll(() async {
|
||||
Cache.disableLocking();
|
||||
@ -88,6 +91,10 @@ void main() {
|
||||
);
|
||||
fakeProcessManager = FakeProcessManager.empty();
|
||||
mockStdio = FakeStdio();
|
||||
fakeAnalytics = getInitializedFakeAnalyticsInstance(
|
||||
fs: MemoryFileSystem.test(),
|
||||
fakeFlutterVersion: fakeFlutterVersion,
|
||||
);
|
||||
});
|
||||
|
||||
tearDown(() {
|
||||
@ -171,10 +178,24 @@ void main() {
|
||||
],
|
||||
);
|
||||
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://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);
|
||||
}, overrides: <Type, Generator>{
|
||||
Pub: () => Pub.test(
|
||||
@ -187,6 +208,7 @@ void main() {
|
||||
stdio: mockStdio,
|
||||
),
|
||||
Logger: () => logger,
|
||||
Analytics: () => fakeAnalytics,
|
||||
});
|
||||
|
||||
testUsingContext('can create a skeleton (list/detail) app', () async {
|
||||
|
@ -355,6 +355,11 @@ flutter:
|
||||
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
|
||||
|
||||
expect((await getCommand.usageValues).commandPackagesNumberPlugins, 0);
|
||||
expect(
|
||||
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
|
||||
.eventData['packagesNumberPlugins'],
|
||||
0,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Stdio: () => mockStdio,
|
||||
Pub: () => Pub.test(
|
||||
@ -380,6 +385,11 @@ flutter:
|
||||
|
||||
// A plugin example depends on the plugin itself, and integration_test.
|
||||
expect((await getCommand.usageValues).commandPackagesNumberPlugins, 2);
|
||||
expect(
|
||||
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
|
||||
.eventData['packagesNumberPlugins'],
|
||||
2,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Stdio: () => mockStdio,
|
||||
Pub: () => Pub.test(
|
||||
@ -402,6 +412,11 @@ flutter:
|
||||
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
|
||||
|
||||
expect((await getCommand.usageValues).commandPackagesProjectModule, false);
|
||||
expect(
|
||||
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
|
||||
.eventData['packagesProjectModule'],
|
||||
false,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Stdio: () => mockStdio,
|
||||
Pub: () => Pub.test(
|
||||
@ -424,6 +439,11 @@ flutter:
|
||||
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
|
||||
|
||||
expect((await getCommand.usageValues).commandPackagesProjectModule, true);
|
||||
expect(
|
||||
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
|
||||
.eventData['packagesProjectModule'],
|
||||
true,
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Stdio: () => mockStdio,
|
||||
Pub: () => Pub.test(
|
||||
@ -455,6 +475,11 @@ flutter:
|
||||
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
|
||||
|
||||
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v1');
|
||||
expect(
|
||||
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
|
||||
.eventData['packagesAndroidEmbeddingVersion'],
|
||||
'v1',
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Stdio: () => mockStdio,
|
||||
Pub: () => Pub.test(
|
||||
@ -477,6 +502,11 @@ flutter:
|
||||
final PackagesGetCommand getCommand = command.subcommands['get']! as PackagesGetCommand;
|
||||
|
||||
expect((await getCommand.usageValues).commandPackagesAndroidEmbeddingVersion, 'v2');
|
||||
expect(
|
||||
(await getCommand.unifiedAnalyticsUsageValues('pub/get'))
|
||||
.eventData['packagesAndroidEmbeddingVersion'],
|
||||
'v2',
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Stdio: () => mockStdio,
|
||||
Pub: () => Pub.test(
|
||||
|
@ -373,7 +373,7 @@ bool analyticsTimingEventExists({
|
||||
};
|
||||
|
||||
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');
|
||||
|
||||
if (const DeepCollectionEquality().equals(lookup, eventData)) {
|
||||
|
Loading…
Reference in New Issue
Block a user