Skip to content

Commit

Permalink
Send analytics on 'build ios' and 'build ipa' for plist impeller valu…
Browse files Browse the repository at this point in the history
…e (#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.
  • Loading branch information
zanderso authored Sep 21, 2023
1 parent c627dbf commit 30a9f99
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
16 changes: 16 additions & 0 deletions packages/flutter_tools/lib/src/commands/build_ios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import '../globals.dart' as globals;
import '../ios/application_package.dart';
import '../ios/mac.dart';
import '../ios/plist_parser.dart';
import '../reporting/reporting.dart';
import '../runner/flutter_command.dart';
import 'build.dart';

Expand Down Expand Up @@ -724,6 +725,21 @@ abstract class _BuildIOSSubCommand extends BuildSubCommand {
if (result.output != null) {
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();
}

Expand Down
1 change: 1 addition & 0 deletions packages/flutter_tools/lib/src/ios/plist_parser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class PlistParser {
static const String kCFBundleVersionKey = 'CFBundleVersion';
static const String kCFBundleDisplayNameKey = 'CFBundleDisplayName';
static const String kCFBundleNameKey = 'CFBundleName';
static const String kFLTEnableImpellerKey = 'FLTEnableImpeller';
static const String kMinimumOSVersionKey = 'MinimumOSVersion';
static const String kNSPrincipalClassKey = 'NSPrincipalClass';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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/ios/code_signing.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/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:test/fake.dart';

Expand Down Expand Up @@ -438,6 +440,133 @@ void main() {
Usage: () => usage,
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', () {
testUsingContext('Trace error if xcresult is empty.', () async {
final BuildCommand command = BuildCommand(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class FakePlistUtils extends Fake implements PlistParser {

@override
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?;
}
}

Expand Down

0 comments on commit 30a9f99

Please sign in to comment.