Add to app measurement (#33458)
diff --git a/packages/flutter_tools/test/commands/create_test.dart b/packages/flutter_tools/test/commands/create_test.dart index 4b1dd0f..b5453cb 100644 --- a/packages/flutter_tools/test/commands/create_test.dart +++ b/packages/flutter_tools/test/commands/create_test.dart
@@ -14,13 +14,16 @@ import 'package:flutter_tools/src/commands/create.dart'; import 'package:flutter_tools/src/dart/sdk.dart'; import 'package:flutter_tools/src/project.dart'; +import 'package:flutter_tools/src/usage.dart'; import 'package:flutter_tools/src/version.dart'; + import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; import '../src/common.dart'; import '../src/context.dart'; + const String frameworkRevision = '12345678'; const String frameworkChannel = 'omega'; final Generator _kNoColorTerminalPlatform = () => FakePlatform.fromPlatform(const LocalPlatform())..stdoutSupportsAnsi = false; @@ -927,8 +930,71 @@ HttpClientFactory: () => () => MockHttpClient(404, result: 'not found'), }); + + group('usageValues', () { + testUsingContext('set template type as usage value', () async { + Cache.flutterRoot = '../..'; + + final CreateCommand command = CreateCommand(); + final CommandRunner<void> runner = createTestCommandRunner(command); + + await runner.run(<String>['create', '--no-pub', '--template=module', projectDir.path]); + expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'module')); + + await runner.run(<String>['create', '--no-pub', '--template=app', projectDir.path]); + expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'app')); + + await runner.run(<String>['create', '--no-pub', '--template=package', projectDir.path]); + expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'package')); + + await runner.run(<String>['create', '--no-pub', '--template=plugin', projectDir.path]); + expect(await command.usageValues, containsPair(kCommandCreateProjectType, 'plugin')); + + }, timeout: allowForCreateFlutterProject); + + testUsingContext('set iOS host language type as usage value', () async { + Cache.flutterRoot = '../..'; + + final CreateCommand command = CreateCommand(); + final CommandRunner<void> runner = createTestCommandRunner(command); + + await runner.run(<String>['create', '--no-pub', '--template=app', projectDir.path]); + expect(await command.usageValues, containsPair(kCommandCreateIosLanguage, 'objc')); + + await runner.run(<String>[ + 'create', + '--no-pub', + '--template=app', + '--ios-language=swift', + projectDir.path, + ]); + expect(await command.usageValues, containsPair(kCommandCreateIosLanguage, 'swift')); + + }, timeout: allowForCreateFlutterProject); + + testUsingContext('set Android host language type as usage value', () async { + Cache.flutterRoot = '../..'; + + final CreateCommand command = CreateCommand(); + final CommandRunner<void> runner = createTestCommandRunner(command); + + await runner.run(<String>['create', '--no-pub', '--template=app', projectDir.path]); + expect(await command.usageValues, containsPair(kCommandCreateAndroidLanguage, 'java')); + + await runner.run(<String>[ + 'create', + '--no-pub', + '--template=app', + '--android-language=kotlin', + projectDir.path, + ]); + expect(await command.usageValues, containsPair(kCommandCreateAndroidLanguage, 'kotlin')); + + }, timeout: allowForCreateFlutterProject); + }); } + Future<void> _createProject( Directory dir, List<String> createArgs,
diff --git a/packages/flutter_tools/test/commands/packages_test.dart b/packages/flutter_tools/test/commands/packages_test.dart index 94dfbae..e3b6119 100644 --- a/packages/flutter_tools/test/commands/packages_test.dart +++ b/packages/flutter_tools/test/commands/packages_test.dart
@@ -10,6 +10,7 @@ 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/usage.dart'; import 'package:process/process.dart'; import '../src/common.dart'; @@ -57,7 +58,7 @@ return projectPath; } - Future<void> runCommandIn(String projectPath, String verb, { List<String> args }) async { + Future<PackagesCommand> runCommandIn(String projectPath, String verb, { List<String> args }) async { final PackagesCommand command = PackagesCommand(); final CommandRunner<void> runner = createTestCommandRunner(command); @@ -67,6 +68,7 @@ commandArgs.add(projectPath); await runner.run(commandArgs); + return command; } void expectExists(String projectPath, String relPath) { @@ -217,6 +219,39 @@ expectZeroPluginsInjected(projectPath); }, timeout: allowForCreateFlutterProject); + testUsingContext('set the number of plugins as usage value', () async { + final String projectPath = await createProject(tempDir, + arguments: <String>['--no-pub', '--template=module']); + removeGeneratedFiles(projectPath); + + final PackagesCommand command = await runCommandIn(projectPath, 'get'); + final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand; + + expect(await getCommand.usageValues, containsPair(kCommandPackagesNumberPlugins, '0')); + }, timeout: allowForCreateFlutterProject); + + testUsingContext('indicate that the project is not a module in usage value', () async { + final String projectPath = await createProject(tempDir, + arguments: <String>['--no-pub']); + removeGeneratedFiles(projectPath); + + final PackagesCommand command = await runCommandIn(projectPath, 'get'); + final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand; + + expect(await getCommand.usageValues, containsPair(kCommandPackagesProjectModule, 'false')); + }, timeout: allowForCreateFlutterProject); + + testUsingContext('indicate that the project is a module in usage value', () async { + final String projectPath = await createProject(tempDir, + arguments: <String>['--no-pub', '--template=module']); + removeGeneratedFiles(projectPath); + + final PackagesCommand command = await runCommandIn(projectPath, 'get'); + final PackagesGetCommand getCommand = command.subcommands['get'] as PackagesGetCommand; + + expect(await getCommand.usageValues, containsPair(kCommandPackagesProjectModule, 'true')); + }, timeout: allowForCreateFlutterProject); + testUsingContext('upgrade fetches packages', () async { final String projectPath = await createProject(tempDir, arguments: <String>['--no-pub', '--template=module']);
diff --git a/packages/flutter_tools/test/project_test.dart b/packages/flutter_tools/test/project_test.dart index 9f281ff..6273421 100644 --- a/packages/flutter_tools/test/project_test.dart +++ b/packages/flutter_tools/test/project_test.dart
@@ -227,6 +227,41 @@ }); }); + group('language', () { + MockXcodeProjectInterpreter mockXcodeProjectInterpreter; + MemoryFileSystem fs; + setUp(() { + fs = MemoryFileSystem(); + mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); + }); + + testInMemory('default host app language', () async { + final FlutterProject project = await someProject(); + expect(project.ios.isSwift, isFalse); + expect(project.android.isKotlin, isFalse); + }); + + testUsingContext('swift and kotlin host app language', () async { + final FlutterProject project = await someProject(); + + when(mockXcodeProjectInterpreter.getBuildSettings(any, any)).thenReturn(<String, String>{ + 'SWIFT_VERSION': '3.0', + }); + addAndroidGradleFile(project.directory, + gradleFileContent: () { + return ''' +apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +'''; + }); + expect(project.ios.isSwift, isTrue); + expect(project.android.isKotlin, isTrue); + }, overrides: <Type, Generator>{ + FileSystem: () => fs, + XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, + }); + }); + group('product bundle identifier', () { MemoryFileSystem fs; MockIOSWorkflow mockIOSWorkflow; @@ -251,7 +286,9 @@ }); testWithMocks('from pbxproj file, if no plist', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject'); + }); expect(project.ios.productBundleIdentifier, 'io.flutter.someProject'); }); testWithMocks('from plist, if no variables', () async { @@ -261,7 +298,9 @@ }); testWithMocks('from pbxproj and plist, if default variable', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject'); + }); when(mockIOSWorkflow.getPlistValueFromFile(any, any)).thenReturn('\$(PRODUCT_BUNDLE_IDENTIFIER)'); expect(project.ios.productBundleIdentifier, 'io.flutter.someProject'); }); @@ -276,17 +315,23 @@ }); testWithMocks('empty surrounded by quotes', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, '', qualifier: '"'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('', qualifier: '"'); + }); expect(project.ios.productBundleIdentifier, ''); }); testWithMocks('surrounded by double quotes', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject', qualifier: '"'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject', qualifier: '"'); + }); expect(project.ios.productBundleIdentifier, 'io.flutter.someProject'); }); testWithMocks('surrounded by single quotes', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject', qualifier: '\''); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject', qualifier: '\''); + }); expect(project.ios.productBundleIdentifier, 'io.flutter.someProject'); }); }); @@ -303,22 +348,32 @@ }); testInMemory('is populated from iOS bundle identifier', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject', qualifier: '\''); + }); expect(project.organizationNames, <String>['io.flutter']); }); testInMemory('is populated from Android application ID', () async { final FlutterProject project = await someProject(); - addAndroidWithApplicationId(project.directory, 'io.flutter.someproject'); + addAndroidGradleFile(project.directory, + gradleFileContent: () { + return gradleFileWithApplicationId('io.flutter.someproject'); + }); expect(project.organizationNames, <String>['io.flutter']); }); testInMemory('is populated from iOS bundle identifier in plugin example', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.example.directory, 'io.flutter.someProject'); + addIosProjectFile(project.example.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject', qualifier: '\''); + }); expect(project.organizationNames, <String>['io.flutter']); }); testInMemory('is populated from Android application ID in plugin example', () async { final FlutterProject project = await someProject(); - addAndroidWithApplicationId(project.example.directory, 'io.flutter.someproject'); + addAndroidGradleFile(project.example.directory, + gradleFileContent: () { + return gradleFileWithApplicationId('io.flutter.someproject'); + }); expect(project.organizationNames, <String>['io.flutter']); }); testInMemory('is populated from Android group in plugin', () async { @@ -328,14 +383,24 @@ }); testInMemory('is singleton, if sources agree', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject'); - addAndroidWithApplicationId(project.directory, 'io.flutter.someproject'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject'); + }); + addAndroidGradleFile(project.directory, + gradleFileContent: () { + return gradleFileWithApplicationId('io.flutter.someproject'); + }); expect(project.organizationNames, <String>['io.flutter']); }); testInMemory('is non-singleton, if sources disagree', () async { final FlutterProject project = await someProject(); - addIosWithBundleId(project.directory, 'io.flutter.someProject'); - addAndroidWithApplicationId(project.directory, 'io.clutter.someproject'); + addIosProjectFile(project.directory, projectFileContent: () { + return projectFileWithBundleId('io.flutter.someProject'); + }); + addAndroidGradleFile(project.directory, + gradleFileContent: () { + return gradleFileWithApplicationId('io.clutter.someproject'); + }); expect( project.organizationNames, <String>['io.flutter', 'io.clutter'], @@ -475,22 +540,22 @@ expect(entity.existsSync(), isFalse); } -void addIosWithBundleId(Directory directory, String id, {String qualifier}) { +void addIosProjectFile(Directory directory, {String projectFileContent()}) { directory .childDirectory('ios') .childDirectory('Runner.xcodeproj') .childFile('project.pbxproj') ..createSync(recursive: true) - ..writeAsStringSync(projectFileWithBundleId(id, qualifier: qualifier)); + ..writeAsStringSync(projectFileContent()); } -void addAndroidWithApplicationId(Directory directory, String id) { +void addAndroidGradleFile(Directory directory, { String gradleFileContent() }) { directory .childDirectory('android') .childDirectory('app') .childFile('build.gradle') ..createSync(recursive: true) - ..writeAsStringSync(gradleFileWithApplicationId(id)); + ..writeAsStringSync(gradleFileContent()); } void addAndroidWithGroup(Directory directory, String id) {