[flutter_tool] Clean up usage events and custom dimensions (#36785)
This commit is contained in:
parent
24f483cf25
commit
ef146f63bb
@ -15,8 +15,7 @@ import 'package:flutter_tools/src/context_runner.dart';
|
||||
import 'package:flutter_tools/src/devfs.dart';
|
||||
import 'package:flutter_tools/src/bundle.dart';
|
||||
import 'package:flutter_tools/src/globals.dart';
|
||||
import 'package:flutter_tools/src/reporting/disabled_usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
|
||||
const String _kOptionPackages = 'packages';
|
||||
const String _kOptionAsset = 'asset-dir';
|
||||
|
@ -18,8 +18,7 @@ import 'package:flutter_tools/src/dart/package_map.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/globals.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/disabled_usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/test/coverage_collector.dart';
|
||||
import 'package:flutter_tools/src/test/runner.dart';
|
||||
|
||||
|
@ -19,8 +19,7 @@ import 'src/base/utils.dart';
|
||||
import 'src/context_runner.dart';
|
||||
import 'src/doctor.dart';
|
||||
import 'src/globals.dart';
|
||||
import 'src/reporting/crash_reporting.dart';
|
||||
import 'src/reporting/usage.dart';
|
||||
import 'src/reporting/reporting.dart';
|
||||
import 'src/runner/flutter_command.dart';
|
||||
import 'src/runner/flutter_command_runner.dart';
|
||||
import 'src/version.dart';
|
||||
|
@ -24,8 +24,7 @@ import '../features.dart';
|
||||
import '../flutter_manifest.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import 'android_sdk.dart';
|
||||
import 'android_studio.dart';
|
||||
|
||||
@ -747,11 +746,7 @@ Future<void> _buildGradleProjectV2(
|
||||
printError('The Gradle failure may have been because of AndroidX incompatibilities in this Flutter app.');
|
||||
printError('See https://goo.gl/CP92wY for more information on the problem and how to fix it.');
|
||||
printError('*******************************************************************************************');
|
||||
String commandName = '';
|
||||
if (FlutterCommand.current != null) {
|
||||
commandName = '-${FlutterCommand.current.name}';
|
||||
}
|
||||
flutterUsage.sendEvent('build$commandName', 'android-x-failure');
|
||||
BuildEvent('android-x-failure').send();
|
||||
}
|
||||
throwToolExit('Gradle task $assembleTask failed with exit code $exitCode', exitCode: exitCode);
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import '../dart/package_map.dart';
|
||||
import '../globals.dart';
|
||||
import '../macos/xcode.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
import 'context.dart';
|
||||
import 'file_system.dart';
|
||||
|
@ -9,7 +9,7 @@ import '../base/context.dart';
|
||||
import '../base/os.dart';
|
||||
import '../build_info.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
|
||||
import 'build.dart';
|
||||
|
||||
@ -38,20 +38,20 @@ class BuildAarCommand extends BuildSubCommand {
|
||||
final String name = 'aar';
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> get usageValues async {
|
||||
final Map<String, String> usage = <String, String>{};
|
||||
Future<Map<CustomDimensions, String>> get usageValues async {
|
||||
final Map<CustomDimensions, String> usage = <CustomDimensions, String>{};
|
||||
final FlutterProject futterProject = _getProject();
|
||||
if (futterProject == null) {
|
||||
return usage;
|
||||
}
|
||||
if (futterProject.manifest.isModule) {
|
||||
usage[kCommandBuildAarProjectType] = 'module';
|
||||
usage[CustomDimensions.commandBuildAarProjectType] = 'module';
|
||||
} else if (futterProject.manifest.isPlugin) {
|
||||
usage[kCommandBuildAarProjectType] = 'plugin';
|
||||
usage[CustomDimensions.commandBuildAarProjectType] = 'plugin';
|
||||
} else {
|
||||
usage[kCommandBuildAarProjectType] = 'app';
|
||||
usage[CustomDimensions.commandBuildAarProjectType] = 'app';
|
||||
}
|
||||
usage[kCommandBuildAarTargetPlatform] =
|
||||
usage[CustomDimensions.commandBuildAarTargetPlatform] =
|
||||
(argResults['target-platform'] as List<String>).join(',');
|
||||
return usage;
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ import '../build_info.dart';
|
||||
import '../bundle.dart';
|
||||
import '../features.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart' show FlutterOptions, FlutterCommandResult;
|
||||
import 'build.dart';
|
||||
|
||||
@ -74,17 +74,15 @@ class BuildBundleCommand extends BuildSubCommand {
|
||||
' iOS runtimes.';
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> get usageValues async {
|
||||
Future<Map<CustomDimensions, String>> get usageValues async {
|
||||
final String projectDir = fs.file(targetFile).parent.parent.path;
|
||||
final FlutterProject futterProject = FlutterProject.fromPath(projectDir);
|
||||
|
||||
if (futterProject == null) {
|
||||
return const <String, String>{};
|
||||
return const <CustomDimensions, String>{};
|
||||
}
|
||||
|
||||
return <String, String>{
|
||||
kCommandBuildBundleTargetPlatform: argResults['target-platform'],
|
||||
kCommandBuildBundleIsModule: '${futterProject.isModule}'
|
||||
return <CustomDimensions, String>{
|
||||
CustomDimensions.commandBuildBundleTargetPlatform: argResults['target-platform'],
|
||||
CustomDimensions.commandBuildBundleIsModule: '${futterProject.isModule}'
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ import '../base/file_system.dart';
|
||||
import '../convert.dart';
|
||||
import '../features.dart';
|
||||
import '../globals.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../version.dart';
|
||||
|
||||
|
@ -23,7 +23,7 @@ import '../doctor.dart';
|
||||
import '../features.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../template.dart';
|
||||
import '../version.dart';
|
||||
@ -165,11 +165,11 @@ class CreateCommand extends FlutterCommand {
|
||||
String get invocation => '${runner.executableName} $name <output directory>';
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> get usageValues async {
|
||||
return <String, String>{
|
||||
kCommandCreateProjectType: argResults['template'],
|
||||
kCommandCreateAndroidLanguage: argResults['android-language'],
|
||||
kCommandCreateIosLanguage: argResults['ios-language'],
|
||||
Future<Map<CustomDimensions, String>> get usageValues async {
|
||||
return <CustomDimensions, String>{
|
||||
CustomDimensions.commandCreateProjectType: argResults['template'],
|
||||
CustomDimensions.commandCreateAndroidLanguage: argResults['android-language'],
|
||||
CustomDimensions.commandCreateIosLanguage: argResults['ios-language'],
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ import '../base/os.dart';
|
||||
import '../cache.dart';
|
||||
import '../dart/pub.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
|
||||
class PackagesCommand extends FlutterCommand {
|
||||
@ -71,8 +71,8 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> get usageValues async {
|
||||
final Map<String, String> usageValues = <String, String>{};
|
||||
Future<Map<CustomDimensions, String>> get usageValues async {
|
||||
final Map<CustomDimensions, String> usageValues = <CustomDimensions, String>{};
|
||||
final String workingDirectory = argResults.rest.length == 1 ? argResults.rest[0] : null;
|
||||
final String target = findProjectRoot(workingDirectory);
|
||||
if (target == null) {
|
||||
@ -82,11 +82,11 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
final bool hasPlugins = await rootProject.flutterPluginsFile.exists();
|
||||
if (hasPlugins) {
|
||||
final int numberOfPlugins = (await rootProject.flutterPluginsFile.readAsLines()).length;
|
||||
usageValues[kCommandPackagesNumberPlugins] = '$numberOfPlugins';
|
||||
usageValues[CustomDimensions.commandPackagesNumberPlugins] = '$numberOfPlugins';
|
||||
} else {
|
||||
usageValues[kCommandPackagesNumberPlugins] = '0';
|
||||
usageValues[CustomDimensions.commandPackagesNumberPlugins] = '0';
|
||||
}
|
||||
usageValues[kCommandPackagesProjectModule] = '${rootProject.isModule}';
|
||||
usageValues[CustomDimensions.commandPackagesProjectModule] = '${rootProject.isModule}';
|
||||
return usageValues;
|
||||
}
|
||||
|
||||
@ -100,11 +100,11 @@ class PackagesGetCommand extends FlutterCommand {
|
||||
checkLastModified: false,
|
||||
);
|
||||
pubGetTimer.stop();
|
||||
flutterUsage.sendEvent('packages-pub-get', 'success');
|
||||
PubGetEvent(success: true).send();
|
||||
flutterUsage.sendTiming('packages-pub-get', 'success', pubGetTimer.elapsed);
|
||||
} catch (_) {
|
||||
pubGetTimer.stop();
|
||||
flutterUsage.sendEvent('packages-pub-get', 'failure');
|
||||
PubGetEvent(success: false).send();
|
||||
flutterUsage.sendTiming('packages-pub-get', 'failure', pubGetTimer.elapsed);
|
||||
rethrow;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import '../features.dart';
|
||||
import '../globals.dart';
|
||||
import '../macos/xcode.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../resident_runner.dart';
|
||||
import '../resident_web_runner.dart';
|
||||
import '../run_cold.dart';
|
||||
@ -198,7 +198,7 @@ class RunCommand extends RunCommandBase {
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, String>> get usageValues async {
|
||||
Future<Map<CustomDimensions, String>> get usageValues async {
|
||||
String deviceType, deviceOsVersion;
|
||||
bool isEmulator;
|
||||
|
||||
@ -227,13 +227,13 @@ class RunCommand extends RunCommandBase {
|
||||
hostLanguage.add(iosProject.isSwift ? 'swift' : 'objc');
|
||||
}
|
||||
|
||||
return <String, String>{
|
||||
kCommandRunIsEmulator: '$isEmulator',
|
||||
kCommandRunTargetName: deviceType,
|
||||
kCommandRunTargetOsVersion: deviceOsVersion,
|
||||
kCommandRunModeName: modeName,
|
||||
kCommandRunProjectModule: '${FlutterProject.current().isModule}',
|
||||
kCommandRunProjectHostLanguage: hostLanguage.join(','),
|
||||
return <CustomDimensions, String>{
|
||||
CustomDimensions.commandRunIsEmulator: '$isEmulator',
|
||||
CustomDimensions.commandRunTargetName: deviceType,
|
||||
CustomDimensions.commandRunTargetOsVersion: deviceOsVersion,
|
||||
CustomDimensions.commandRunModeName: modeName,
|
||||
CustomDimensions.commandRunProjectModule: '${FlutterProject.current().isModule}',
|
||||
CustomDimensions.commandRunProjectHostLanguage: hostLanguage.join(','),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,7 @@ import 'macos/cocoapods_validator.dart';
|
||||
import 'macos/macos_workflow.dart';
|
||||
import 'macos/xcode.dart';
|
||||
import 'macos/xcode_validator.dart';
|
||||
import 'reporting/usage.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
import 'run_hot.dart';
|
||||
import 'version.dart';
|
||||
import 'web/chrome.dart';
|
||||
|
@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:json_rpc_2/json_rpc_2.dart' as rpc;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
|
@ -31,7 +31,7 @@ import 'macos/cocoapods_validator.dart';
|
||||
import 'macos/macos_workflow.dart';
|
||||
import 'macos/xcode_validator.dart';
|
||||
import 'proxy_validator.dart';
|
||||
import 'reporting/usage.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
import 'tester/flutter_tester.dart';
|
||||
import 'version.dart';
|
||||
import 'vscode/vscode_validator.dart';
|
||||
@ -236,7 +236,7 @@ class Doctor {
|
||||
break;
|
||||
}
|
||||
|
||||
flutterUsage.sendEvent('doctorResult.${validator.runtimeType.toString()}', result.typeStr);
|
||||
DoctorResultEvent(validator: validator, result: result).send();
|
||||
|
||||
if (result.statusInfo != null) {
|
||||
printStatus('${result.coloredLeadingBox} ${validator.title} (${result.statusInfo})',
|
||||
|
@ -16,7 +16,7 @@ import '../convert.dart';
|
||||
import '../devfs.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
import 'fuchsia_pm.dart';
|
||||
import 'fuchsia_sdk.dart';
|
||||
|
@ -24,7 +24,7 @@ import '../globals.dart';
|
||||
import '../macos/cocoapod_utils.dart';
|
||||
import '../macos/xcode.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../services.dart';
|
||||
import 'code_signing.dart';
|
||||
import 'xcodeproj.dart';
|
||||
@ -472,13 +472,10 @@ Future<void> diagnoseXcodeBuildFailure(XcodeBuildResult result) async {
|
||||
if (result.xcodeBuildExecution != null &&
|
||||
result.xcodeBuildExecution.buildForPhysicalDevice &&
|
||||
result.stdout?.toUpperCase()?.contains('BITCODE') == true) {
|
||||
flutterUsage.sendEvent(
|
||||
'Xcode',
|
||||
'bitcode-failure',
|
||||
parameters: <String, String>{
|
||||
'build-commands': result.xcodeBuildExecution.buildCommands.toString(),
|
||||
'build-settings': result.xcodeBuildExecution.buildSettings.toString(),
|
||||
});
|
||||
BuildEvent('xcode-bitcode-failure',
|
||||
command: result.xcodeBuildExecution.buildCommands.toString(),
|
||||
settings: result.xcodeBuildExecution.buildSettings.toString(),
|
||||
).send();
|
||||
}
|
||||
|
||||
if (result.xcodeBuildExecution != null &&
|
||||
|
@ -13,7 +13,7 @@ import '../cache.dart';
|
||||
import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
/// Builds the Linux project through the Makefile.
|
||||
Future<void> buildLinux(LinuxProject linuxProject, BuildInfo buildInfo, {String target = 'lib/main.dart'}) async {
|
||||
|
@ -12,7 +12,7 @@ import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
import '../ios/xcodeproj.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
import 'cocoapod_utils.dart';
|
||||
|
||||
|
@ -2,17 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../base/io.dart';
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../globals.dart';
|
||||
|
||||
import 'usage.dart';
|
||||
part of reporting;
|
||||
|
||||
/// Tells crash backend that the error is from the Flutter CLI.
|
||||
const String _kProductId = 'Flutter_Tools';
|
||||
|
@ -2,9 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'usage.dart';
|
||||
part of reporting;
|
||||
|
||||
class DisabledUsage implements Usage {
|
||||
@override
|
||||
|
137
packages/flutter_tools/lib/src/reporting/events.dart
Normal file
137
packages/flutter_tools/lib/src/reporting/events.dart
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
part of reporting;
|
||||
|
||||
/// A generic usage even that does not involve custom dimensions.
|
||||
///
|
||||
/// If sending values for custom dimensions is required, extend this class as
|
||||
/// below.
|
||||
class UsageEvent {
|
||||
UsageEvent(this.category, this.parameter);
|
||||
|
||||
final String category;
|
||||
final String parameter;
|
||||
|
||||
void send() {
|
||||
flutterUsage.sendEvent(category, parameter);
|
||||
}
|
||||
}
|
||||
|
||||
/// A usage event related to hot reload/restart.
|
||||
///
|
||||
/// On a successful hot reload, we collect stats that help understand scale of
|
||||
/// the update. For example, [syncedLibraryCount]/[finalLibraryCount] indicates
|
||||
/// how many libraries were affected by the hot reload request. Relation of
|
||||
/// [invalidatedSourcesCount] to [syncedLibraryCount] should help understand
|
||||
/// sync/transfer "overhead" of updating this number of source files.
|
||||
class HotEvent extends UsageEvent {
|
||||
HotEvent(String parameter, {
|
||||
@required this.targetPlatform,
|
||||
@required this.sdkName,
|
||||
@required this.emulator,
|
||||
@required this.fullRestart,
|
||||
this.reason,
|
||||
this.finalLibraryCount,
|
||||
this.syncedLibraryCount,
|
||||
this.syncedClassesCount,
|
||||
this.syncedProceduresCount,
|
||||
this.syncedBytes,
|
||||
this.invalidatedSourcesCount,
|
||||
this.transferTimeInMs,
|
||||
this.overallTimeInMs,
|
||||
}) : super('hot', parameter);
|
||||
|
||||
final String reason;
|
||||
final String targetPlatform;
|
||||
final String sdkName;
|
||||
final bool emulator;
|
||||
final bool fullRestart;
|
||||
final int finalLibraryCount;
|
||||
final int syncedLibraryCount;
|
||||
final int syncedClassesCount;
|
||||
final int syncedProceduresCount;
|
||||
final int syncedBytes;
|
||||
final int invalidatedSourcesCount;
|
||||
final int transferTimeInMs;
|
||||
final int overallTimeInMs;
|
||||
|
||||
@override
|
||||
void send() {
|
||||
final Map<String, String> parameters = _useCdKeys(<CustomDimensions, String>{
|
||||
CustomDimensions.hotEventTargetPlatform: targetPlatform,
|
||||
CustomDimensions.hotEventSdkName: sdkName,
|
||||
CustomDimensions.hotEventEmulator: emulator.toString(),
|
||||
CustomDimensions.hotEventFullRestart: fullRestart.toString(),
|
||||
if (reason != null)
|
||||
CustomDimensions.hotEventReason: reason,
|
||||
if (finalLibraryCount != null)
|
||||
CustomDimensions.hotEventFinalLibraryCount: finalLibraryCount.toString(),
|
||||
if (syncedLibraryCount != null)
|
||||
CustomDimensions.hotEventSyncedLibraryCount: syncedLibraryCount.toString(),
|
||||
if (syncedClassesCount != null)
|
||||
CustomDimensions.hotEventSyncedClassesCount: syncedClassesCount.toString(),
|
||||
if (syncedProceduresCount != null)
|
||||
CustomDimensions.hotEventSyncedProceduresCount: syncedProceduresCount.toString(),
|
||||
if (syncedBytes != null)
|
||||
CustomDimensions.hotEventSyncedBytes: syncedBytes.toString(),
|
||||
if (invalidatedSourcesCount != null)
|
||||
CustomDimensions.hotEventInvalidatedSourcesCount: invalidatedSourcesCount.toString(),
|
||||
if (transferTimeInMs != null)
|
||||
CustomDimensions.hotEventTransferTimeInMs: transferTimeInMs.toString(),
|
||||
if (overallTimeInMs != null)
|
||||
CustomDimensions.hotEventOverallTimeInMs: overallTimeInMs.toString(),
|
||||
});
|
||||
flutterUsage.sendEvent(category, parameter, parameters: parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// An event that reports the result of a [DoctorValidator]
|
||||
class DoctorResultEvent extends UsageEvent {
|
||||
DoctorResultEvent({
|
||||
@required DoctorValidator validator,
|
||||
@required ValidationResult result
|
||||
}) : super('doctorResult.${validator.runtimeType.toString()}',
|
||||
result.typeStr);
|
||||
// TODO(zra): Override send() to detect a GroupedValidator and send separate
|
||||
// events for each sub-validator.
|
||||
}
|
||||
|
||||
/// An event that reports success or failure of a pub get.
|
||||
class PubGetEvent extends UsageEvent {
|
||||
PubGetEvent({
|
||||
@required bool success,
|
||||
}) : super('packages-pub-get', success ? 'success' : 'failure');
|
||||
}
|
||||
|
||||
/// An event that reports something about a build.
|
||||
class BuildEvent extends UsageEvent {
|
||||
BuildEvent(String parameter, {
|
||||
this.command,
|
||||
this.settings,
|
||||
}) : super(
|
||||
'build' +
|
||||
(FlutterCommand.current == null ? '' : '-${FlutterCommand.current.name}'),
|
||||
parameter);
|
||||
|
||||
final String command;
|
||||
final String settings;
|
||||
|
||||
@override
|
||||
void send() {
|
||||
final Map<String, String> parameters = _useCdKeys(<CustomDimensions, String>{
|
||||
if (command != null)
|
||||
CustomDimensions.buildEventCommand: command,
|
||||
if (settings != null)
|
||||
CustomDimensions.buildEventSettings: settings,
|
||||
});
|
||||
flutterUsage.sendEvent(category, parameter, parameters: parameters);
|
||||
}
|
||||
}
|
||||
|
||||
/// An event that reports the result of a top-level command.
|
||||
class CommandResultEvent extends UsageEvent {
|
||||
CommandResultEvent(String commandPath, FlutterCommandResult result)
|
||||
: super(commandPath, result?.toString() ?? 'unspecified');
|
||||
}
|
30
packages/flutter_tools/lib/src/reporting/reporting.dart
Normal file
30
packages/flutter_tools/lib/src/reporting/reporting.dart
Normal file
@ -0,0 +1,30 @@
|
||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
library reporting;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:usage/usage_io.dart';
|
||||
|
||||
import '../base/config.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/time.dart';
|
||||
import '../base/utils.dart';
|
||||
import '../doctor.dart';
|
||||
import '../features.dart';
|
||||
import '../globals.dart';
|
||||
import '../runner/flutter_command.dart';
|
||||
import '../version.dart';
|
||||
|
||||
part 'crash_reporting.dart';
|
||||
part 'disabled_usage.dart';
|
||||
part 'events.dart';
|
||||
part 'usage.dart';
|
@ -2,78 +2,146 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:usage/usage_io.dart';
|
||||
|
||||
import '../base/config.dart';
|
||||
import '../base/context.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/os.dart';
|
||||
import '../base/platform.dart';
|
||||
import '../base/time.dart';
|
||||
import '../base/utils.dart';
|
||||
import '../features.dart';
|
||||
import '../globals.dart';
|
||||
import '../version.dart';
|
||||
part of reporting;
|
||||
|
||||
const String _kFlutterUA = 'UA-67589403-6';
|
||||
|
||||
// Attached to all `Usage.sendCommand` and `Usage.sendEvent`.
|
||||
const String _kLocalTimeParameter = 'cd33';
|
||||
/// The collection of custom dimensions understood by the analytics backend.
|
||||
/// When adding to this list, first ensure that the custom dimension is
|
||||
/// defined in the backend, or will be defined shortly after the relevent PR
|
||||
/// lands.
|
||||
enum CustomDimensions {
|
||||
sessionHostOsDetails, // cd1
|
||||
sessionChannelName, // cd2
|
||||
commandRunIsEmulator, // cd3
|
||||
commandRunTargetName, // cd4
|
||||
hotEventReason, // cd5
|
||||
hotEventFinalLibraryCount, // cd6
|
||||
hotEventSyncedLibraryCount, // cd7
|
||||
hotEventSyncedClassesCount, // cd8
|
||||
hotEventSyncedProceduresCount, // cd9
|
||||
hotEventSyncedBytes, // cd10
|
||||
hotEventInvalidatedSourcesCount, // cd11
|
||||
hotEventTransferTimeInMs, // cd12
|
||||
hotEventOverallTimeInMs, // cd13
|
||||
commandRunProjectType, // cd14
|
||||
commandRunProjectHostLanguage, // cd15
|
||||
commandCreateAndroidLanguage, // cd16
|
||||
commandCreateIosLanguage, // cd17
|
||||
commandRunProjectModule, // cd18
|
||||
commandCreateProjectType, // cd19
|
||||
commandPackagesNumberPlugins, // cd20
|
||||
commandPackagesProjectModule, // cd21
|
||||
commandRunTargetOsVersion, // cd22
|
||||
commandRunModeName, // cd23
|
||||
commandBuildBundleTargetPlatform, // cd24
|
||||
commandBuildBundleIsModule, // cd25
|
||||
commandResult, // cd26
|
||||
hotEventTargetPlatform, // cd27
|
||||
hotEventSdkName, // cd28
|
||||
hotEventEmulator, // cd29
|
||||
hotEventFullRestart, // cd30
|
||||
commandHasTerminal, // cd31
|
||||
enabledFlutterFeatures, // cd32
|
||||
localTime, // cd33
|
||||
commandBuildAarTargetPlatform, // cd34
|
||||
commandBuildAarProjectType, // cd35
|
||||
buildEventCommand, // cd36
|
||||
buildEventSettings, // cd37
|
||||
}
|
||||
|
||||
const String kSessionHostOsDetails = 'cd1';
|
||||
const String kSessionChannelName = 'cd2';
|
||||
String cdKey(CustomDimensions cd) => 'cd${cd.index + 1}';
|
||||
|
||||
const String kEventReloadReasonParameterName = 'cd5';
|
||||
const String kEventReloadFinalLibraryCount = 'cd6';
|
||||
const String kEventReloadSyncedLibraryCount = 'cd7';
|
||||
const String kEventReloadSyncedClassesCount = 'cd8';
|
||||
const String kEventReloadSyncedProceduresCount = 'cd9';
|
||||
const String kEventReloadSyncedBytes = 'cd10';
|
||||
const String kEventReloadInvalidatedSourcesCount = 'cd11';
|
||||
const String kEventReloadTransferTimeInMs = 'cd12';
|
||||
const String kEventReloadOverallTimeInMs = 'cd13';
|
||||
|
||||
const String kCommandRunIsEmulator = 'cd3';
|
||||
const String kCommandRunTargetName = 'cd4';
|
||||
const String kCommandRunProjectType = 'cd14';
|
||||
const String kCommandRunProjectHostLanguage = 'cd15';
|
||||
const String kCommandRunProjectModule = 'cd18';
|
||||
const String kCommandRunTargetOsVersion = 'cd22';
|
||||
const String kCommandRunModeName = 'cd23';
|
||||
|
||||
const String kCommandCreateAndroidLanguage = 'cd16';
|
||||
const String kCommandCreateIosLanguage = 'cd17';
|
||||
const String kCommandCreateProjectType = 'cd19';
|
||||
|
||||
const String kCommandPackagesNumberPlugins = 'cd20';
|
||||
const String kCommandPackagesProjectModule = 'cd21';
|
||||
|
||||
const String kCommandBuildBundleTargetPlatform = 'cd24';
|
||||
const String kCommandBuildBundleIsModule = 'cd25';
|
||||
|
||||
const String kCommandResult = 'cd26';
|
||||
const String kCommandHasTerminal = 'cd31';
|
||||
|
||||
const String kCommandBuildAarTargetPlatform = 'cd34';
|
||||
const String kCommandBuildAarProjectType = 'cd35';
|
||||
|
||||
const String reloadExceptionTargetPlatform = 'cd27';
|
||||
const String reloadExceptionSdkName = 'cd28';
|
||||
const String reloadExceptionEmulator = 'cd29';
|
||||
const String reloadExceptionFullRestart = 'cd30';
|
||||
|
||||
const String enabledFlutterFeatures = 'cd32';
|
||||
// Next ID: cd36
|
||||
Map<String, String> _useCdKeys(Map<CustomDimensions, String> parameters) {
|
||||
return parameters.map((CustomDimensions k, String v) =>
|
||||
MapEntry<String, String>(cdKey(k), v));
|
||||
}
|
||||
|
||||
Usage get flutterUsage => Usage.instance;
|
||||
|
||||
class Usage {
|
||||
abstract class Usage {
|
||||
factory Usage({
|
||||
String settingsName = 'flutter',
|
||||
String versionOverride,
|
||||
String configDirOverride
|
||||
}) => _UsageImpl(settingsName: settingsName,
|
||||
versionOverride: versionOverride,
|
||||
configDirOverride: configDirOverride);
|
||||
|
||||
/// Returns [Usage] active in the current app context.
|
||||
static Usage get instance => context.get<Usage>();
|
||||
|
||||
/// Uses the global [Usage] instance to send a 'command' to analytics.
|
||||
static void command(String command, {
|
||||
Map<CustomDimensions, String> parameters,
|
||||
}) => flutterUsage.sendCommand(command, parameters: _useCdKeys(parameters));
|
||||
|
||||
/// Whether this is the first run of the tool.
|
||||
bool get isFirstRun;
|
||||
|
||||
/// Whether analytics reporting should be supressed.
|
||||
bool get suppressAnalytics;
|
||||
|
||||
/// Suppress analytics for this session.
|
||||
set suppressAnalytics(bool value);
|
||||
|
||||
/// Whether analytics reporting is enabled.
|
||||
bool get enabled;
|
||||
|
||||
/// Enable or disable reporting analytics.
|
||||
set enabled(bool value);
|
||||
|
||||
/// A stable randomly generated UUID used to deduplicate multiple identical
|
||||
/// reports coming from the same computer.
|
||||
String get clientId;
|
||||
|
||||
/// Sends a 'command' to the underlying analytics implementation.
|
||||
///
|
||||
/// Note that using [command] above is preferred to ensure that the parameter
|
||||
/// keys are well-defined in [CustomDimensions] above.
|
||||
void sendCommand(String command, {
|
||||
Map<String, String> parameters
|
||||
});
|
||||
|
||||
/// Sends an 'event' to the underlying analytics implementation.
|
||||
///
|
||||
/// Note that this method should not be used directly, instead see the
|
||||
/// event types defined in this directory in events.dart.
|
||||
@visibleForOverriding
|
||||
@visibleForTesting
|
||||
void sendEvent(String category, String parameter, {
|
||||
Map<String, String> parameters
|
||||
});
|
||||
|
||||
/// Sends timing information to the underlying analytics implementation.
|
||||
void sendTiming(String category, String variableName, Duration duration, {
|
||||
String label
|
||||
});
|
||||
|
||||
/// Sends an exception to the underlying analytics implementation.
|
||||
void sendException(dynamic exception);
|
||||
|
||||
/// Fires whenever analytics data is sent over the network.
|
||||
@visibleForTesting
|
||||
Stream<Map<String, dynamic>> get onSend;
|
||||
|
||||
/// Returns when the last analytics event has been sent, or after a fixed
|
||||
/// (short) delay, whichever is less.
|
||||
Future<void> ensureAnalyticsSent();
|
||||
|
||||
/// Prints a welcome message that informs the tool user about the collection
|
||||
/// of anonymous usage information.
|
||||
void printWelcome();
|
||||
}
|
||||
|
||||
class _UsageImpl implements Usage {
|
||||
/// Create a new Usage instance; [versionOverride] and [configDirOverride] are
|
||||
/// used for testing.
|
||||
Usage({ String settingsName = 'flutter', String versionOverride, String configDirOverride}) {
|
||||
_UsageImpl({
|
||||
String settingsName = 'flutter',
|
||||
String versionOverride,
|
||||
String configDirOverride
|
||||
}) {
|
||||
final FlutterVersion flutterVersion = FlutterVersion.instance;
|
||||
final String version = versionOverride ?? flutterVersion.getVersionString(redactUnknownBranches: true);
|
||||
|
||||
@ -90,9 +158,10 @@ class Usage {
|
||||
LogToFileAnalytics(logFilePath);
|
||||
|
||||
// Report a more detailed OS version string than package:usage does by default.
|
||||
_analytics.setSessionValue(kSessionHostOsDetails, os.name);
|
||||
_analytics.setSessionValue(cdKey(CustomDimensions.sessionHostOsDetails), os.name);
|
||||
// Send the branch name as the "channel".
|
||||
_analytics.setSessionValue(kSessionChannelName, flutterVersion.getBranchName(redactUnknownBranches: true));
|
||||
_analytics.setSessionValue(cdKey(CustomDimensions.sessionChannelName),
|
||||
flutterVersion.getBranchName(redactUnknownBranches: true));
|
||||
// For each flutter experimental feature, record a session value in a comma
|
||||
// separated list.
|
||||
final String enabledFeatures = allFeatures
|
||||
@ -102,7 +171,7 @@ class Usage {
|
||||
})
|
||||
.map((Feature feature) => feature.configSetting)
|
||||
.join(',');
|
||||
_analytics.setSessionValue(enabledFlutterFeatures, enabledFeatures);
|
||||
_analytics.setSessionValue(cdKey(CustomDimensions.enabledFlutterFeatures), enabledFeatures);
|
||||
|
||||
// Record the host as the application installer ID - the context that flutter_tools is running in.
|
||||
if (platform.environment.containsKey('FLUTTER_HOST')) {
|
||||
@ -119,34 +188,34 @@ class Usage {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns [Usage] active in the current app context.
|
||||
static Usage get instance => context.get<Usage>();
|
||||
|
||||
Analytics _analytics;
|
||||
|
||||
bool _printedWelcome = false;
|
||||
bool _suppressAnalytics = false;
|
||||
|
||||
@override
|
||||
bool get isFirstRun => _analytics.firstRun;
|
||||
|
||||
bool get enabled => _analytics.enabled;
|
||||
|
||||
@override
|
||||
bool get suppressAnalytics => _suppressAnalytics || _analytics.firstRun;
|
||||
|
||||
/// Suppress analytics for this session.
|
||||
@override
|
||||
set suppressAnalytics(bool value) {
|
||||
_suppressAnalytics = value;
|
||||
}
|
||||
|
||||
/// Enable or disable reporting analytics.
|
||||
@override
|
||||
bool get enabled => _analytics.enabled;
|
||||
|
||||
@override
|
||||
set enabled(bool value) {
|
||||
_analytics.enabled = value;
|
||||
}
|
||||
|
||||
/// A stable randomly generated UUID used to deduplicate multiple identical
|
||||
/// reports coming from the same computer.
|
||||
@override
|
||||
String get clientId => _analytics.clientId;
|
||||
|
||||
@override
|
||||
void sendCommand(String command, { Map<String, String> parameters }) {
|
||||
if (suppressAnalytics) {
|
||||
return;
|
||||
@ -154,11 +223,12 @@ class Usage {
|
||||
|
||||
final Map<String, String> paramsWithLocalTime = <String, String>{
|
||||
...?parameters,
|
||||
_kLocalTimeParameter: systemClock.now().toString(),
|
||||
cdKey(CustomDimensions.localTime): systemClock.now().toString(),
|
||||
};
|
||||
_analytics.sendScreenView(command, parameters: paramsWithLocalTime);
|
||||
}
|
||||
|
||||
@override
|
||||
void sendEvent(
|
||||
String category,
|
||||
String parameter, {
|
||||
@ -170,12 +240,13 @@ class Usage {
|
||||
|
||||
final Map<String, String> paramsWithLocalTime = <String, String>{
|
||||
...?parameters,
|
||||
_kLocalTimeParameter: systemClock.now().toString(),
|
||||
cdKey(CustomDimensions.localTime): systemClock.now().toString(),
|
||||
};
|
||||
|
||||
_analytics.sendEvent(category, parameter, parameters: paramsWithLocalTime);
|
||||
}
|
||||
|
||||
@override
|
||||
void sendTiming(
|
||||
String category,
|
||||
String variableName,
|
||||
@ -193,6 +264,7 @@ class Usage {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void sendException(dynamic exception) {
|
||||
if (suppressAnalytics) {
|
||||
return;
|
||||
@ -200,12 +272,10 @@ class Usage {
|
||||
_analytics.sendException(exception.runtimeType.toString());
|
||||
}
|
||||
|
||||
/// Fires whenever analytics data is sent over the network.
|
||||
@visibleForTesting
|
||||
@override
|
||||
Stream<Map<String, dynamic>> get onSend => _analytics.onSend;
|
||||
|
||||
/// Returns when the last analytics event has been sent, or after a fixed
|
||||
/// (short) delay, whichever is less.
|
||||
@override
|
||||
Future<void> ensureAnalyticsSent() async {
|
||||
// TODO(devoncarew): This may delay tool exit and could cause some analytics
|
||||
// events to not be reported. Perhaps we could send the analytics pings
|
||||
@ -213,6 +283,7 @@ class Usage {
|
||||
await _analytics.waitForLastPing(timeout: const Duration(milliseconds: 250));
|
||||
}
|
||||
|
||||
@override
|
||||
void printWelcome() {
|
||||
// This gets called if it's the first run by the selected command, if any,
|
||||
// and on exit, in case there was no command.
|
||||
@ -255,7 +326,9 @@ class LogToFileAnalytics extends AnalyticsMock {
|
||||
final Map<String, String> _sessionValues = <String, String>{};
|
||||
|
||||
@override
|
||||
Future<void> sendScreenView(String viewName, {Map<String, String> parameters}) {
|
||||
Future<void> sendScreenView(String viewName, {
|
||||
Map<String, String> parameters,
|
||||
}) {
|
||||
parameters ??= <String, String>{};
|
||||
parameters['viewName'] = viewName;
|
||||
parameters.addAll(_sessionValues);
|
||||
|
@ -21,7 +21,7 @@ import 'convert.dart';
|
||||
import 'devfs.dart';
|
||||
import 'device.dart';
|
||||
import 'globals.dart';
|
||||
import 'reporting/usage.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
import 'resident_runner.dart';
|
||||
import 'vmservice.dart';
|
||||
|
||||
@ -367,15 +367,12 @@ class HotRunner extends ResidentRunner {
|
||||
futures.add(view.flushUIThreadTasks());
|
||||
await Future.wait(futures);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Future<OperationResult> _restartFromSources({ String reason, bool benchmarkMode = false }) async {
|
||||
final Map<String, String> analyticsParameters =
|
||||
reason == null
|
||||
? null
|
||||
: <String, String>{kEventReloadReasonParameterName: reason};
|
||||
|
||||
Future<OperationResult> _restartFromSources({
|
||||
String reason,
|
||||
bool benchmarkMode = false
|
||||
}) async {
|
||||
if (!_isPaused()) {
|
||||
printTrace('Refreshing active FlutterViews before restarting.');
|
||||
await refreshViews();
|
||||
@ -387,23 +384,27 @@ class HotRunner extends ResidentRunner {
|
||||
final UpdateFSReport updatedDevFS = await _updateDevFS(fullRestart: true);
|
||||
if (!updatedDevFS.success) {
|
||||
for (FlutterDevice device in flutterDevices) {
|
||||
if (device.generator != null)
|
||||
if (device.generator != null) {
|
||||
await device.generator.reject();
|
||||
}
|
||||
}
|
||||
return OperationResult(1, 'DevFS synchronization failed');
|
||||
}
|
||||
_resetDirtyAssets();
|
||||
for (FlutterDevice device in flutterDevices) {
|
||||
// VM must have accepted the kernel binary, there will be no reload
|
||||
// report, so we let incremental compiler know that source code was accepted.
|
||||
if (device.generator != null)
|
||||
if (device.generator != null) {
|
||||
device.generator.accept();
|
||||
}
|
||||
}
|
||||
// Check if the isolate is paused and resume it.
|
||||
final List<Future<void>> futures = <Future<void>>[];
|
||||
for (FlutterDevice device in flutterDevices) {
|
||||
for (FlutterView view in device.views) {
|
||||
if (view.uiIsolate != null) {
|
||||
if (view.uiIsolate == null) {
|
||||
continue;
|
||||
}
|
||||
// Reload the isolate.
|
||||
final Completer<void> completer = Completer<void>();
|
||||
futures.add(completer.future);
|
||||
@ -421,7 +422,6 @@ class HotRunner extends ResidentRunner {
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
await Future.wait(futures);
|
||||
// We are now running from source.
|
||||
_runningFromSnapshot = false;
|
||||
@ -432,7 +432,8 @@ class HotRunner extends ResidentRunner {
|
||||
_runningFromSnapshot = false;
|
||||
_addBenchmarkData('hotRestartMillisecondsToFrame',
|
||||
restartTimer.elapsed.inMilliseconds);
|
||||
flutterUsage.sendEvent('hot', 'restart', parameters: analyticsParameters);
|
||||
|
||||
// Send timing analytics.
|
||||
flutterUsage.sendTiming('hot', 'restart', restartTimer.elapsed);
|
||||
|
||||
// In benchmark mode, make sure all stream notifications have finished.
|
||||
@ -499,9 +500,62 @@ class HotRunner extends ResidentRunner {
|
||||
bool get supportsRestart => true;
|
||||
|
||||
@override
|
||||
Future<OperationResult> restart({ bool fullRestart = false, bool pauseAfterRestart = false, String reason, bool benchmarkMode = false }) async {
|
||||
Future<OperationResult> restart({
|
||||
bool fullRestart = false,
|
||||
bool pauseAfterRestart = false,
|
||||
String reason,
|
||||
bool benchmarkMode = false
|
||||
}) async {
|
||||
String targetPlatform;
|
||||
String sdkName;
|
||||
bool emulator;
|
||||
if (flutterDevices.length == 1) {
|
||||
final Device device = flutterDevices.first.device;
|
||||
targetPlatform = getNameForTargetPlatform(await device.targetPlatform);
|
||||
sdkName = await device.sdkNameAndVersion;
|
||||
emulator = await device.isLocalEmulator;
|
||||
} else if (flutterDevices.length > 1) {
|
||||
targetPlatform = 'multiple';
|
||||
sdkName = 'multiple';
|
||||
emulator = false;
|
||||
} else {
|
||||
targetPlatform = 'unknown';
|
||||
sdkName = 'unknown';
|
||||
emulator = false;
|
||||
}
|
||||
final Stopwatch timer = Stopwatch()..start();
|
||||
if (fullRestart) {
|
||||
final OperationResult result = await _fullRestartHelper(
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
reason: reason,
|
||||
benchmarkMode: benchmarkMode,
|
||||
);
|
||||
printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
|
||||
return result;
|
||||
}
|
||||
final OperationResult result = await _hotReloadHelper(
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
reason: reason,
|
||||
pauseAfterRestart: pauseAfterRestart,
|
||||
);
|
||||
if (result.isOk) {
|
||||
final String elapsed = getElapsedAsMilliseconds(timer.elapsed);
|
||||
printStatus('${result.message} in $elapsed.');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<OperationResult> _fullRestartHelper({
|
||||
String targetPlatform,
|
||||
String sdkName,
|
||||
bool emulator,
|
||||
String reason,
|
||||
bool benchmarkMode,
|
||||
}) async {
|
||||
if (!canHotRestart) {
|
||||
return OperationResult(1, 'hotRestart not supported');
|
||||
}
|
||||
@ -510,21 +564,41 @@ class HotRunner extends ResidentRunner {
|
||||
timeout: timeoutConfiguration.fastOperation,
|
||||
progressId: 'hot.restart',
|
||||
);
|
||||
OperationResult result;
|
||||
String restartEvent = 'restart';
|
||||
try {
|
||||
if (!(await hotRunnerConfig.setupHotRestart()))
|
||||
if (!(await hotRunnerConfig.setupHotRestart())) {
|
||||
return OperationResult(1, 'setupHotRestart failed');
|
||||
final OperationResult result = await _restartFromSources(reason: reason, benchmarkMode: benchmarkMode,);
|
||||
if (!result.isOk)
|
||||
return result;
|
||||
}
|
||||
result = await _restartFromSources(
|
||||
reason: reason,
|
||||
benchmarkMode: benchmarkMode,
|
||||
);
|
||||
if (!result.isOk) {
|
||||
restartEvent = 'restart-failed';
|
||||
}
|
||||
} on rpc.RpcException {
|
||||
await _measureJsonRpcException(flutterDevices, fullRestart);
|
||||
restartEvent = 'exception';
|
||||
return OperationResult(1, 'hot restart failed to complete', fatal: true);
|
||||
} finally {
|
||||
HotEvent(restartEvent,
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
fullRestart: true,
|
||||
reason: reason).send();
|
||||
status.cancel();
|
||||
}
|
||||
printStatus('Restarted application in ${getElapsedAsMilliseconds(timer.elapsed)}.');
|
||||
return OperationResult.ok;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<OperationResult> _hotReloadHelper({
|
||||
String targetPlatform,
|
||||
String sdkName,
|
||||
bool emulator,
|
||||
String reason,
|
||||
bool pauseAfterRestart = false,
|
||||
}) async {
|
||||
final bool reloadOnTopOfSnapshot = _runningFromSnapshot;
|
||||
final String progressPrefix = reloadOnTopOfSnapshot ? 'Initializing' : 'Performing';
|
||||
Status status = logger.startProgress(
|
||||
@ -533,9 +607,11 @@ class HotRunner extends ResidentRunner {
|
||||
progressId: 'hot.reload',
|
||||
);
|
||||
OperationResult result;
|
||||
bool showTime = true;
|
||||
try {
|
||||
result = await _reloadSources(
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
pause: pauseAfterRestart,
|
||||
reason: reason,
|
||||
onSlow: (String message) {
|
||||
@ -545,37 +621,37 @@ class HotRunner extends ResidentRunner {
|
||||
timeout: timeoutConfiguration.slowOperation,
|
||||
progressId: 'hot.reload',
|
||||
);
|
||||
showTime = false;
|
||||
},
|
||||
);
|
||||
} on rpc.RpcException {
|
||||
await _measureJsonRpcException(flutterDevices, fullRestart);
|
||||
HotEvent('exception',
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
fullRestart: false,
|
||||
reason: reason).send();
|
||||
return OperationResult(1, 'hot reload failed to complete', fatal: true);
|
||||
} finally {
|
||||
status.cancel();
|
||||
}
|
||||
if (result.isOk) {
|
||||
if (showTime) {
|
||||
printStatus('${result.message} in ${getElapsedAsMilliseconds(timer.elapsed)}.');
|
||||
} else {
|
||||
printStatus('${result.message}.');
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Future<OperationResult> _reloadSources({ bool pause = false, String reason, void Function(String message) onSlow }) async {
|
||||
final Map<String, String> analyticsParameters = <String, String>{};
|
||||
if (reason != null) {
|
||||
analyticsParameters[kEventReloadReasonParameterName] = reason;
|
||||
}
|
||||
Future<OperationResult> _reloadSources({
|
||||
String targetPlatform,
|
||||
String sdkName,
|
||||
bool emulator,
|
||||
bool pause = false,
|
||||
String reason,
|
||||
void Function(String message) onSlow
|
||||
}) async {
|
||||
for (FlutterDevice device in flutterDevices) {
|
||||
for (FlutterView view in device.views) {
|
||||
if (view.uiIsolate == null)
|
||||
if (view.uiIsolate == null) {
|
||||
throw 'Application isolate not found';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The initial launch is from a script snapshot. When we reload from source
|
||||
// on top of a script snapshot, the first reload will be a worst case reload
|
||||
@ -595,10 +671,12 @@ class HotRunner extends ResidentRunner {
|
||||
final UpdateFSReport updatedDevFS = await _updateDevFS();
|
||||
// Record time it took to synchronize to DevFS.
|
||||
_addBenchmarkData('hotReloadDevFSSyncMilliseconds', devFSTimer.elapsed.inMilliseconds);
|
||||
if (!updatedDevFS.success)
|
||||
if (!updatedDevFS.success) {
|
||||
return OperationResult(1, 'DevFS synchronization failed');
|
||||
}
|
||||
String reloadMessage;
|
||||
final Stopwatch vmReloadTimer = Stopwatch()..start();
|
||||
Map<String, dynamic> firstReloadDetails;
|
||||
try {
|
||||
final String entryPath = fs.path.relative(
|
||||
getReloadPath(fullRestart: false),
|
||||
@ -635,28 +713,24 @@ class HotRunner extends ResidentRunner {
|
||||
final Map<String, dynamic> reloadReport = report.reports[0];
|
||||
if (!validateReloadReport(reloadReport)) {
|
||||
// Reload failed.
|
||||
flutterUsage.sendEvent('hot', 'reload-reject');
|
||||
HotEvent('reload-reject',
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
fullRestart: false,
|
||||
reason: reason,
|
||||
).send();
|
||||
return OperationResult(1, 'Reload rejected');
|
||||
} else {
|
||||
// Collect stats that help understand scale of update for this hot reload request.
|
||||
// For example, [syncedLibraryCount]/[finalLibraryCount] indicates how
|
||||
// many libraries were affected by the hot reload request.
|
||||
// Relation of [invalidatedSourcesCount] to [syncedLibraryCount] should help
|
||||
// understand sync/transfer "overhead" of updating this number of source files.
|
||||
final Map<String, dynamic> details = reloadReport['details'];
|
||||
analyticsParameters[kEventReloadFinalLibraryCount] = "${details['finalLibraryCount']}";
|
||||
analyticsParameters[kEventReloadSyncedLibraryCount] = "${details['receivedLibraryCount']}";
|
||||
analyticsParameters[kEventReloadSyncedClassesCount] = "${details['receivedClassesCount']}";
|
||||
analyticsParameters[kEventReloadSyncedProceduresCount] = "${details['receivedProceduresCount']}";
|
||||
analyticsParameters[kEventReloadSyncedBytes] = '${updatedDevFS.syncedBytes}';
|
||||
analyticsParameters[kEventReloadInvalidatedSourcesCount] = '${updatedDevFS.invalidatedSourcesCount}';
|
||||
analyticsParameters[kEventReloadTransferTimeInMs] = '${devFSTimer.elapsed.inMilliseconds}';
|
||||
}
|
||||
// Collect stats only from the first device. If/when run -d all is
|
||||
// refactored, we'll probably need to send one hot reload/restart event
|
||||
// per device to analytics.
|
||||
firstReloadDetails ??= reloadReport['details'];
|
||||
final int loadedLibraryCount = reloadReport['details']['loadedLibraryCount'];
|
||||
final int finalLibraryCount = reloadReport['details']['finalLibraryCount'];
|
||||
printTrace('reloaded $loadedLibraryCount of $finalLibraryCount libraries');
|
||||
reloadMessage = 'Reloaded $loadedLibraryCount of $finalLibraryCount libraries';
|
||||
}
|
||||
}
|
||||
} on Map<String, dynamic> catch (error, stackTrace) {
|
||||
printTrace('Hot reload failed: $error\n$stackTrace');
|
||||
final int errorCode = error['code'];
|
||||
@ -666,7 +740,13 @@ class HotRunner extends ResidentRunner {
|
||||
'the source code. Please address the error and then use "R" to '
|
||||
'restart the app.\n'
|
||||
'$errorMessage (error code: $errorCode)';
|
||||
flutterUsage.sendEvent('hot', 'reload-barred');
|
||||
HotEvent('reload-barred',
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
fullRestart: false,
|
||||
reason: reason,
|
||||
).send();
|
||||
return OperationResult(errorCode, errorMessage);
|
||||
}
|
||||
return OperationResult(errorCode, '$errorMessage (error code: $errorCode)');
|
||||
@ -783,8 +863,26 @@ class HotRunner extends ResidentRunner {
|
||||
final Duration reloadDuration = reloadTimer.elapsed;
|
||||
final int reloadInMs = reloadDuration.inMilliseconds;
|
||||
|
||||
analyticsParameters[kEventReloadOverallTimeInMs] = '$reloadInMs';
|
||||
flutterUsage.sendEvent('hot', 'reload', parameters: analyticsParameters);
|
||||
// Collect stats that help understand scale of update for this hot reload request.
|
||||
// For example, [syncedLibraryCount]/[finalLibraryCount] indicates how
|
||||
// many libraries were affected by the hot reload request.
|
||||
// Relation of [invalidatedSourcesCount] to [syncedLibraryCount] should help
|
||||
// understand sync/transfer "overhead" of updating this number of source files.
|
||||
HotEvent('reload',
|
||||
targetPlatform: targetPlatform,
|
||||
sdkName: sdkName,
|
||||
emulator: emulator,
|
||||
fullRestart: false,
|
||||
reason: reason,
|
||||
overallTimeInMs: reloadInMs,
|
||||
finalLibraryCount: firstReloadDetails['finalLibraryCount'],
|
||||
syncedLibraryCount: firstReloadDetails['receivedLibraryCount'],
|
||||
syncedClassesCount: firstReloadDetails['receivedClassesCount'],
|
||||
syncedProceduresCount: firstReloadDetails['receivedProceduresCount'],
|
||||
syncedBytes: updatedDevFS.syncedBytes,
|
||||
invalidatedSourcesCount: updatedDevFS.invalidatedSourcesCount,
|
||||
transferTimeInMs: devFSTimer.elapsed.inMilliseconds,
|
||||
).send();
|
||||
|
||||
if (shouldReportReloadTime) {
|
||||
printTrace('Hot reload performed in ${getElapsedAsMilliseconds(reloadDuration)}.');
|
||||
@ -952,32 +1050,3 @@ class ProjectFileInvalidator {
|
||||
return invalidatedFiles;
|
||||
}
|
||||
}
|
||||
|
||||
// This is an error case we would like to know more about.
|
||||
Future<void> _measureJsonRpcException(List<FlutterDevice> flutterDevices, bool fullRestart) async {
|
||||
String targetPlatform;
|
||||
String deviceSdk;
|
||||
bool emulator;
|
||||
if (flutterDevices.length == 1) {
|
||||
final Device device = flutterDevices.first.device;
|
||||
targetPlatform = getNameForTargetPlatform(await device.targetPlatform);
|
||||
deviceSdk = await device.sdkNameAndVersion;
|
||||
emulator = await device.isLocalEmulator;
|
||||
} else if (flutterDevices.length > 1) {
|
||||
targetPlatform = 'multiple';
|
||||
deviceSdk = 'multiple';
|
||||
emulator = false;
|
||||
} else {
|
||||
targetPlatform = 'unknown';
|
||||
deviceSdk = 'unknown';
|
||||
emulator = false;
|
||||
}
|
||||
flutterUsage.sendEvent('hot', 'exception',
|
||||
parameters: <String, String>{
|
||||
reloadExceptionTargetPlatform: targetPlatform,
|
||||
reloadExceptionSdkName: deviceSdk,
|
||||
reloadExceptionEmulator: emulator.toString(),
|
||||
reloadExceptionFullRestart: fullRestart.toString(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ import '../doctor.dart';
|
||||
import '../features.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import 'flutter_command_runner.dart';
|
||||
|
||||
export '../cache.dart' show DevelopmentArtifact;
|
||||
@ -63,6 +63,21 @@ class FlutterCommandResult {
|
||||
/// [FlutterCommand] will automatically measure and report the command's
|
||||
/// complete time if not overridden.
|
||||
final DateTime endTimeOverride;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (exitStatus) {
|
||||
case ExitStatus.success:
|
||||
return 'success';
|
||||
case ExitStatus.warning:
|
||||
return 'warning';
|
||||
case ExitStatus.fail:
|
||||
return 'fail';
|
||||
default:
|
||||
assert(false);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Common flutter command line options.
|
||||
@ -366,7 +381,8 @@ abstract class FlutterCommand extends Command<void> {
|
||||
}
|
||||
|
||||
/// Additional usage values to be sent with the usage ping.
|
||||
Future<Map<String, String>> get usageValues async => const <String, String>{};
|
||||
Future<Map<CustomDimensions, String>> get usageValues async =>
|
||||
const <CustomDimensions, String>{};
|
||||
|
||||
/// Runs this command.
|
||||
///
|
||||
@ -382,8 +398,9 @@ abstract class FlutterCommand extends Command<void> {
|
||||
name: 'command',
|
||||
overrides: <Type, Generator>{FlutterCommand: () => this},
|
||||
body: () async {
|
||||
if (flutterUsage.isFirstRun)
|
||||
if (flutterUsage.isFirstRun) {
|
||||
flutterUsage.printWelcome();
|
||||
}
|
||||
final String commandPath = await usagePath;
|
||||
FlutterCommandResult commandResult;
|
||||
try {
|
||||
@ -411,21 +428,7 @@ abstract class FlutterCommand extends Command<void> {
|
||||
}
|
||||
|
||||
// Send command result.
|
||||
String result = 'unspecified';
|
||||
if (commandResult != null) {
|
||||
switch (commandResult.exitStatus) {
|
||||
case ExitStatus.success:
|
||||
result = 'success';
|
||||
break;
|
||||
case ExitStatus.warning:
|
||||
result = 'warning';
|
||||
break;
|
||||
case ExitStatus.fail:
|
||||
result = 'fail';
|
||||
break;
|
||||
}
|
||||
}
|
||||
flutterUsage.sendEvent(commandPath, result);
|
||||
CommandResultEvent(commandPath, commandResult).send();
|
||||
|
||||
// Send timing.
|
||||
final List<String> labels = <String>[
|
||||
@ -476,12 +479,12 @@ abstract class FlutterCommand extends Command<void> {
|
||||
setupApplicationPackages();
|
||||
|
||||
if (commandPath != null) {
|
||||
final Map<String, String> additionalUsageValues = <String,String>{
|
||||
final Map<CustomDimensions, String> additionalUsageValues =
|
||||
<CustomDimensions, String>{
|
||||
...?await usageValues,
|
||||
CustomDimensions.commandHasTerminal: io.stdout.hasTerminal ? 'true' : 'false',
|
||||
};
|
||||
additionalUsageValues[kCommandHasTerminal] =
|
||||
io.stdout.hasTerminal ? 'true' : 'false';
|
||||
flutterUsage.sendCommand(commandPath, parameters: additionalUsageValues);
|
||||
Usage.command(commandPath, parameters: additionalUsageValues);
|
||||
}
|
||||
|
||||
return await runCommand();
|
||||
|
@ -30,7 +30,7 @@ import '../convert.dart';
|
||||
import '../dart/package_map.dart';
|
||||
import '../device.dart';
|
||||
import '../globals.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../tester/flutter_tester.dart';
|
||||
import '../version.dart';
|
||||
import '../vmservice.dart';
|
||||
|
@ -13,7 +13,7 @@ import '../build_info.dart';
|
||||
import '../bundle.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
/// The [WebCompilationProxy] instance.
|
||||
WebCompilationProxy get webCompilationProxy => context.get<WebCompilationProxy>();
|
||||
|
@ -13,7 +13,7 @@ import '../cache.dart';
|
||||
import '../convert.dart';
|
||||
import '../globals.dart';
|
||||
import '../project.dart';
|
||||
import '../reporting/usage.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
|
||||
import 'msbuild_utils.dart';
|
||||
import 'visual_studio.dart';
|
||||
|
@ -4,22 +4,21 @@
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import 'package:flutter_tools/src/base/config.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/build.dart';
|
||||
import 'package:flutter_tools/src/commands/config.dart';
|
||||
import 'package:flutter_tools/src/commands/doctor.dart';
|
||||
import 'package:flutter_tools/src/doctor.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:flutter_tools/src/version.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
@ -94,7 +93,8 @@ void main() {
|
||||
final Usage usage = Usage();
|
||||
usage.sendCommand('test');
|
||||
|
||||
expect(fs.file('test').readAsStringSync(), contains('$enabledFlutterFeatures: enable-web'));
|
||||
final String featuresKey = cdKey(CustomDimensions.enabledFlutterFeatures);
|
||||
expect(fs.file('test').readAsStringSync(), contains('$featuresKey: enable-web'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterVersion: () => FlutterVersion(const SystemClock()),
|
||||
Config: () => mockFlutterConfig,
|
||||
@ -114,7 +114,8 @@ void main() {
|
||||
final Usage usage = Usage();
|
||||
usage.sendCommand('test');
|
||||
|
||||
expect(fs.file('test').readAsStringSync(), contains('$enabledFlutterFeatures: enable-web,enable-linux-desktop,enable-macos-desktop'));
|
||||
final String featuresKey = cdKey(CustomDimensions.enabledFlutterFeatures);
|
||||
expect(fs.file('test').readAsStringSync(), contains('$featuresKey: enable-web,enable-linux-desktop,enable-macos-desktop'));
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterVersion: () => FlutterVersion(const SystemClock()),
|
||||
Config: () => mockFlutterConfig,
|
||||
|
@ -3,11 +3,11 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
import '../src/context.dart';
|
||||
|
@ -7,7 +7,7 @@ import 'package:flutter_tools/src/android/aar.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/build_aar.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -51,7 +51,7 @@ void main() {
|
||||
|
||||
final BuildAarCommand command = await runCommandIn(projectPath);
|
||||
expect(await command.usageValues,
|
||||
containsPair(kCommandBuildAarProjectType, 'module'));
|
||||
containsPair(CustomDimensions.commandBuildAarProjectType, 'module'));
|
||||
|
||||
}, overrides: <Type, Generator>{
|
||||
AarBuilder: () => mockAarBuilder,
|
||||
@ -63,7 +63,7 @@ void main() {
|
||||
|
||||
final BuildAarCommand command = await runCommandIn(projectPath);
|
||||
expect(await command.usageValues,
|
||||
containsPair(kCommandBuildAarProjectType, 'plugin'));
|
||||
containsPair(CustomDimensions.commandBuildAarProjectType, 'plugin'));
|
||||
|
||||
}, overrides: <Type, Generator>{
|
||||
AarBuilder: () => mockAarBuilder,
|
||||
@ -76,7 +76,7 @@ void main() {
|
||||
final BuildAarCommand command = await runCommandIn(projectPath,
|
||||
arguments: <String>['--target-platform=android-arm']);
|
||||
expect(await command.usageValues,
|
||||
containsPair(kCommandBuildAarTargetPlatform, 'android-arm'));
|
||||
containsPair(CustomDimensions.commandBuildAarTargetPlatform, 'android-arm'));
|
||||
|
||||
}, overrides: <Type, Generator>{
|
||||
AarBuilder: () => mockAarBuilder,
|
||||
|
@ -6,11 +6,11 @@ import 'package:args/command_runner.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/bundle.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/build_bundle.dart';
|
||||
import 'package:flutter_tools/src/bundle.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -70,7 +70,7 @@ void main() {
|
||||
final BuildBundleCommand command = await runCommandIn(projectPath);
|
||||
|
||||
expect(await command.usageValues,
|
||||
containsPair(kCommandBuildBundleIsModule, 'true'));
|
||||
containsPair(CustomDimensions.commandBuildBundleIsModule, 'true'));
|
||||
}, timeout: allowForCreateFlutterProject);
|
||||
|
||||
testUsingContext('bundle getUsage indicate that project is not a module', () async {
|
||||
@ -80,7 +80,7 @@ void main() {
|
||||
final BuildBundleCommand command = await runCommandIn(projectPath);
|
||||
|
||||
expect(await command.usageValues,
|
||||
containsPair(kCommandBuildBundleIsModule, 'false'));
|
||||
containsPair(CustomDimensions.commandBuildBundleIsModule, 'false'));
|
||||
}, timeout: allowForCreateFlutterProject);
|
||||
|
||||
testUsingContext('bundle getUsage indicate the target platform', () async {
|
||||
@ -90,7 +90,7 @@ void main() {
|
||||
final BuildBundleCommand command = await runCommandIn(projectPath);
|
||||
|
||||
expect(await command.usageValues,
|
||||
containsPair(kCommandBuildBundleTargetPlatform, 'android-arm'));
|
||||
containsPair(CustomDimensions.commandBuildBundleTargetPlatform, 'android-arm'));
|
||||
}, timeout: allowForCreateFlutterProject);
|
||||
|
||||
testUsingContext('bundle fails to build for Windows if feature is disabled', () async {
|
||||
|
@ -7,7 +7,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/create.dart';
|
||||
import 'package:flutter_tools/src/doctor.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
@ -43,25 +43,35 @@ void main() {
|
||||
final CreateCommand command = CreateCommand();
|
||||
final CommandRunner<void> runner = createTestCommandRunner(command);
|
||||
|
||||
await runner.run(<String>['create', '--flutter-root=flutter', '--no-pub', '--template=module', 'testy']);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'module'));
|
||||
await runner.run(<String>[
|
||||
'create', '--flutter-root=flutter', '--no-pub', '--template=module', 'testy']);
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateProjectType, 'module'));
|
||||
|
||||
await runner.run(<String>['create', '--flutter-root=flutter', '--no-pub', '--template=app', 'testy']);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'app'));
|
||||
await runner.run(<String>[
|
||||
'create', '--flutter-root=flutter', '--no-pub', '--template=app', 'testy']);
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateProjectType, 'app'));
|
||||
|
||||
await runner.run(<String>['create', '--flutter-root=flutter', '--no-pub', '--template=package', 'testy']);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'package'));
|
||||
await runner.run(<String>[
|
||||
'create', '--flutter-root=flutter', '--no-pub', '--template=package', 'testy']);
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateProjectType, 'package'));
|
||||
|
||||
await runner.run(<String>['create', '--flutter-root=flutter', '--no-pub', '--template=plugin', 'testy']);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'plugin'));
|
||||
await runner.run(<String>[
|
||||
'create', '--flutter-root=flutter', '--no-pub', '--template=plugin', 'testy']);
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateProjectType, 'plugin'));
|
||||
}));
|
||||
|
||||
test('set iOS host language type as usage value', () => testbed.run(() async {
|
||||
final CreateCommand command = CreateCommand();
|
||||
final CommandRunner<void> runner = createTestCommandRunner(command);
|
||||
|
||||
await runner.run(<String>['create', '--flutter-root=flutter', '--no-pub', '--template=app', 'testy']);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateIosLanguage, 'objc'));
|
||||
await runner.run(<String>[
|
||||
'create', '--flutter-root=flutter', '--no-pub', '--template=app', 'testy']);
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateIosLanguage, 'objc'));
|
||||
|
||||
await runner.run(<String>[
|
||||
'create',
|
||||
@ -71,7 +81,8 @@ void main() {
|
||||
'--ios-language=swift',
|
||||
'testy',
|
||||
]);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateIosLanguage, 'swift'));
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateIosLanguage, 'swift'));
|
||||
|
||||
}));
|
||||
|
||||
@ -80,7 +91,8 @@ void main() {
|
||||
final CommandRunner<void> runner = createTestCommandRunner(command);
|
||||
|
||||
await runner.run(<String>['create', '--flutter-root=flutter', '--no-pub', '--template=app', 'testy']);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateAndroidLanguage, 'java'));
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateAndroidLanguage, 'java'));
|
||||
|
||||
await runner.run(<String>[
|
||||
'create',
|
||||
@ -90,7 +102,8 @@ void main() {
|
||||
'--android-language=kotlin',
|
||||
'testy',
|
||||
]);
|
||||
expect(await command.usageValues, containsPair(kCommandCreateAndroidLanguage, 'kotlin'));
|
||||
expect(await command.usageValues,
|
||||
containsPair(CustomDimensions.commandCreateAndroidLanguage, 'kotlin'));
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
@ -16,9 +16,9 @@ import 'package:flutter_tools/src/base/user_messages.dart';
|
||||
import 'package:flutter_tools/src/doctor.dart';
|
||||
import 'package:flutter_tools/src/globals.dart';
|
||||
import 'package:flutter_tools/src/proxy_validator.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/vscode/vscode.dart';
|
||||
import 'package:flutter_tools/src/vscode/vscode_validator.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
|
@ -10,7 +10,7 @@ import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/utils.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/packages.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:process/process.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
@ -226,7 +226,8 @@ void main() {
|
||||
final PackagesCommand command = await runCommandIn(projectPath, 'get');
|
||||
final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;
|
||||
|
||||
expect(await getCommand.usageValues, containsPair(kCommandPackagesNumberPlugins, '0'));
|
||||
expect(await getCommand.usageValues,
|
||||
containsPair(CustomDimensions.commandPackagesNumberPlugins, '0'));
|
||||
}, timeout: allowForCreateFlutterProject);
|
||||
|
||||
testUsingContext('indicate that the project is not a module in usage value', () async {
|
||||
@ -237,7 +238,8 @@ void main() {
|
||||
final PackagesCommand command = await runCommandIn(projectPath, 'get');
|
||||
final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;
|
||||
|
||||
expect(await getCommand.usageValues, containsPair(kCommandPackagesProjectModule, 'false'));
|
||||
expect(await getCommand.usageValues,
|
||||
containsPair(CustomDimensions.commandPackagesProjectModule, 'false'));
|
||||
}, timeout: allowForCreateFlutterProject);
|
||||
|
||||
testUsingContext('indicate that the project is a module in usage value', () async {
|
||||
@ -248,7 +250,8 @@ void main() {
|
||||
final PackagesCommand command = await runCommandIn(projectPath, 'get');
|
||||
final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand;
|
||||
|
||||
expect(await getCommand.usageValues, containsPair(kCommandPackagesProjectModule, 'true'));
|
||||
expect(await getCommand.usageValues,
|
||||
containsPair(CustomDimensions.commandPackagesProjectModule, 'true'));
|
||||
}, timeout: allowForCreateFlutterProject);
|
||||
|
||||
testUsingContext('upgrade fetches packages', () async {
|
||||
|
@ -6,9 +6,9 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
|
@ -8,17 +8,16 @@ import 'dart:convert';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/local.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:http/testing.dart';
|
||||
|
||||
import 'package:flutter_tools/runner.dart' as tools;
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/reporting/crash_reporting.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'package:http/testing.dart';
|
||||
import 'package:pedantic/pedantic.dart';
|
||||
|
||||
import '../src/common.dart';
|
||||
|
@ -129,6 +129,7 @@ void main() {
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
when(mockDevice.supportsHotReload).thenReturn(true);
|
||||
when(mockDevice.supportsHotRestart).thenReturn(false);
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
||||
// Trigger hot restart.
|
||||
final List<FlutterDevice> devices = <FlutterDevice>[
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
|
||||
@ -190,6 +191,7 @@ void main() {
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
when(mockDevice.supportsHotReload).thenReturn(true);
|
||||
when(mockDevice.supportsHotRestart).thenReturn(true);
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
||||
final List<FlutterDevice> devices = <FlutterDevice>[
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug),
|
||||
];
|
||||
@ -206,6 +208,7 @@ void main() {
|
||||
final MockDevice mockDevice = MockDevice();
|
||||
when(mockDevice.supportsHotReload).thenReturn(true);
|
||||
when(mockDevice.supportsHotRestart).thenReturn(true);
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation _) async => TargetPlatform.tester);
|
||||
// Trigger hot restart.
|
||||
final List<FlutterDevice> devices = <FlutterDevice>[
|
||||
FlutterDevice(mockDevice, generator: residentCompiler, trackWidgetCreation: false, buildMode: BuildMode.debug)..devFS = mockDevFs,
|
||||
|
@ -5,14 +5,14 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult;
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/ios/mac.dart';
|
||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||
import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
import 'package:platform/platform.dart';
|
||||
import 'package:process/process.dart';
|
||||
@ -183,9 +183,10 @@ void main() {
|
||||
);
|
||||
|
||||
await diagnoseXcodeBuildFailure(buildResult);
|
||||
verify(mockUsage.sendEvent('Xcode', 'bitcode-failure', parameters: <String, String>{
|
||||
'build-commands': buildCommands.toString(),
|
||||
'build-settings': buildSettings.toString(),
|
||||
verify(mockUsage.sendEvent('build', 'xcode-bitcode-failure',
|
||||
parameters: <String, String>{
|
||||
cdKey(CustomDimensions.buildEventCommand): buildCommands.toString(),
|
||||
cdKey(CustomDimensions.buildEventSettings): buildSettings.toString(),
|
||||
})).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => mockUsage,
|
||||
|
@ -4,17 +4,17 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/flutter_manifest.dart';
|
||||
import 'package:flutter_tools/src/ios/ios_workflow.dart';
|
||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
|
@ -11,7 +11,7 @@ import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/devfs.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/resident_runner.dart';
|
||||
import 'package:flutter_tools/src/run_hot.dart';
|
||||
import 'package:flutter_tools/src/vmservice.dart';
|
||||
@ -53,6 +53,7 @@ void main() {
|
||||
when(mockDevFS.lastCompiled).thenReturn(DateTime(2000));
|
||||
when(mockDevFS.sources).thenReturn(<Uri>[]);
|
||||
when(mockDevFS.destroy()).thenAnswer((Invocation invocation) async { });
|
||||
when(mockDevFS.assetPathsToEvict).thenReturn(<String>{});
|
||||
// FlutterDevice Mocks.
|
||||
when(mockFlutterDevice.updateDevFS(
|
||||
// Intentionally provide empty list to match above mock.
|
||||
@ -96,6 +97,19 @@ void main() {
|
||||
mockVMService,
|
||||
]);
|
||||
when(mockFlutterDevice.refreshViews()).thenAnswer((Invocation invocation) async { });
|
||||
when(mockFlutterDevice.reloadSources(any, pause: anyNamed('pause'))).thenReturn(<Future<Map<String, dynamic>>>[
|
||||
Future<Map<String, dynamic>>.value(<String, dynamic>{
|
||||
'type': 'ReloadReport',
|
||||
'success': true,
|
||||
'details': <String, dynamic>{
|
||||
'loadedLibraryCount': 1,
|
||||
'finalLibraryCount': 1,
|
||||
'receivedLibraryCount': 1,
|
||||
'receivedClassesCount': 1,
|
||||
'receivedProceduresCount': 1,
|
||||
},
|
||||
}),
|
||||
]);
|
||||
// VMService mocks.
|
||||
when(mockVMService.wsAddress).thenReturn(testUri);
|
||||
when(mockVMService.done).thenAnswer((Invocation invocation) {
|
||||
@ -108,6 +122,9 @@ void main() {
|
||||
when(mockIsolate.flutterExit()).thenAnswer((Invocation invocation) {
|
||||
return Future<Map<String, Object>>.value(null);
|
||||
});
|
||||
when(mockIsolate.reload()).thenAnswer((Invocation invocation) {
|
||||
return Future<ServiceObject>.value(null);
|
||||
});
|
||||
});
|
||||
|
||||
test('ResidentRunner can attach to device successfully', () => testbed.run(() async {
|
||||
@ -162,15 +179,48 @@ void main() {
|
||||
expect(result.fatal, true);
|
||||
expect(result.code, 1);
|
||||
verify(flutterUsage.sendEvent('hot', 'exception', parameters: <String, String>{
|
||||
reloadExceptionTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
|
||||
reloadExceptionSdkName: 'Example',
|
||||
reloadExceptionEmulator: 'false',
|
||||
reloadExceptionFullRestart: 'false',
|
||||
cdKey(CustomDimensions.hotEventTargetPlatform):
|
||||
getNameForTargetPlatform(TargetPlatform.android_arm),
|
||||
cdKey(CustomDimensions.hotEventSdkName): 'Example',
|
||||
cdKey(CustomDimensions.hotEventEmulator): 'false',
|
||||
cdKey(CustomDimensions.hotEventFullRestart): 'false',
|
||||
})).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockUsage(),
|
||||
}));
|
||||
|
||||
|
||||
// Need one for hot restart as well.
|
||||
|
||||
test('ResidentRunner can send target platform to analytics from hot reload', () => testbed.run(() async {
|
||||
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
|
||||
return 'Example';
|
||||
});
|
||||
when(mockDevice.targetPlatform).thenAnswer((Invocation invocation) async {
|
||||
return TargetPlatform.android_arm;
|
||||
});
|
||||
when(mockDevice.isLocalEmulator).thenAnswer((Invocation invocation) async {
|
||||
return false;
|
||||
});
|
||||
final Completer<DebugConnectionInfo> onConnectionInfo = Completer<DebugConnectionInfo>.sync();
|
||||
final Completer<void> onAppStart = Completer<void>.sync();
|
||||
unawaited(residentRunner.attach(
|
||||
appStartedCompleter: onAppStart,
|
||||
connectionInfoCompleter: onConnectionInfo,
|
||||
));
|
||||
|
||||
final OperationResult result = await residentRunner.restart(fullRestart: false);
|
||||
expect(result.fatal, false);
|
||||
expect(result.code, 0);
|
||||
expect(verify(flutterUsage.sendEvent('hot', 'reload',
|
||||
parameters: captureAnyNamed('parameters'))).captured[0],
|
||||
containsPair(cdKey(CustomDimensions.hotEventTargetPlatform),
|
||||
getNameForTargetPlatform(TargetPlatform.android_arm))
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockUsage(),
|
||||
}));
|
||||
|
||||
test('ResidentRunner Can handle an RPC exception from hot restart', () => testbed.run(() async {
|
||||
when(mockDevice.sdkNameAndVersion).thenAnswer((Invocation invocation) async {
|
||||
return 'Example';
|
||||
@ -206,10 +256,11 @@ void main() {
|
||||
expect(result.fatal, true);
|
||||
expect(result.code, 1);
|
||||
verify(flutterUsage.sendEvent('hot', 'exception', parameters: <String, String>{
|
||||
reloadExceptionTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
|
||||
reloadExceptionSdkName: 'Example',
|
||||
reloadExceptionEmulator: 'false',
|
||||
reloadExceptionFullRestart: 'true',
|
||||
cdKey(CustomDimensions.hotEventTargetPlatform):
|
||||
getNameForTargetPlatform(TargetPlatform.android_arm),
|
||||
cdKey(CustomDimensions.hotEventSdkName): 'Example',
|
||||
cdKey(CustomDimensions.hotEventEmulator): 'false',
|
||||
cdKey(CustomDimensions.hotEventFullRestart): 'true',
|
||||
})).called(1);
|
||||
}, overrides: <Type, Generator>{
|
||||
Usage: () => MockUsage(),
|
||||
|
@ -2,10 +2,10 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:flutter_tools/src/version.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
@ -5,7 +5,7 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
||||
|
@ -5,9 +5,6 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:args/command_runner.dart';
|
||||
import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf;
|
||||
import 'package:test_api/test_api.dart' as test_package show TypeMatcher;
|
||||
|
||||
import 'package:flutter_tools/src/base/common.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
@ -15,6 +12,8 @@ import 'package:flutter_tools/src/base/process.dart';
|
||||
import 'package:flutter_tools/src/commands/create.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
|
||||
import 'package:test_api/test_api.dart' as test_package show TypeMatcher;
|
||||
import 'package:test_api/test_api.dart' hide TypeMatcher, isInstanceOf;
|
||||
|
||||
export 'package:test_core/test_core.dart' hide TypeMatcher, isInstanceOf; // Defines a 'package:test' shim.
|
||||
|
||||
|
@ -13,15 +13,15 @@ import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/context_runner.dart';
|
||||
import 'package:flutter_tools/src/device.dart';
|
||||
import 'package:flutter_tools/src/doctor.dart';
|
||||
import 'package:flutter_tools/src/ios/simulators.dart';
|
||||
import 'package:flutter_tools/src/ios/xcodeproj.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/version.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:mockito/mockito.dart';
|
||||
|
@ -8,17 +8,17 @@ import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/context.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/context_runner.dart';
|
||||
import 'package:flutter_tools/src/features.dart';
|
||||
import 'package:flutter_tools/src/reporting/usage.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/version.dart';
|
||||
|
||||
import 'context.dart';
|
||||
|
Loading…
x
Reference in New Issue
Block a user