mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Fixes: - https://github.com/flutter/flutter/issues/141017
This commit is contained in:
parent
4ec0689fa1
commit
019fc5d65f
@ -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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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();
|
|
||||||
try {
|
|
||||||
return CustomDimensions(
|
|
||||||
commandBuildBundleTargetPlatform: _environment.defines[kTargetPlatform],
|
commandBuildBundleTargetPlatform: _environment.defines[kTargetPlatform],
|
||||||
commandBuildBundleIsModule: flutterProject.isModule,
|
commandBuildBundleIsModule: _flutterProject.isModule,
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<Event> unifiedAnalyticsUsageValues(String commandPath) async => Event.commandUsageValues(
|
||||||
|
workflow: commandPath,
|
||||||
|
commandHasTerminal: hasTerminal,
|
||||||
|
buildBundleTargetPlatform: _environment.defines[kTargetPlatform],
|
||||||
|
buildBundleIsModule: _flutterProject.isModule,
|
||||||
);
|
);
|
||||||
} on Exception {
|
|
||||||
// We've failed to send usage.
|
|
||||||
}
|
|
||||||
return const CustomDimensions();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
|
Future<Set<DevelopmentArtifact>> get requiredArtifacts async {
|
||||||
@ -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(),
|
||||||
@ -265,7 +267,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)) {
|
||||||
@ -296,7 +298,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 @@
|
|||||||
// 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';
|
||||||
@ -75,14 +77,15 @@ class BuildAarCommand extends BuildSubCommand {
|
|||||||
DevelopmentArtifact.androidGenSnapshot,
|
DevelopmentArtifact.androidGenSnapshot,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
late final FlutterProject project = _getProject();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<CustomDimensions> get usageValues async {
|
Future<CustomDimensions> get usageValues async {
|
||||||
final FlutterProject flutterProject = _getProject();
|
|
||||||
|
|
||||||
String projectType;
|
String projectType;
|
||||||
if (flutterProject.manifest.isModule) {
|
if (project.manifest.isModule) {
|
||||||
projectType = 'module';
|
projectType = 'module';
|
||||||
} else if (flutterProject.manifest.isPlugin) {
|
} else if (project.manifest.isPlugin) {
|
||||||
projectType = 'plugin';
|
projectType = 'plugin';
|
||||||
} else {
|
} else {
|
||||||
projectType = 'app';
|
projectType = 'app';
|
||||||
@ -94,6 +97,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 @@
|
|||||||
// 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 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 @@
|
|||||||
// 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 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 @@
|
|||||||
// 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 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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 @@
|
|||||||
// 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 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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>(),
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:args/args.dart';
|
import 'package:args/args.dart';
|
||||||
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
import '../base/os.dart';
|
import '../base/os.dart';
|
||||||
@ -372,6 +373,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
|
||||||
@ -388,7 +407,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;
|
||||||
@ -397,7 +416,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,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,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';
|
||||||
@ -431,6 +432,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;
|
||||||
@ -496,19 +534,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 {
|
||||||
@ -783,3 +821,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,
|
||||||
|
});
|
||||||
|
@ -61,6 +61,7 @@ import 'persistent_tool_state.dart';
|
|||||||
import 'reporting/crash_reporting.dart';
|
import 'reporting/crash_reporting.dart';
|
||||||
import 'reporting/first_run.dart';
|
import 'reporting/first_run.dart';
|
||||||
import 'reporting/reporting.dart';
|
import 'reporting/reporting.dart';
|
||||||
|
import 'reporting/unified_analytics.dart';
|
||||||
import 'resident_runner.dart';
|
import 'resident_runner.dart';
|
||||||
import 'run_hot.dart';
|
import 'run_hot.dart';
|
||||||
import 'runner/local_engine.dart';
|
import 'runner/local_engine.dart';
|
||||||
@ -88,11 +89,10 @@ Future<T> runInContext<T>(
|
|||||||
body: runnerWrapper,
|
body: runnerWrapper,
|
||||||
overrides: overrides,
|
overrides: overrides,
|
||||||
fallbacks: <Type, Generator>{
|
fallbacks: <Type, Generator>{
|
||||||
Analytics: () => Analytics(
|
Analytics: () => getAnalytics(
|
||||||
tool: DashTool.flutterTool,
|
runningOnBot: runningOnBot,
|
||||||
flutterChannel: globals.flutterVersion.channel,
|
flutterVersion: globals.flutterVersion,
|
||||||
flutterVersion: globals.flutterVersion.frameworkVersion,
|
environment: globals.platform.environment,
|
||||||
dartVersion: globals.flutterVersion.dartSdkVersion,
|
|
||||||
),
|
),
|
||||||
AndroidBuilder: () => AndroidGradleBuilder(
|
AndroidBuilder: () => AndroidGradleBuilder(
|
||||||
java: globals.java,
|
java: globals.java,
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
|
import '../version.dart';
|
||||||
|
|
||||||
|
/// This function is called from within the context runner to perform
|
||||||
|
/// checks that are necessary for determining if a no-op version of
|
||||||
|
/// [Analytics] gets returned.
|
||||||
|
///
|
||||||
|
/// When [enableAsserts] is set to `true`, various assert statements
|
||||||
|
/// will be enabled to ensure usage of this class is within GA4 limitations.
|
||||||
|
///
|
||||||
|
/// For testing purposes, pass in a [FakeAnalytics] instance initialized with
|
||||||
|
/// an in-memory [FileSystem] to prevent writing to disk.
|
||||||
|
Analytics getAnalytics({
|
||||||
|
required bool runningOnBot,
|
||||||
|
required FlutterVersion flutterVersion,
|
||||||
|
required Map<String, String> environment,
|
||||||
|
bool enableAsserts = false,
|
||||||
|
FakeAnalytics? analyticsOverride,
|
||||||
|
}) {
|
||||||
|
final String version = flutterVersion.getVersionString(redactUnknownBranches: true);
|
||||||
|
final bool suppressEnvFlag = environment['FLUTTER_SUPPRESS_ANALYTICS']?.toLowerCase() == 'true';
|
||||||
|
|
||||||
|
if (// Ignore local user branches.
|
||||||
|
version.startsWith('[user-branch]') ||
|
||||||
|
// Many CI systems don't do a full git checkout.
|
||||||
|
version.endsWith('/unknown') ||
|
||||||
|
// Ignore bots.
|
||||||
|
runningOnBot ||
|
||||||
|
// Ignore when suppressed by FLUTTER_SUPPRESS_ANALYTICS.
|
||||||
|
suppressEnvFlag) {
|
||||||
|
return NoOpAnalytics();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providing an override of the [Analytics] instance is preferred when
|
||||||
|
// running tests for this function to prevent writing to the filesystem
|
||||||
|
if (analyticsOverride != null) {
|
||||||
|
return analyticsOverride;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Analytics(
|
||||||
|
tool: DashTool.flutterTool,
|
||||||
|
flutterChannel: flutterVersion.channel,
|
||||||
|
flutterVersion: flutterVersion.frameworkVersion,
|
||||||
|
dartVersion: flutterVersion.dartSdkVersion,
|
||||||
|
enableAsserts: enableAsserts,
|
||||||
|
);
|
||||||
|
}
|
@ -7,6 +7,7 @@ import 'package:args/command_runner.dart';
|
|||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:package_config/package_config_types.dart';
|
import 'package:package_config/package_config_types.dart';
|
||||||
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
import '../application_package.dart';
|
import '../application_package.dart';
|
||||||
import '../base/common.dart';
|
import '../base/common.dart';
|
||||||
@ -213,6 +214,11 @@ abstract class FlutterCommand extends Command<void> {
|
|||||||
bool _excludeDebug = false;
|
bool _excludeDebug = false;
|
||||||
bool _excludeRelease = false;
|
bool _excludeRelease = false;
|
||||||
|
|
||||||
|
/// Grabs the [Analytics] instance from the global context. It is defined
|
||||||
|
/// at the [FlutterCommand] level to enable any classes that extend it to
|
||||||
|
/// easily reference it or overwrite as necessary.
|
||||||
|
Analytics get analytics => globals.analytics;
|
||||||
|
|
||||||
void requiresPubspecYaml() {
|
void requiresPubspecYaml() {
|
||||||
_requiresPubspecYaml = true;
|
_requiresPubspecYaml = true;
|
||||||
}
|
}
|
||||||
@ -327,6 +333,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.
|
||||||
@ -1321,6 +1330,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
|
||||||
@ -1654,9 +1671,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();
|
||||||
|
@ -48,7 +48,7 @@ dependencies:
|
|||||||
http_multi_server: 3.2.1
|
http_multi_server: 3.2.1
|
||||||
convert: 3.1.1
|
convert: 3.1.1
|
||||||
async: 2.11.0
|
async: 2.11.0
|
||||||
unified_analytics: 4.0.0
|
unified_analytics: 5.8.0
|
||||||
|
|
||||||
cli_config: 0.1.1
|
cli_config: 0.1.1
|
||||||
graphs: 2.3.1
|
graphs: 2.3.1
|
||||||
@ -112,4 +112,4 @@ dartdoc:
|
|||||||
# Exclude this package from the hosted API docs.
|
# Exclude this package from the hosted API docs.
|
||||||
nodoc: true
|
nodoc: true
|
||||||
|
|
||||||
# PUBSPEC CHECKSUM: 05d8
|
# PUBSPEC CHECKSUM: e7e1
|
||||||
|
@ -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() {
|
|||||||
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() {
|
|||||||
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)),
|
||||||
|
@ -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() {
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
'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() {
|
|||||||
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() {
|
|||||||
'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() {
|
|||||||
ProcessManager: () => FakeProcessManager.any(),
|
ProcessManager: () => FakeProcessManager.any(),
|
||||||
Stdio: () => FakeStdio(),
|
Stdio: () => FakeStdio(),
|
||||||
Usage: () => usage,
|
Usage: () => usage,
|
||||||
|
analytics.Analytics: () => fakeAnalytics,
|
||||||
});
|
});
|
||||||
|
|
||||||
group('--machine', () {
|
group('--machine', () {
|
||||||
|
@ -69,17 +69,6 @@ void main() {
|
|||||||
AndroidBuilder: () => FakeAndroidBuilder(),
|
AndroidBuilder: () => FakeAndroidBuilder(),
|
||||||
});
|
});
|
||||||
|
|
||||||
testUsingContext('indicate that project is a plugin', () async {
|
|
||||||
final String projectPath = await createProject(tempDir,
|
|
||||||
arguments: <String>['--no-pub', '--template=plugin', '--project-name=aar_test']);
|
|
||||||
|
|
||||||
final BuildAarCommand command = await runCommandIn(projectPath);
|
|
||||||
expect((await command.usageValues).commandBuildAarProjectType, 'plugin');
|
|
||||||
|
|
||||||
}, overrides: <Type, Generator>{
|
|
||||||
AndroidBuilder: () => FakeAndroidBuilder(),
|
|
||||||
});
|
|
||||||
|
|
||||||
testUsingContext('indicate the target platform', () async {
|
testUsingContext('indicate the target platform', () async {
|
||||||
final String projectPath = await createProject(tempDir,
|
final String projectPath = await createProject(tempDir,
|
||||||
arguments: <String>['--no-pub', '--template=module']);
|
arguments: <String>['--no-pub', '--template=module']);
|
||||||
@ -128,7 +117,7 @@ void main() {
|
|||||||
|
|
||||||
testUsingContext('defaults', () async {
|
testUsingContext('defaults', () async {
|
||||||
final String projectPath = await createProject(tempDir,
|
final String projectPath = await createProject(tempDir,
|
||||||
arguments: <String>['--no-pub']);
|
arguments: <String>['--no-pub', '--template=module']);
|
||||||
await runCommandIn(projectPath);
|
await runCommandIn(projectPath);
|
||||||
|
|
||||||
expect(fakeAndroidBuilder.buildNumber, '1.0');
|
expect(fakeAndroidBuilder.buildNumber, '1.0');
|
||||||
@ -158,7 +147,7 @@ void main() {
|
|||||||
|
|
||||||
testUsingContext('parses flags', () async {
|
testUsingContext('parses flags', () async {
|
||||||
final String projectPath = await createProject(tempDir,
|
final String projectPath = await createProject(tempDir,
|
||||||
arguments: <String>['--no-pub']);
|
arguments: <String>['--no-pub', '--template=module']);
|
||||||
await runCommandIn(
|
await runCommandIn(
|
||||||
projectPath,
|
projectPath,
|
||||||
arguments: <String>[
|
arguments: <String>[
|
||||||
|
@ -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;
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
|
|
||||||
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 @@
|
|||||||
// 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;
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
|
|
||||||
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 {
|
||||||
|
@ -20,6 +20,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';
|
||||||
@ -33,21 +34,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(),
|
||||||
@ -70,6 +76,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 {
|
||||||
@ -79,6 +98,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';
|
|||||||
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';
|
|||||||
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() {
|
|||||||
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() {
|
|||||||
);
|
);
|
||||||
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() {
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
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() {
|
|||||||
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 {
|
||||||
|
@ -339,6 +339,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(
|
||||||
@ -364,6 +369,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(
|
||||||
@ -386,6 +396,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(
|
||||||
@ -408,6 +423,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(
|
||||||
@ -439,6 +459,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(
|
||||||
@ -461,6 +486,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(
|
||||||
|
@ -0,0 +1,139 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:file/memory.dart';
|
||||||
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
|
import 'package:flutter_tools/src/reporting/unified_analytics.dart';
|
||||||
|
import 'package:unified_analytics/src/enums.dart';
|
||||||
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
|
import '../src/common.dart';
|
||||||
|
import '../src/fakes.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
const String userBranch = 'abc123';
|
||||||
|
const String homeDirectoryName = 'home';
|
||||||
|
const DashTool tool = DashTool.flutterTool;
|
||||||
|
|
||||||
|
late FileSystem fs;
|
||||||
|
late Directory home;
|
||||||
|
late FakeAnalytics analyticsOverride;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
fs = MemoryFileSystem.test();
|
||||||
|
home = fs.directory(homeDirectoryName);
|
||||||
|
|
||||||
|
// Prepare the tests by "onboarding" the tool into the package
|
||||||
|
// by invoking the [clientShowedMessage] method for the provided
|
||||||
|
// [tool]
|
||||||
|
final FakeAnalytics initialAnalytics = FakeAnalytics(
|
||||||
|
tool: tool,
|
||||||
|
homeDirectory: home,
|
||||||
|
dartVersion: '3.0.0',
|
||||||
|
platform: DevicePlatform.macos,
|
||||||
|
fs: fs,
|
||||||
|
surveyHandler: SurveyHandler(
|
||||||
|
homeDirectory: home,
|
||||||
|
fs: fs,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
initialAnalytics.clientShowedMessage();
|
||||||
|
|
||||||
|
analyticsOverride = FakeAnalytics(
|
||||||
|
tool: tool,
|
||||||
|
homeDirectory: home,
|
||||||
|
dartVersion: '3.0.0',
|
||||||
|
platform: DevicePlatform.macos,
|
||||||
|
fs: fs,
|
||||||
|
surveyHandler: SurveyHandler(
|
||||||
|
homeDirectory: home,
|
||||||
|
fs: fs,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('Unit testing getAnalytics', () {
|
||||||
|
testWithoutContext('Successfully creates the instance for standard branch', () {
|
||||||
|
final Analytics analytics = getAnalytics(
|
||||||
|
runningOnBot: false,
|
||||||
|
flutterVersion: FakeFlutterVersion(),
|
||||||
|
environment: const <String, String>{},
|
||||||
|
analyticsOverride: analyticsOverride,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(analytics.clientId, isNot(NoOpAnalytics.staticClientId),
|
||||||
|
reason: 'The CLIENT ID should be a randomly generated id');
|
||||||
|
expect(analytics, isNot(isA<NoOpAnalytics>()));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('NoOp instance for user branch', () {
|
||||||
|
final Analytics analytics = getAnalytics(
|
||||||
|
runningOnBot: false,
|
||||||
|
flutterVersion: FakeFlutterVersion(
|
||||||
|
branch: userBranch,
|
||||||
|
frameworkRevision: '3.14.0-14.0.pre.370',
|
||||||
|
),
|
||||||
|
environment: const <String, String>{},
|
||||||
|
analyticsOverride: analyticsOverride,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
analytics.clientId,
|
||||||
|
NoOpAnalytics.staticClientId,
|
||||||
|
reason: 'The client ID should match the NoOp client id',
|
||||||
|
);
|
||||||
|
expect(analytics, isA<NoOpAnalytics>());
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('NoOp instance for unknown branch', () {
|
||||||
|
final Analytics analytics = getAnalytics(
|
||||||
|
runningOnBot: false,
|
||||||
|
flutterVersion: FakeFlutterVersion(
|
||||||
|
frameworkRevision: 'unknown',
|
||||||
|
),
|
||||||
|
environment: const <String, String>{},
|
||||||
|
analyticsOverride: analyticsOverride,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
analytics.clientId,
|
||||||
|
NoOpAnalytics.staticClientId,
|
||||||
|
reason: 'The client ID should match the NoOp client id',
|
||||||
|
);
|
||||||
|
expect(analytics, isA<NoOpAnalytics>());
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('NoOp instance when running on bots', () {
|
||||||
|
final Analytics analytics = getAnalytics(
|
||||||
|
runningOnBot: true,
|
||||||
|
flutterVersion: FakeFlutterVersion(),
|
||||||
|
environment: const <String, String>{},
|
||||||
|
analyticsOverride: analyticsOverride,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
analytics.clientId,
|
||||||
|
NoOpAnalytics.staticClientId,
|
||||||
|
reason: 'The client ID should match the NoOp client id',
|
||||||
|
);
|
||||||
|
expect(analytics, isA<NoOpAnalytics>());
|
||||||
|
});
|
||||||
|
|
||||||
|
testWithoutContext('NoOp instance when suppressing via env variable', () {
|
||||||
|
final Analytics analytics = getAnalytics(
|
||||||
|
runningOnBot: true,
|
||||||
|
flutterVersion: FakeFlutterVersion(),
|
||||||
|
environment: const <String, String>{'FLUTTER_SUPPRESS_ANALYTICS': 'true'},
|
||||||
|
analyticsOverride: analyticsOverride,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
analytics.clientId,
|
||||||
|
NoOpAnalytics.staticClientId,
|
||||||
|
reason: 'The client ID should match the NoOp client id',
|
||||||
|
);
|
||||||
|
expect(analytics, isA<NoOpAnalytics>());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
@ -5,6 +5,7 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:args/command_runner.dart';
|
import 'package:args/command_runner.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:file/memory.dart';
|
import 'package:file/memory.dart';
|
||||||
import 'package:flutter_tools/src/base/common.dart';
|
import 'package:flutter_tools/src/base/common.dart';
|
||||||
import 'package:flutter_tools/src/base/context.dart';
|
import 'package:flutter_tools/src/base/context.dart';
|
||||||
@ -16,6 +17,10 @@ import 'package:meta/meta.dart';
|
|||||||
import 'package:path/path.dart' as path; // flutter_ignore: package_path_import
|
import 'package:path/path.dart' as path; // flutter_ignore: package_path_import
|
||||||
import 'package:test/test.dart' as test_package show test;
|
import 'package:test/test.dart' as test_package show test;
|
||||||
import 'package:test/test.dart' hide test;
|
import 'package:test/test.dart' hide test;
|
||||||
|
import 'package:unified_analytics/src/enums.dart';
|
||||||
|
import 'package:unified_analytics/unified_analytics.dart';
|
||||||
|
|
||||||
|
import 'fakes.dart';
|
||||||
|
|
||||||
export 'package:path/path.dart' show Context; // flutter_ignore: package_path_import
|
export 'package:path/path.dart' show Context; // flutter_ignore: package_path_import
|
||||||
export 'package:test/test.dart' hide isInstanceOf, test;
|
export 'package:test/test.dart' hide isInstanceOf, test;
|
||||||
@ -305,3 +310,75 @@ class FileExceptionHandler {
|
|||||||
throw exception;
|
throw exception;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This method is required to fetch an instance of [FakeAnalytics]
|
||||||
|
/// because there is initialization logic that is required. An initial
|
||||||
|
/// instance will first be created and will let package:unified_analytics
|
||||||
|
/// know that the consent message has been shown. After confirming on the first
|
||||||
|
/// instance, then a second instance will be generated and returned. This second
|
||||||
|
/// instance will be cleared to send events.
|
||||||
|
FakeAnalytics getInitializedFakeAnalyticsInstance({
|
||||||
|
required FileSystem fs,
|
||||||
|
required FakeFlutterVersion fakeFlutterVersion,
|
||||||
|
String? clientIde,
|
||||||
|
}) {
|
||||||
|
final Directory homeDirectory = fs.directory('/');
|
||||||
|
final FakeAnalytics initialAnalytics = FakeAnalytics(
|
||||||
|
tool: DashTool.flutterTool,
|
||||||
|
homeDirectory: homeDirectory,
|
||||||
|
dartVersion: fakeFlutterVersion.dartSdkVersion,
|
||||||
|
platform: DevicePlatform.linux,
|
||||||
|
fs: fs,
|
||||||
|
surveyHandler: SurveyHandler(homeDirectory: homeDirectory, fs: fs),
|
||||||
|
flutterChannel: fakeFlutterVersion.channel,
|
||||||
|
flutterVersion: fakeFlutterVersion.getVersionString(),
|
||||||
|
);
|
||||||
|
initialAnalytics.clientShowedMessage();
|
||||||
|
|
||||||
|
return FakeAnalytics(
|
||||||
|
tool: DashTool.flutterTool,
|
||||||
|
homeDirectory: homeDirectory,
|
||||||
|
dartVersion: fakeFlutterVersion.dartSdkVersion,
|
||||||
|
platform: DevicePlatform.linux,
|
||||||
|
fs: fs,
|
||||||
|
surveyHandler: SurveyHandler(homeDirectory: homeDirectory, fs: fs),
|
||||||
|
flutterChannel: fakeFlutterVersion.channel,
|
||||||
|
flutterVersion: fakeFlutterVersion.getVersionString(),
|
||||||
|
clientIde: clientIde,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns "true" if the timing event searched for exists in [sentEvents].
|
||||||
|
///
|
||||||
|
/// This utility function allows us to check for an instance of
|
||||||
|
/// [Event.timing] within a [FakeAnalytics] instance. Normally, we can
|
||||||
|
/// use the equality operator for [Event] to check if the event exists, but
|
||||||
|
/// we are unable to do so for the timing event because the elapsed time
|
||||||
|
/// is variable so we cannot predict what that value will be in tests.
|
||||||
|
///
|
||||||
|
/// This function allows us to check for the other keys that have
|
||||||
|
/// string values by removing the `elapsedMilliseconds` from the
|
||||||
|
/// [Event.eventData] map and checking for a match.
|
||||||
|
bool analyticsTimingEventExists({
|
||||||
|
required List<Event> sentEvents,
|
||||||
|
required String workflow,
|
||||||
|
required String variableName,
|
||||||
|
String? label,
|
||||||
|
}) {
|
||||||
|
final Map<String, String> lookup = <String, String>{
|
||||||
|
'workflow': workflow,
|
||||||
|
'variableName': variableName,
|
||||||
|
if (label != null) 'label': label,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (final Event e in sentEvents) {
|
||||||
|
final Map<String, Object?> eventData = <String, Object?>{...e.eventData};
|
||||||
|
eventData.remove('elapsedMilliseconds');
|
||||||
|
|
||||||
|
if (const DeepCollectionEquality().equals(lookup, eventData)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -35,6 +35,7 @@ import 'package:flutter_tools/src/reporting/reporting.dart';
|
|||||||
import 'package:flutter_tools/src/version.dart';
|
import 'package:flutter_tools/src/version.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 'common.dart';
|
import 'common.dart';
|
||||||
import 'fake_http_client.dart';
|
import 'fake_http_client.dart';
|
||||||
@ -120,6 +121,7 @@ void testUsingContext(
|
|||||||
Pub: () => ThrowingPub(), // prevent accidentally using pub.
|
Pub: () => ThrowingPub(), // prevent accidentally using pub.
|
||||||
CrashReporter: () => const NoopCrashReporter(),
|
CrashReporter: () => const NoopCrashReporter(),
|
||||||
TemplateRenderer: () => const MustacheTemplateRenderer(),
|
TemplateRenderer: () => const MustacheTemplateRenderer(),
|
||||||
|
Analytics: () => NoOpAnalytics(),
|
||||||
},
|
},
|
||||||
body: () {
|
body: () {
|
||||||
return runZonedGuarded<Future<dynamic>>(() {
|
return runZonedGuarded<Future<dynamic>>(() {
|
||||||
|
@ -438,7 +438,7 @@ class FakeFlutterVersion implements FlutterVersion {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
String getVersionString({bool redactUnknownBranches = false}) {
|
String getVersionString({bool redactUnknownBranches = false}) {
|
||||||
return 'v0.0.0';
|
return '${getBranchName(redactUnknownBranches: redactUnknownBranches)}/$frameworkRevision';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
Loading…
Reference in New Issue
Block a user