Unverified Commit 30a9f99b authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

Send analytics on 'build ios' and 'build ipa' for plist impeller value (#135193)

This analytics event only records the value of the plist entry on
`build` commands. This will give an idea of the proportion of users who
are disabling Impeller when shipping apps.
parent c627dbfb
...@@ -20,6 +20,7 @@ import '../globals.dart' as globals; ...@@ -20,6 +20,7 @@ import '../globals.dart' as globals;
import '../ios/application_package.dart'; import '../ios/application_package.dart';
import '../ios/mac.dart'; import '../ios/mac.dart';
import '../ios/plist_parser.dart'; import '../ios/plist_parser.dart';
import '../reporting/reporting.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import 'build.dart'; import 'build.dart';
...@@ -724,6 +725,21 @@ abstract class _BuildIOSSubCommand extends BuildSubCommand { ...@@ -724,6 +725,21 @@ abstract class _BuildIOSSubCommand extends BuildSubCommand {
if (result.output != null) { if (result.output != null) {
globals.printStatus('Built ${result.output}.'); globals.printStatus('Built ${result.output}.');
// When an app is successfully built, record to analytics whether Impeller
// is enabled or disabled.
final BuildableIOSApp app = await buildableIOSApp;
final String plistPath = app.project.infoPlist.path;
final bool? impellerEnabled = globals.plistParser.getValueFromFile<bool>(
plistPath, PlistParser.kFLTEnableImpellerKey,
);
BuildEvent(
impellerEnabled == false
? 'plist-impeller-disabled'
: 'plist-impeller-enabled',
type: 'ios',
flutterUsage: globals.flutterUsage,
).send();
return FlutterCommandResult.success(); return FlutterCommandResult.success();
} }
......
...@@ -31,6 +31,7 @@ class PlistParser { ...@@ -31,6 +31,7 @@ class PlistParser {
static const String kCFBundleVersionKey = 'CFBundleVersion'; static const String kCFBundleVersionKey = 'CFBundleVersion';
static const String kCFBundleDisplayNameKey = 'CFBundleDisplayName'; static const String kCFBundleDisplayNameKey = 'CFBundleDisplayName';
static const String kCFBundleNameKey = 'CFBundleName'; static const String kCFBundleNameKey = 'CFBundleName';
static const String kFLTEnableImpellerKey = 'FLTEnableImpeller';
static const String kMinimumOSVersionKey = 'MinimumOSVersion'; static const String kMinimumOSVersionKey = 'MinimumOSVersion';
static const String kNSPrincipalClassKey = 'NSPrincipalClass'; static const String kNSPrincipalClassKey = 'NSPrincipalClass';
......
...@@ -17,7 +17,9 @@ import 'package:flutter_tools/src/commands/build.dart'; ...@@ -17,7 +17,9 @@ import 'package:flutter_tools/src/commands/build.dart';
import 'package:flutter_tools/src/commands/build_ios.dart'; import 'package:flutter_tools/src/commands/build_ios.dart';
import 'package:flutter_tools/src/ios/code_signing.dart'; import 'package:flutter_tools/src/ios/code_signing.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/plist_parser.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
...@@ -438,6 +440,133 @@ void main() { ...@@ -438,6 +440,133 @@ void main() {
Usage: () => usage, Usage: () => usage,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(), XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
}); });
group('Analytics for impeller plist setting', () {
const String plistContents = '''
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>FLTEnableImpeller</key>
<false/>
</dict>
</plist>
''';
const FakeCommand plutilCommand = FakeCommand(
command: <String>[
'/usr/bin/plutil', '-convert', 'xml1', '-o', '-', '/ios/Runner/Info.plist',
],
stdout: plistContents,
);
testUsingContext('Sends an analytics event when Impeller is enabled', () async {
final BuildCommand command = BuildCommand(
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: MemoryFileSystem.test(),
logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
await createTestCommandRunner(command).run(
const <String>['build', 'ios', '--no-pub']
);
expect(usage.events, contains(
const TestUsageEvent(
'build', 'ios',
label:'plist-impeller-enabled',
parameters:CustomDimensions(),
),
));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(onRun: () {
fileSystem.directory('build/ios/Release-iphoneos/Runner.app')
.createSync(recursive: true);
}),
setUpRsyncCommand(onRun: () =>
fileSystem.file('build/ios/iphoneos/Runner.app/Frameworks/App.framework/App')
..createSync(recursive: true)
..writeAsBytesSync(List<int>.generate(10000, (int index) => 0))),
]),
Platform: () => macosPlatform,
FileSystemUtils: () => FileSystemUtils(
fileSystem: fileSystem,
platform: macosPlatform,
),
Usage: () => usage,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
});
testUsingContext('Sends an analytics event when Impeller is disabled', () async {
final BuildCommand command = BuildCommand(
androidSdk: FakeAndroidSdk(),
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
fileSystem: fileSystem,
logger: BufferLogger.test(),
osUtils: FakeOperatingSystemUtils(),
);
createMinimalMockProjectFiles();
fileSystem.file(
fileSystem.path.join('usr', 'bin', 'plutil'),
).createSync(recursive: true);
final File infoPlist = fileSystem.file(fileSystem.path.join(
'ios', 'Runner', 'Info.plist',
))..createSync(recursive: true);
infoPlist.writeAsStringSync(plistContents);
await createTestCommandRunner(command).run(
const <String>['build', 'ios', '--no-pub']
);
expect(usage.events, contains(
const TestUsageEvent(
'build', 'ios',
label:'plist-impeller-disabled',
parameters:CustomDimensions(),
),
));
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
xattrCommand,
setUpFakeXcodeBuildHandler(onRun: () {
fileSystem.directory('build/ios/Release-iphoneos/Runner.app')
.createSync(recursive: true);
}),
setUpRsyncCommand(onRun: () =>
fileSystem.file('build/ios/iphoneos/Runner.app/Frameworks/App.framework/App')
..createSync(recursive: true)
..writeAsBytesSync(List<int>.generate(10000, (int index) => 0))),
]),
Platform: () => macosPlatform,
FileSystemUtils: () => FileSystemUtils(
fileSystem: fileSystem,
platform: macosPlatform,
),
Usage: () => usage,
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
FlutterProjectFactory: () => FlutterProjectFactory(
fileSystem: fileSystem,
logger: BufferLogger.test(),
),
PlistParser: () => PlistParser(
fileSystem: fileSystem,
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
plutilCommand, plutilCommand, plutilCommand,
]),
),
});
});
group('xcresults device', () { group('xcresults device', () {
testUsingContext('Trace error if xcresult is empty.', () async { testUsingContext('Trace error if xcresult is empty.', () async {
final BuildCommand command = BuildCommand( final BuildCommand command = BuildCommand(
......
...@@ -63,7 +63,8 @@ class FakePlistUtils extends Fake implements PlistParser { ...@@ -63,7 +63,8 @@ class FakePlistUtils extends Fake implements PlistParser {
@override @override
T? getValueFromFile<T>(String plistFilePath, String key) { T? getValueFromFile<T>(String plistFilePath, String key) {
return fileContents[plistFilePath]![key] as T?; final Map<String, Object>? plistFile = fileContents[plistFilePath];
return plistFile == null ? null : plistFile[key] as T?;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment