[go: nahoru, domu]

Skip to content

Commit

Permalink
Add usage event to track when a iOS network device is used (#118915)
Browse files Browse the repository at this point in the history
* Add usage event to track when a iOS network device is used

* update usage event to track percentage of iOS network vs usb devices, update and fix tests

* refactor tracking to happen in usageValues with a custom dimension
  • Loading branch information
vashworth committed Jan 25, 2023
1 parent e85547b commit 81052a7
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 4 deletions.
22 changes: 19 additions & 3 deletions packages/flutter_tools/lib/src/commands/run.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import '../daemon.dart';
import '../device.dart';
import '../features.dart';
import '../globals.dart' as globals;
import '../ios/devices.dart';
import '../ios/iproxy.dart';
import '../project.dart';
import '../reporting/reporting.dart';
import '../resident_runner.dart';
Expand Down Expand Up @@ -407,18 +409,23 @@ class RunCommand extends RunCommandBase {
bool isEmulator;
bool anyAndroidDevices = false;
bool anyIOSDevices = false;
bool anyIOSNetworkDevices = false;

if (devices == null || devices!.isEmpty) {
deviceType = 'none';
deviceOsVersion = 'none';
isEmulator = false;
} else if (devices!.length == 1) {
final TargetPlatform platform = await devices![0].targetPlatform;
final Device device = devices![0];
final TargetPlatform platform = await device.targetPlatform;
anyAndroidDevices = platform == TargetPlatform.android;
anyIOSDevices = platform == TargetPlatform.ios;
if (device is IOSDevice && device.interfaceType == IOSDeviceConnectionInterface.network) {
anyIOSNetworkDevices = true;
}
deviceType = getNameForTargetPlatform(platform);
deviceOsVersion = await devices![0].sdkNameAndVersion;
isEmulator = await devices![0].isLocalEmulator;
deviceOsVersion = await device.sdkNameAndVersion;
isEmulator = await device.isLocalEmulator;
} else {
deviceType = 'multiple';
deviceOsVersion = 'multiple';
Expand All @@ -427,12 +434,20 @@ class RunCommand extends RunCommandBase {
final TargetPlatform platform = await device.targetPlatform;
anyAndroidDevices = anyAndroidDevices || (platform == TargetPlatform.android);
anyIOSDevices = anyIOSDevices || (platform == TargetPlatform.ios);
if (device is IOSDevice && device.interfaceType == IOSDeviceConnectionInterface.network) {
anyIOSNetworkDevices = true;
}
if (anyAndroidDevices && anyIOSDevices) {
break;
}
}
}

String? iOSInterfaceType;
if (anyIOSDevices) {
iOSInterfaceType = anyIOSNetworkDevices ? 'wireless' : 'usb';
}

String? androidEmbeddingVersion;
final List<String> hostLanguage = <String>[];
if (anyAndroidDevices) {
Expand Down Expand Up @@ -464,6 +479,7 @@ class RunCommand extends RunCommandBase {
commandRunProjectHostLanguage: hostLanguage.join(','),
commandRunAndroidEmbeddingVersion: androidEmbeddingVersion,
commandRunEnableImpeller: enableImpeller,
commandRunIOSInterfaceType: iOSInterfaceType,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ class CustomDimensions {
this.hotEventReassembleTimeInMs,
this.hotEventReloadVMTimeInMs,
this.commandRunEnableImpeller,
this.commandRunIOSInterfaceType,
});

final String? sessionHostOsDetails; // cd1
Expand Down Expand Up @@ -125,6 +126,7 @@ class CustomDimensions {
final int? hotEventReassembleTimeInMs; // cd 54
final int? hotEventReloadVMTimeInMs; // cd 55
final bool? commandRunEnableImpeller; // cd 56
final String? commandRunIOSInterfaceType; // cd 57

/// Convert to a map that will be used to upload to the analytics backend.
Map<String, String> toMap() => <String, String>{
Expand Down Expand Up @@ -184,6 +186,7 @@ class CustomDimensions {
if (hotEventReassembleTimeInMs != null) cdKey(CustomDimensionsEnum.hotEventReassembleTimeInMs): hotEventReassembleTimeInMs.toString(),
if (hotEventReloadVMTimeInMs != null) cdKey(CustomDimensionsEnum.hotEventReloadVMTimeInMs): hotEventReloadVMTimeInMs.toString(),
if (commandRunEnableImpeller != null) cdKey(CustomDimensionsEnum.commandRunEnableImpeller): commandRunEnableImpeller.toString(),
if (commandRunIOSInterfaceType != null) cdKey(CustomDimensionsEnum.commandRunIOSInterfaceType): commandRunIOSInterfaceType.toString(),
};

/// Merge the values of two [CustomDimensions] into one. If a value is defined
Expand Down Expand Up @@ -250,6 +253,7 @@ class CustomDimensions {
hotEventReassembleTimeInMs: other.hotEventReassembleTimeInMs ?? hotEventReassembleTimeInMs,
hotEventReloadVMTimeInMs: other.hotEventReloadVMTimeInMs ?? hotEventReloadVMTimeInMs,
commandRunEnableImpeller: other.commandRunEnableImpeller ?? commandRunEnableImpeller,
commandRunIOSInterfaceType: other.commandRunIOSInterfaceType ?? commandRunIOSInterfaceType,
);
}

Expand Down Expand Up @@ -310,6 +314,7 @@ class CustomDimensions {
hotEventReassembleTimeInMs: _extractInt(map, CustomDimensionsEnum.hotEventReassembleTimeInMs),
hotEventReloadVMTimeInMs: _extractInt(map, CustomDimensionsEnum.hotEventReloadVMTimeInMs),
commandRunEnableImpeller: _extractBool(map, CustomDimensionsEnum.commandRunEnableImpeller),
commandRunIOSInterfaceType: _extractString(map, CustomDimensionsEnum.commandRunIOSInterfaceType),
);

static bool? _extractBool(Map<String, String> map, CustomDimensionsEnum field) =>
Expand Down Expand Up @@ -396,6 +401,7 @@ enum CustomDimensionsEnum {
hotEventReassembleTimeInMs, // cd54
hotEventReloadVMTimeInMs, // cd55
commandRunEnableImpeller, // cd56
commandRunIOSInterfaceType, // cd57
}

String cdKey(CustomDimensionsEnum cd) => 'cd${cd.index + 1}';
145 changes: 144 additions & 1 deletion packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import 'package:flutter_tools/src/commands/run.dart';
import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/resident_runner.dart';
Expand Down Expand Up @@ -426,7 +428,7 @@ void main() {
TestUsageCommand('run', parameters: CustomDimensions.fromMap(<String, String>{
'cd3': 'false', 'cd4': 'ios', 'cd22': 'iOS 13',
'cd23': 'debug', 'cd18': 'false', 'cd15': 'swift', 'cd31': 'true',
'cd56': 'false',
'cd56': 'false', 'cd57': 'usb',
})
)));
}, overrides: <Type, Generator>{
Expand Down Expand Up @@ -664,6 +666,104 @@ void main() {
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});

group('usageValues', () {
testUsingContext('with only non-iOS usb device', () async {
final List<Device> devices = <Device>[
FakeDevice(targetPlatform: TargetPlatform.android_arm, platformType: PlatformType.android),
];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CustomDimensions dimensions = await command.usageValues;

expect(dimensions, const CustomDimensions(
commandRunIsEmulator: false,
commandRunTargetName: 'android-arm',
commandRunTargetOsVersion: '',
commandRunModeName: 'debug',
commandRunProjectModule: false,
commandRunProjectHostLanguage: '',
commandRunEnableImpeller: false,
));
}, overrides: <Type, Generator>{
DeviceManager: () => testDeviceManager,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});

testUsingContext('with only iOS usb device', () async {
final List<Device> devices = <Device>[
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.usb, sdkNameAndVersion: 'iOS 16.2'),
];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CustomDimensions dimensions = await command.usageValues;

expect(dimensions, const CustomDimensions(
commandRunIsEmulator: false,
commandRunTargetName: 'ios',
commandRunTargetOsVersion: 'iOS 16.2',
commandRunModeName: 'debug',
commandRunProjectModule: false,
commandRunProjectHostLanguage: '',
commandRunEnableImpeller: false,
commandRunIOSInterfaceType: 'usb',
));
}, overrides: <Type, Generator>{
DeviceManager: () => testDeviceManager,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});

testUsingContext('with only iOS network device', () async {
final List<Device> devices = <Device>[
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.network, sdkNameAndVersion: 'iOS 16.2'),
];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CustomDimensions dimensions = await command.usageValues;

expect(dimensions, const CustomDimensions(
commandRunIsEmulator: false,
commandRunTargetName: 'ios',
commandRunTargetOsVersion: 'iOS 16.2',
commandRunModeName: 'debug',
commandRunProjectModule: false,
commandRunProjectHostLanguage: '',
commandRunEnableImpeller: false,
commandRunIOSInterfaceType: 'wireless',
));
}, overrides: <Type, Generator>{
DeviceManager: () => testDeviceManager,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});

testUsingContext('with both iOS usb and network devices', () async {
final List<Device> devices = <Device>[
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.network, sdkNameAndVersion: 'iOS 16.2'),
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.usb, sdkNameAndVersion: 'iOS 16.2'),
];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CustomDimensions dimensions = await command.usageValues;

expect(dimensions, const CustomDimensions(
commandRunIsEmulator: false,
commandRunTargetName: 'multiple',
commandRunTargetOsVersion: 'multiple',
commandRunModeName: 'debug',
commandRunProjectModule: false,
commandRunProjectHostLanguage: '',
commandRunEnableImpeller: false,
commandRunIOSInterfaceType: 'wireless',
));
}, overrides: <Type, Generator>{
DeviceManager: () => testDeviceManager,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
});
});
});

group('dart-defines and web-renderer options', () {
Expand Down Expand Up @@ -1032,6 +1132,49 @@ class FakeDevice extends Fake implements Device {
}
}

// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
class FakeIOSDevice extends Fake implements IOSDevice {
FakeIOSDevice({
this.interfaceType = IOSDeviceConnectionInterface.none,
bool isLocalEmulator = false,
String sdkNameAndVersion = '',
}): _isLocalEmulator = isLocalEmulator,
_sdkNameAndVersion = sdkNameAndVersion;

final bool _isLocalEmulator;
final String _sdkNameAndVersion;

@override
Future<bool> get isLocalEmulator => Future<bool>.value(_isLocalEmulator);

@override
Future<String> get sdkNameAndVersion => Future<String>.value(_sdkNameAndVersion);

@override
final IOSDeviceConnectionInterface interfaceType;

@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
}

class TestRunCommandForUsageValues extends RunCommand {
TestRunCommandForUsageValues({
this.devices,
});

@override
// devices is not set within usageValues, so we override the field
// ignore: overridden_fields
List<Device>? devices;

@override
Future<BuildInfo> getBuildInfo({ BuildMode? forcedBuildMode, File? forcedTargetFile }) async {
return const BuildInfo(BuildMode.debug, null, treeShakeIcons: false);
}
}

class TestRunCommandWithFakeResidentRunner extends RunCommand {
late FakeResidentRunner fakeResidentRunner;

Expand Down

0 comments on commit 81052a7

Please sign in to comment.