Skip to content

Commit 30a9f99

Browse files
authored
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.
1 parent c627dbf commit 30a9f99

File tree

4 files changed

+148
-1
lines changed

4 files changed

+148
-1
lines changed

packages/flutter_tools/lib/src/commands/build_ios.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import '../globals.dart' as globals;
2020
import '../ios/application_package.dart';
2121
import '../ios/mac.dart';
2222
import '../ios/plist_parser.dart';
23+
import '../reporting/reporting.dart';
2324
import '../runner/flutter_command.dart';
2425
import 'build.dart';
2526

@@ -724,6 +725,21 @@ abstract class _BuildIOSSubCommand extends BuildSubCommand {
724725
if (result.output != null) {
725726
globals.printStatus('Built ${result.output}.');
726727

728+
// When an app is successfully built, record to analytics whether Impeller
729+
// is enabled or disabled.
730+
final BuildableIOSApp app = await buildableIOSApp;
731+
final String plistPath = app.project.infoPlist.path;
732+
final bool? impellerEnabled = globals.plistParser.getValueFromFile<bool>(
733+
plistPath, PlistParser.kFLTEnableImpellerKey,
734+
);
735+
BuildEvent(
736+
impellerEnabled == false
737+
? 'plist-impeller-disabled'
738+
: 'plist-impeller-enabled',
739+
type: 'ios',
740+
flutterUsage: globals.flutterUsage,
741+
).send();
742+
727743
return FlutterCommandResult.success();
728744
}
729745

packages/flutter_tools/lib/src/ios/plist_parser.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class PlistParser {
3131
static const String kCFBundleVersionKey = 'CFBundleVersion';
3232
static const String kCFBundleDisplayNameKey = 'CFBundleDisplayName';
3333
static const String kCFBundleNameKey = 'CFBundleName';
34+
static const String kFLTEnableImpellerKey = 'FLTEnableImpeller';
3435
static const String kMinimumOSVersionKey = 'MinimumOSVersion';
3536
static const String kNSPrincipalClassKey = 'NSPrincipalClass';
3637

packages/flutter_tools/test/commands.shard/hermetic/build_ios_test.dart

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ import 'package:flutter_tools/src/commands/build.dart';
1717
import 'package:flutter_tools/src/commands/build_ios.dart';
1818
import 'package:flutter_tools/src/ios/code_signing.dart';
1919
import 'package:flutter_tools/src/ios/mac.dart';
20+
import 'package:flutter_tools/src/ios/plist_parser.dart';
2021
import 'package:flutter_tools/src/ios/xcodeproj.dart';
22+
import 'package:flutter_tools/src/project.dart';
2123
import 'package:flutter_tools/src/reporting/reporting.dart';
2224
import 'package:test/fake.dart';
2325

@@ -438,6 +440,133 @@ void main() {
438440
Usage: () => usage,
439441
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
440442
});
443+
444+
group('Analytics for impeller plist setting', () {
445+
const String plistContents = '''
446+
<?xml version="1.0" encoding="UTF-8"?>
447+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
448+
<plist version="1.0">
449+
<dict>
450+
<key>FLTEnableImpeller</key>
451+
<false/>
452+
</dict>
453+
</plist>
454+
''';
455+
const FakeCommand plutilCommand = FakeCommand(
456+
command: <String>[
457+
'/usr/bin/plutil', '-convert', 'xml1', '-o', '-', '/ios/Runner/Info.plist',
458+
],
459+
stdout: plistContents,
460+
);
461+
462+
testUsingContext('Sends an analytics event when Impeller is enabled', () async {
463+
final BuildCommand command = BuildCommand(
464+
androidSdk: FakeAndroidSdk(),
465+
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
466+
fileSystem: MemoryFileSystem.test(),
467+
logger: BufferLogger.test(),
468+
osUtils: FakeOperatingSystemUtils(),
469+
);
470+
createMinimalMockProjectFiles();
471+
472+
await createTestCommandRunner(command).run(
473+
const <String>['build', 'ios', '--no-pub']
474+
);
475+
476+
expect(usage.events, contains(
477+
const TestUsageEvent(
478+
'build', 'ios',
479+
label:'plist-impeller-enabled',
480+
parameters:CustomDimensions(),
481+
),
482+
));
483+
}, overrides: <Type, Generator>{
484+
FileSystem: () => fileSystem,
485+
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
486+
xattrCommand,
487+
setUpFakeXcodeBuildHandler(onRun: () {
488+
fileSystem.directory('build/ios/Release-iphoneos/Runner.app')
489+
.createSync(recursive: true);
490+
}),
491+
setUpRsyncCommand(onRun: () =>
492+
fileSystem.file('build/ios/iphoneos/Runner.app/Frameworks/App.framework/App')
493+
..createSync(recursive: true)
494+
..writeAsBytesSync(List<int>.generate(10000, (int index) => 0))),
495+
]),
496+
Platform: () => macosPlatform,
497+
FileSystemUtils: () => FileSystemUtils(
498+
fileSystem: fileSystem,
499+
platform: macosPlatform,
500+
),
501+
Usage: () => usage,
502+
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
503+
});
504+
505+
testUsingContext('Sends an analytics event when Impeller is disabled', () async {
506+
final BuildCommand command = BuildCommand(
507+
androidSdk: FakeAndroidSdk(),
508+
buildSystem: TestBuildSystem.all(BuildResult(success: true)),
509+
fileSystem: fileSystem,
510+
logger: BufferLogger.test(),
511+
osUtils: FakeOperatingSystemUtils(),
512+
);
513+
createMinimalMockProjectFiles();
514+
515+
fileSystem.file(
516+
fileSystem.path.join('usr', 'bin', 'plutil'),
517+
).createSync(recursive: true);
518+
519+
final File infoPlist = fileSystem.file(fileSystem.path.join(
520+
'ios', 'Runner', 'Info.plist',
521+
))..createSync(recursive: true);
522+
523+
infoPlist.writeAsStringSync(plistContents);
524+
525+
await createTestCommandRunner(command).run(
526+
const <String>['build', 'ios', '--no-pub']
527+
);
528+
529+
expect(usage.events, contains(
530+
const TestUsageEvent(
531+
'build', 'ios',
532+
label:'plist-impeller-disabled',
533+
parameters:CustomDimensions(),
534+
),
535+
));
536+
}, overrides: <Type, Generator>{
537+
FileSystem: () => fileSystem,
538+
ProcessManager: () => FakeProcessManager.list(<FakeCommand>[
539+
xattrCommand,
540+
setUpFakeXcodeBuildHandler(onRun: () {
541+
fileSystem.directory('build/ios/Release-iphoneos/Runner.app')
542+
.createSync(recursive: true);
543+
}),
544+
setUpRsyncCommand(onRun: () =>
545+
fileSystem.file('build/ios/iphoneos/Runner.app/Frameworks/App.framework/App')
546+
..createSync(recursive: true)
547+
..writeAsBytesSync(List<int>.generate(10000, (int index) => 0))),
548+
]),
549+
Platform: () => macosPlatform,
550+
FileSystemUtils: () => FileSystemUtils(
551+
fileSystem: fileSystem,
552+
platform: macosPlatform,
553+
),
554+
Usage: () => usage,
555+
XcodeProjectInterpreter: () => FakeXcodeProjectInterpreterWithBuildSettings(),
556+
FlutterProjectFactory: () => FlutterProjectFactory(
557+
fileSystem: fileSystem,
558+
logger: BufferLogger.test(),
559+
),
560+
PlistParser: () => PlistParser(
561+
fileSystem: fileSystem,
562+
logger: BufferLogger.test(),
563+
processManager: FakeProcessManager.list(<FakeCommand>[
564+
plutilCommand, plutilCommand, plutilCommand,
565+
]),
566+
),
567+
});
568+
});
569+
441570
group('xcresults device', () {
442571
testUsingContext('Trace error if xcresult is empty.', () async {
443572
final BuildCommand command = BuildCommand(

packages/flutter_tools/test/commands.shard/hermetic/build_ipa_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ class FakePlistUtils extends Fake implements PlistParser {
6363

6464
@override
6565
T? getValueFromFile<T>(String plistFilePath, String key) {
66-
return fileContents[plistFilePath]![key] as T?;
66+
final Map<String, Object>? plistFile = fileContents[plistFilePath];
67+
return plistFile == null ? null : plistFile[key] as T?;
6768
}
6869
}
6970

0 commit comments

Comments
 (0)