| // 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:file_testing/file_testing.dart'; |
| import 'package:flutter_tools/src/base/file_system.dart'; |
| import 'package:flutter_tools/src/base/io.dart'; |
| import 'package:flutter_tools/src/cache.dart'; |
| |
| import '../src/common.dart'; |
| import 'test_data/plugin_each_settings_gradle_project.dart'; |
| import 'test_data/plugin_project.dart'; |
| import 'test_data/project.dart'; |
| import 'test_utils.dart'; |
| |
| void main() { |
| late Directory tempDir; |
| |
| setUp(() { |
| Cache.flutterRoot = getFlutterRoot(); |
| tempDir = createResolvedTempDirectorySync('flutter_plugin_test.'); |
| }); |
| |
| tearDown(() async { |
| tryToDelete(tempDir); |
| }); |
| |
| // Regression test for https://github.com/flutter/flutter/issues/97729 (#137115). |
| /// Creates a project which uses a plugin, which is not supported on Android. |
| /// This means it has no entry in pubspec.yaml for: |
| /// flutter -> plugin -> platforms -> android |
| /// |
| /// [createAndroidPluginFolder] indicates that the plugin can additionally |
| /// have a functioning `android` folder. |
| Future<ProcessResult> testUnsupportedPlugin({ |
| required Project project, |
| required bool createAndroidPluginFolder, |
| }) async { |
| final String flutterBin = fileSystem.path.join( |
| getFlutterRoot(), |
| 'bin', |
| 'flutter', |
| ); |
| |
| // Create dummy plugin that supports iOS and optionally Android. |
| processManager.runSync(<String>[ |
| flutterBin, |
| ...getLocalEngineArguments(), |
| 'create', |
| '--template=plugin', |
| '--platforms=ios${createAndroidPluginFolder ? ',android' : ''}', |
| 'test_plugin', |
| ], workingDirectory: tempDir.path); |
| |
| final Directory pluginAppDir = tempDir.childDirectory('test_plugin'); |
| |
| final File pubspecFile = pluginAppDir.childFile('pubspec.yaml'); |
| String pubspecYamlSrc = |
| pubspecFile.readAsStringSync().replaceAll('\r\n', '\n'); |
| if (createAndroidPluginFolder) { |
| // Override pubspec to drop support for the Android implementation. |
| pubspecYamlSrc = pubspecYamlSrc |
| .replaceFirst( |
| RegExp(r'name:.*\n'), |
| 'name: test_plugin\n', |
| ) |
| .replaceFirst(''' |
| android: |
| package: com.example.test_plugin |
| pluginClass: TestPlugin |
| ''', ''' |
| # android: |
| # package: com.example.test_plugin |
| # pluginClass: TestPlugin |
| '''); |
| |
| pubspecFile.writeAsStringSync(pubspecYamlSrc); |
| |
| // Check the android directory and the build.gradle file within. |
| final File pluginGradleFile = |
| pluginAppDir.childDirectory('android').childFile('build.gradle'); |
| expect(pluginGradleFile, exists); |
| } else { |
| expect(pubspecYamlSrc, isNot(contains('android:'))); |
| } |
| |
| // Create a project which includes the plugin to test against |
| final Directory pluginExampleAppDir = |
| pluginAppDir.childDirectory('example'); |
| |
| await project.setUpIn(pluginExampleAppDir); |
| |
| // Run flutter build apk to build plugin example project. |
| return processManager.runSync(<String>[ |
| flutterBin, |
| ...getLocalEngineArguments(), |
| 'build', |
| 'apk', |
| '--debug', |
| ], workingDirectory: pluginExampleAppDir.path); |
| } |
| |
| test('skip plugin if it does not support the Android platform', () async { |
| final Project project = PluginWithPathAndroidProject(); |
| final ProcessResult buildApkResult = await testUnsupportedPlugin( |
| project: project, createAndroidPluginFolder: false); |
| expect(buildApkResult.stderr.toString(), |
| isNot(contains('Please fix your settings.gradle'))); |
| expect(buildApkResult, const ProcessResultMatcher()); |
| }); |
| |
| test( |
| 'skip plugin with android folder if it does not support the Android platform', |
| () async { |
| final Project project = PluginWithPathAndroidProject(); |
| final ProcessResult buildApkResult = await testUnsupportedPlugin( |
| project: project, createAndroidPluginFolder: true); |
| expect(buildApkResult.stderr.toString(), |
| isNot(contains('Please fix your settings.gradle'))); |
| expect(buildApkResult, const ProcessResultMatcher()); |
| }); |
| |
| // TODO(54566): Remove test when issue is resolved. |
| /// Test project with a `settings.gradle` (PluginEach) that apps were created |
| /// with until Flutter v1.22.0. |
| /// It uses the `.flutter-plugins` file to load EACH plugin. |
| test( |
| 'skip plugin if it does not support the Android platform with a _plugin.each_ settings.gradle', |
| () async { |
| final Project project = PluginEachWithPathAndroidProject(); |
| final ProcessResult buildApkResult = await testUnsupportedPlugin( |
| project: project, createAndroidPluginFolder: false); |
| expect(buildApkResult.stderr.toString(), |
| isNot(contains('Please fix your settings.gradle'))); |
| expect(buildApkResult, const ProcessResultMatcher()); |
| }); |
| |
| // TODO(54566): Remove test when issue is resolved. |
| /// Test project with a `settings.gradle` (PluginEach) that apps were created |
| /// with until Flutter v1.22.0. |
| /// It uses the `.flutter-plugins` file to load EACH plugin. |
| /// The plugin includes a functional 'android' folder. |
| test( |
| 'skip plugin with android folder if it does not support the Android platform with a _plugin.each_ settings.gradle', |
| () async { |
| final Project project = PluginEachWithPathAndroidProject(); |
| final ProcessResult buildApkResult = await testUnsupportedPlugin( |
| project: project, createAndroidPluginFolder: true); |
| expect(buildApkResult.stderr.toString(), |
| isNot(contains('Please fix your settings.gradle'))); |
| expect(buildApkResult, const ProcessResultMatcher()); |
| }); |
| |
| // TODO(54566): Remove test when issue is resolved. |
| /// Test project with a `settings.gradle` (PluginEach) that apps were created |
| /// with until Flutter v1.22.0. |
| /// It is compromised by removing the 'include' statement of the plugins. |
| /// As the "'.flutter-plugins'" keyword is still present, the framework |
| /// assumes that all plugins are included, which is not the case. |
| /// Therefore it should throw an error. |
| test( |
| 'skip plugin if it does not support the Android platform with a compromised _plugin.each_ settings.gradle', |
| () async { |
| final Project project = PluginCompromisedEachWithPathAndroidProject(); |
| final ProcessResult buildApkResult = await testUnsupportedPlugin( |
| project: project, createAndroidPluginFolder: true); |
| expect( |
| buildApkResult, |
| const ProcessResultMatcher( |
| stderrPattern: 'Please fix your settings.gradle'), |
| ); |
| }); |
| } |
| |
| const String pubspecWithPluginPath = r''' |
| name: test |
| environment: |
| sdk: '>=3.2.0-0 <4.0.0' |
| dependencies: |
| flutter: |
| sdk: flutter |
| |
| test_plugin: |
| path: ../ |
| '''; |
| |
| /// Project that load's a plugin from the specified path. |
| class PluginWithPathAndroidProject extends PluginProject { |
| @override |
| String get pubspec => pubspecWithPluginPath; |
| } |
| |
| // TODO(54566): Remove class when issue is resolved. |
| /// [PluginEachSettingsGradleProject] that load's a plugin from the specified |
| /// path. |
| class PluginEachWithPathAndroidProject extends PluginEachSettingsGradleProject { |
| @override |
| String get pubspec => pubspecWithPluginPath; |
| } |
| |
| // TODO(54566): Remove class when issue is resolved. |
| /// [PluginCompromisedEachSettingsGradleProject] that load's a plugin from the |
| /// specified path. |
| class PluginCompromisedEachWithPathAndroidProject |
| extends PluginCompromisedEachSettingsGradleProject { |
| @override |
| String get pubspec => pubspecWithPluginPath; |
| } |