From 7020f59ace273d8644f0c8f3d0c6e7c9c95ba51a Mon Sep 17 00:00:00 2001 From: Alex Wallen Date: Tue, 8 Nov 2022 14:53:23 -0800 Subject: [PATCH] [tool] Support `--flavor` option for `flutter install`. (#114048) * Alphabetize setup calls * Add --flavor as an option for install * Add verbose logging in install command * Test that flavors build succeeds with proper flavor and fails with bogus one. * Remove unused import * The import was used... * SQUASH * Add flavor install test * Rename test * Add flavors install integration tests * correct error message * remove unused imports * Delete copy test * update test target * Refactor mechanism to read buildInfo * Remove unused import * Set affected test targets to bringup: true Co-authored-by: a-wallen --- .ci.yaml | 4 ++- dev/devicelab/bin/tasks/flavors_test.dart | 25 ++++++++++++++++ dev/devicelab/bin/tasks/flavors_test_ios.dart | 25 ++++++++++++++++ dev/devicelab/bin/tasks/flavors_test_win.dart | 18 ----------- packages/flutter_tools/lib/executable.dart | 4 ++- .../lib/src/commands/install.dart | 11 +++++-- .../commands.shard/hermetic/install_test.dart | 30 +++++++++++++++---- 7 files changed, 89 insertions(+), 28 deletions(-) delete mode 100644 dev/devicelab/bin/tasks/flavors_test_win.dart diff --git a/.ci.yaml b/.ci.yaml index e158f52f516..dd0986e711c 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -1687,6 +1687,7 @@ targets: task_name: fast_scroll_large_images__memory - name: Linux_android flavors_test + bringup: true recipe: devicelab/devicelab_drone presubmit: false timeout: 60 @@ -3371,6 +3372,7 @@ targets: task_name: route_test_ios - name: Mac_ios flavors_test_ios + bringup: true recipe: devicelab/devicelab_drone presubmit: false timeout: 60 @@ -4537,7 +4539,7 @@ targets: properties: tags: > ["devicelab", "android", "windows"] - task_name: flavors_test_win + task_name: flavors_test - name: Windows_android flutter_gallery_win__compile recipe: devicelab/devicelab_drone diff --git a/dev/devicelab/bin/tasks/flavors_test.dart b/dev/devicelab/bin/tasks/flavors_test.dart index 7e50969cd30..c690c217b88 100644 --- a/dev/devicelab/bin/tasks/flavors_test.dart +++ b/dev/devicelab/bin/tasks/flavors_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_devicelab/framework/devices.dart'; import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/task_result.dart'; +import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/tasks/integration_tests.dart'; Future main() async { @@ -12,6 +13,30 @@ Future main() async { await task(() async { await createFlavorsTest().call(); await createIntegrationTestFlavorsTest().call(); + // test install and uninstall of flavors app + await inDirectory('${flutterDirectory.path}/dev/integration_tests/flavors', () async { + await flutter( + 'install', + options: ['--flavor', 'paid'], + ); + await flutter( + 'install', + options: ['--flavor', 'paid', '--uninstall-only'], + ); + final StringBuffer stderr = StringBuffer(); + await evalFlutter( + 'install', + canFail: true, + stderr: stderr, + options: ['--flavor', 'bogus'], + ); + + final String stderrString = stderr.toString(); + if (!stderrString.contains('The Xcode project defines schemes: free, paid')) { + print(stderrString); + return TaskResult.failure('Should not succeed with bogus flavor'); + } + }); return TaskResult.success(null); }); diff --git a/dev/devicelab/bin/tasks/flavors_test_ios.dart b/dev/devicelab/bin/tasks/flavors_test_ios.dart index c508c7fa889..2fbfcb9e0a4 100644 --- a/dev/devicelab/bin/tasks/flavors_test_ios.dart +++ b/dev/devicelab/bin/tasks/flavors_test_ios.dart @@ -5,6 +5,7 @@ import 'package:flutter_devicelab/framework/devices.dart'; import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/task_result.dart'; +import 'package:flutter_devicelab/framework/utils.dart'; import 'package:flutter_devicelab/tasks/integration_tests.dart'; Future main() async { @@ -12,6 +13,30 @@ Future main() async { await task(() async { await createFlavorsTest().call(); await createIntegrationTestFlavorsTest().call(); + // test install and uninstall of flavors app + await inDirectory('${flutterDirectory.path}/dev/integration_tests/flavors', () async { + await flutter( + 'install', + options: ['--flavor', 'paid'], + ); + await flutter( + 'install', + options: ['--flavor', 'paid', '--uninstall-only'], + ); + final StringBuffer stderr = StringBuffer(); + await evalFlutter( + 'install', + canFail: true, + stderr: stderr, + options: ['--flavor', 'bogus'], + ); + + final String stderrString = stderr.toString(); + if (!stderrString.contains('install failed, bogus flavor not found')) { + print(stderrString); + return TaskResult.failure('Should not succeed with bogus flavor'); + } + }); return TaskResult.success(null); }); diff --git a/dev/devicelab/bin/tasks/flavors_test_win.dart b/dev/devicelab/bin/tasks/flavors_test_win.dart deleted file mode 100644 index 7e50969cd30..00000000000 --- a/dev/devicelab/bin/tasks/flavors_test_win.dart +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter_devicelab/framework/devices.dart'; -import 'package:flutter_devicelab/framework/framework.dart'; -import 'package:flutter_devicelab/framework/task_result.dart'; -import 'package:flutter_devicelab/tasks/integration_tests.dart'; - -Future main() async { - deviceOperatingSystem = DeviceOperatingSystem.android; - await task(() async { - await createFlavorsTest().call(); - await createIntegrationTestFlavorsTest().call(); - - return TaskResult.success(null); - }); -} diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart index 1d774354539..a2807a22faa 100644 --- a/packages/flutter_tools/lib/executable.dart +++ b/packages/flutter_tools/lib/executable.dart @@ -200,7 +200,9 @@ List generateCommands({ artifacts: globals.artifacts!, processManager: globals.processManager, ), - InstallCommand(), + InstallCommand( + verboseHelp: verboseHelp, + ), LogsCommand(), MakeHostAppEditableCommand(), PackagesCommand(), diff --git a/packages/flutter_tools/lib/src/commands/install.dart b/packages/flutter_tools/lib/src/commands/install.dart index e7e5b92b709..9bff80a3c27 100644 --- a/packages/flutter_tools/lib/src/commands/install.dart +++ b/packages/flutter_tools/lib/src/commands/install.dart @@ -12,11 +12,15 @@ import '../globals.dart' as globals; import '../runner/flutter_command.dart'; class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts { - InstallCommand() { + InstallCommand({ + required bool verboseHelp, + }) { + addBuildModeFlags(verboseHelp: verboseHelp); requiresPubspecYaml(); - usesDeviceUserOption(); - usesDeviceTimeoutOption(); usesApplicationBinaryOption(); + usesDeviceTimeoutOption(); + usesDeviceUserOption(); + usesFlavorOption(); argParser.addFlag('uninstall-only', help: 'Uninstall the app if already on the device. Skip install.', ); @@ -60,6 +64,7 @@ class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts final ApplicationPackage? package = await applicationPackages?.getPackageForPlatform( await targetDevice.targetPlatform, applicationBinary: _applicationBinary, + buildInfo: await getBuildInfo(), ); if (package == null) { throwToolExit('Could not find or build package'); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart index 54810d87114..6b3309f3ad7 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart @@ -32,7 +32,7 @@ void main() { }); testUsingContext('returns 0 when Android is connected and ready for an install', () async { - final InstallCommand command = InstallCommand(); + final InstallCommand command = InstallCommand(verboseHelp: false); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); final FakeAndroidDevice device = FakeAndroidDevice(); @@ -46,7 +46,7 @@ void main() { }); testUsingContext('returns 1 when targeted device is not Android with --device-user', () async { - final InstallCommand command = InstallCommand(); + final InstallCommand command = InstallCommand(verboseHelp: false); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); final FakeIOSDevice device = FakeIOSDevice(); @@ -61,7 +61,7 @@ void main() { }); testUsingContext('returns 0 when iOS is connected and ready for an install', () async { - final InstallCommand command = InstallCommand(); + final InstallCommand command = InstallCommand(verboseHelp: false); command.applicationPackages = FakeApplicationPackageFactory(FakeIOSApp()); final FakeIOSDevice device = FakeIOSDevice(); @@ -75,7 +75,7 @@ void main() { }); testUsingContext('fails when prebuilt binary not found', () async { - final InstallCommand command = InstallCommand(); + final InstallCommand command = InstallCommand(verboseHelp: false); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); final FakeAndroidDevice device = FakeAndroidDevice(); @@ -90,7 +90,7 @@ void main() { }); testUsingContext('succeeds using prebuilt binary', () async { - final InstallCommand command = InstallCommand(); + final InstallCommand command = InstallCommand(verboseHelp: false); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); final FakeAndroidDevice device = FakeAndroidDevice(); @@ -103,6 +103,24 @@ void main() { FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), }); + + testUsingContext('Passes flavor to application package.', () async { + const String flavor = 'free'; + final InstallCommand command = InstallCommand(verboseHelp: false); + final FakeApplicationPackageFactory fakeAppFactory = FakeApplicationPackageFactory(FakeIOSApp()); + command.applicationPackages = fakeAppFactory; + + final FakeIOSDevice device = FakeIOSDevice(); + testDeviceManager.addDevice(device); + + await createTestCommandRunner(command).run(['install', '--flavor', flavor]); + expect(fakeAppFactory.buildInfo, isNotNull); + expect(fakeAppFactory.buildInfo!.flavor, flavor); + }, overrides: { + Cache: () => Cache.test(processManager: FakeProcessManager.any()), + FileSystem: () => fileSystem, + ProcessManager: () => FakeProcessManager.any(), + }); }); } @@ -110,9 +128,11 @@ class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFa FakeApplicationPackageFactory(this.app); final ApplicationPackage app; + BuildInfo? buildInfo; @override Future getPackageForPlatform(TargetPlatform platform, {BuildInfo? buildInfo, File? applicationBinary}) async { + this.buildInfo = buildInfo; return app; } }