Reland "[flutter_tools] Removes the need of a no-op plugin implementations #48614" (#49085)
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index efee39f..e3aa595 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -11,6 +11,7 @@
import 'android/gradle.dart';
import 'base/common.dart';
import 'base/file_system.dart';
+import 'base/time.dart';
import 'convert.dart';
import 'dart/package_map.dart';
import 'features.dart';
@@ -18,6 +19,7 @@
import 'macos/cocoapods.dart';
import 'platform_plugins.dart';
import 'project.dart';
+import 'version.dart';
void _renderTemplateToFile(String template, dynamic context, String filePath) {
final String renderedTemplate =
@@ -263,7 +265,7 @@
final Map<String, PluginPlatform> platforms;
}
-Plugin _pluginFromPubspec(String name, Uri packageRoot) {
+Plugin _pluginFromPackage(String name, Uri packageRoot) {
final String pubspecPath = globals.fs.path.fromUri(packageRoot.resolve('pubspec.yaml'));
if (!globals.fs.isFileSync(pubspecPath)) {
return null;
@@ -302,7 +304,7 @@
}
packages.forEach((String name, Uri uri) {
final Uri packageRoot = uri.resolve('..');
- final Plugin plugin = _pluginFromPubspec(name, packageRoot);
+ final Plugin plugin = _pluginFromPackage(name, packageRoot);
if (plugin != null) {
plugins.add(plugin);
}
@@ -310,55 +312,163 @@
return plugins;
}
-/// Writes the .flutter-plugins and .flutter-plugins-dependencies files based on the list of plugins.
-/// If there aren't any plugins, then the files aren't written to disk.
-///
-/// Finally, returns [true] if .flutter-plugins or .flutter-plugins-dependencies have changed,
-/// otherwise returns [false].
-bool _writeFlutterPluginsList(FlutterProject project, List<Plugin> plugins) {
- final List<dynamic> directAppDependencies = <dynamic>[];
- const String info = 'This is a generated file; do not edit or check into version control.';
- final StringBuffer flutterPluginsBuffer = StringBuffer('# $info\n');
+ /// Filters [plugins] to those supported by [platformKey].
+ List<Map<String, dynamic>> _filterPluginsByPlatform(List<Plugin>plugins, String platformKey) {
+ final Iterable<Plugin> platformPlugins = plugins.where((Plugin p) {
+ return p.platforms.containsKey(platformKey);
+ });
- final Set<String> pluginNames = <String>{};
- for (final Plugin plugin in plugins) {
- pluginNames.add(plugin.name);
+ final Set<String> pluginNames = platformPlugins.map((Plugin plugin) => plugin.name).toSet();
+ final List<Map<String, dynamic>> list = <Map<String, dynamic>>[];
+ for (final Plugin plugin in platformPlugins) {
+ list.add(<String, dynamic>{
+ 'name': plugin.name,
+ 'path': fsUtils.escapePath(plugin.path),
+ 'dependencies': <String>[...plugin.dependencies.where(pluginNames.contains)],
+ });
+ }
+ return list;
}
+
+/// Writes the .flutter-plugins-dependencies file based on the list of plugins.
+/// If there aren't any plugins, then the files aren't written to disk. The resulting
+/// file looks something like this (order of keys is not guaranteed):
+/// {
+/// "info": "This is a generated file; do not edit or check into version control.",
+/// "plugins": {
+/// "ios": [
+/// {
+/// "name": "test",
+/// "path": "test_path",
+/// "dependencies": [
+/// "plugin-a",
+/// "plugin-b"
+/// ]
+/// }
+/// ],
+/// "android": [],
+/// "macos": [],
+/// "linux": [],
+/// "windows": [],
+/// "web": []
+/// },
+/// "dependencyGraph": [
+/// {
+/// "name": "plugin-a",
+/// "dependencies": [
+/// "plugin-b",
+/// "plugin-c"
+/// ]
+/// },
+/// {
+/// "name": "plugin-b",
+/// "dependencies": [
+/// "plugin-c"
+/// ]
+/// },
+/// {
+/// "name": "plugin-c",
+/// "dependencies": []
+/// }
+/// ],
+/// "date_created": "1970-01-01 00:00:00.000",
+/// "version": "0.0.0-unknown"
+/// }
+///
+///
+/// Finally, returns [true] if the plugins list has changed, otherwise returns [false].
+bool _writeFlutterPluginsList(FlutterProject project, List<Plugin> plugins) {
+ final File pluginsFile = project.flutterPluginsDependenciesFile;
+ if (plugins.isEmpty) {
+ if (pluginsFile.existsSync()) {
+ pluginsFile.deleteSync();
+ return true;
+ }
+ return false;
+ }
+
+ final String iosKey = project.ios.pluginConfigKey;
+ final String androidKey = project.android.pluginConfigKey;
+ final String macosKey = project.macos.pluginConfigKey;
+ final String linuxKey = project.linux.pluginConfigKey;
+ final String windowsKey = project.windows.pluginConfigKey;
+ final String webKey = project.web.pluginConfigKey;
+
+ final Map<String, dynamic> pluginsMap = <String, dynamic>{};
+ pluginsMap[iosKey] = _filterPluginsByPlatform(plugins, iosKey);
+ pluginsMap[androidKey] = _filterPluginsByPlatform(plugins, androidKey);
+ pluginsMap[macosKey] = _filterPluginsByPlatform(plugins, macosKey);
+ pluginsMap[linuxKey] = _filterPluginsByPlatform(plugins, linuxKey);
+ pluginsMap[windowsKey] = _filterPluginsByPlatform(plugins, windowsKey);
+ pluginsMap[webKey] = _filterPluginsByPlatform(plugins, webKey);
+
+ final Map<String, dynamic> result = <String, dynamic> {};
+
+ result['info'] = 'This is a generated file; do not edit or check into version control.';
+ result['plugins'] = pluginsMap;
+ /// The dependencyGraph object is kept for backwards compatibility, but
+ /// should be removed once migration is complete.
+ /// https://github.com/flutter/flutter/issues/48918
+ result['dependencyGraph'] = _createPluginLegacyDependencyGraph(plugins);
+ result['date_created'] = systemClock.now().toString();
+ result['version'] = flutterVersion.frameworkVersion;
+
+ // Only notify if the plugins list has changed. [date_created] will always be different,
+ // [version] is not relevant for this check.
+ final String oldPluginsFileStringContent = _readFileContent(pluginsFile);
+ bool pluginsChanged = true;
+ if (oldPluginsFileStringContent != null) {
+ pluginsChanged = oldPluginsFileStringContent.contains(pluginsMap.toString());
+ }
+ final String pluginFileContent = json.encode(result);
+ pluginsFile.writeAsStringSync(pluginFileContent, flush: true);
+
+ return pluginsChanged;
+}
+
+List<dynamic> _createPluginLegacyDependencyGraph(List<Plugin> plugins) {
+ final List<dynamic> directAppDependencies = <dynamic>[];
+
+ final Set<String> pluginNames = plugins.map((Plugin plugin) => plugin.name).toSet();
for (final Plugin plugin in plugins) {
- flutterPluginsBuffer.write('${plugin.name}=${fsUtils.escapePath(plugin.path)}\n');
directAppDependencies.add(<String, dynamic>{
'name': plugin.name,
// Extract the plugin dependencies which happen to be plugins.
'dependencies': <String>[...plugin.dependencies.where(pluginNames.contains)],
});
}
+ return directAppDependencies;
+}
+
+// The .flutter-plugins file will be DEPRECATED in favor of .flutter-plugins-dependencies.
+// TODO(franciscojma): Remove this method once deprecated.
+// https://github.com/flutter/flutter/issues/48918
+//
+/// Writes the .flutter-plugins files based on the list of plugins.
+/// If there aren't any plugins, then the files aren't written to disk.
+///
+/// Finally, returns [true] if .flutter-plugins has changed, otherwise returns [false].
+bool _writeFlutterPluginsListLegacy(FlutterProject project, List<Plugin> plugins) {
final File pluginsFile = project.flutterPluginsFile;
- final String oldPluginFileContent = _readFileContent(pluginsFile);
- final String pluginFileContent = flutterPluginsBuffer.toString();
- if (pluginNames.isNotEmpty) {
- pluginsFile.writeAsStringSync(pluginFileContent, flush: true);
- } else {
+ if (plugins.isEmpty) {
if (pluginsFile.existsSync()) {
pluginsFile.deleteSync();
+ return true;
}
+ return false;
}
- final File dependenciesFile = project.flutterPluginsDependenciesFile;
- final String oldDependenciesFileContent = _readFileContent(dependenciesFile);
- final String dependenciesFileContent = json.encode(<String, dynamic>{
- '_info': '// $info',
- 'dependencyGraph': directAppDependencies,
- });
- if (pluginNames.isNotEmpty) {
- dependenciesFile.writeAsStringSync(dependenciesFileContent, flush: true);
- } else {
- if (dependenciesFile.existsSync()) {
- dependenciesFile.deleteSync();
- }
- }
+ const String info = 'This is a generated file; do not edit or check into version control.';
+ final StringBuffer flutterPluginsBuffer = StringBuffer('# $info\n');
- return oldPluginFileContent != _readFileContent(pluginsFile)
- || oldDependenciesFileContent != _readFileContent(dependenciesFile);
+ for (final Plugin plugin in plugins) {
+ flutterPluginsBuffer.write('${plugin.name}=${fsUtils.escapePath(plugin.path)}\n');
+ }
+ final String oldPluginFileContent = _readFileContent(pluginsFile);
+ final String pluginFileContent = flutterPluginsBuffer.toString();
+ pluginsFile.writeAsStringSync(pluginFileContent, flush: true);
+
+ return oldPluginFileContent != _readFileContent(pluginsFile);
}
/// Returns the contents of [File] or [null] if that file does not exist.
@@ -782,8 +892,13 @@
/// Assumes `pub get` has been executed since last change to `pubspec.yaml`.
void refreshPluginsList(FlutterProject project, {bool checkProjects = false}) {
final List<Plugin> plugins = findPlugins(project);
+
+ // TODO(franciscojma): Remove once migration is complete.
+ // Write the legacy plugin files to avoid breaking existing apps.
+ final bool legacyChanged = _writeFlutterPluginsListLegacy(project, plugins);
+
final bool changed = _writeFlutterPluginsList(project, plugins);
- if (changed) {
+ if (changed || legacyChanged) {
if (!checkProjects || project.ios.existsSync()) {
cocoaPods.invalidatePodInstallOutput(project.ios);
}
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index fd8a480..f397a33 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -20,6 +20,7 @@
import 'globals.dart' as globals;
import 'ios/plist_parser.dart';
import 'ios/xcodeproj.dart' as xcode;
+import 'platform_plugins.dart';
import 'plugins.dart';
import 'template.dart';
@@ -251,6 +252,16 @@
}
}
+/// Base class for projects per platform.
+abstract class FlutterProjectPlatform {
+
+ /// Plugin's platform config key, e.g., "macos", "ios".
+ String get pluginConfigKey;
+
+ /// Whether the platform exists in the project.
+ bool existsSync();
+}
+
/// Represents an Xcode-based sub-project.
///
/// This defines interfaces common to iOS and macOS projects.
@@ -300,12 +311,15 @@
///
/// Instances will reflect the contents of the `ios/` sub-folder of
/// Flutter applications and the `.ios/` sub-folder of Flutter module projects.
-class IosProject implements XcodeBasedProject {
+class IosProject extends FlutterProjectPlatform implements XcodeBasedProject {
IosProject.fromFlutter(this.parent);
@override
final FlutterProject parent;
+ @override
+ String get pluginConfigKey => IOSPlugin.kConfigKey;
+
static final RegExp _productBundleIdPattern = RegExp(r'''^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(["']?)(.*?)\1;\s*$''');
static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)';
static const String _hostAppBundleName = 'Runner';
@@ -574,12 +588,15 @@
///
/// Instances will reflect the contents of the `android/` sub-folder of
/// Flutter applications and the `.android/` sub-folder of Flutter module projects.
-class AndroidProject {
+class AndroidProject extends FlutterProjectPlatform {
AndroidProject._(this.parent);
/// The parent of this project.
final FlutterProject parent;
+ @override
+ String get pluginConfigKey => AndroidPlugin.kConfigKey;
+
static final RegExp _applicationIdPattern = RegExp('^\\s*applicationId\\s+[\'\"](.*)[\'\"]\\s*\$');
static final RegExp _kotlinPluginPattern = RegExp('^\\s*apply plugin\:\\s+[\'\"]kotlin-android[\'\"]\\s*\$');
static final RegExp _groupPattern = RegExp('^\\s*group\\s+[\'\"](.*)[\'\"]\\s*\$');
@@ -627,6 +644,7 @@
}
/// Whether the current flutter project has an Android sub-project.
+ @override
bool existsSync() {
return parent.isModule || _editableHostAppDirectory.existsSync();
}
@@ -760,12 +778,16 @@
}
/// Represents the web sub-project of a Flutter project.
-class WebProject {
+class WebProject extends FlutterProjectPlatform {
WebProject._(this.parent);
final FlutterProject parent;
+ @override
+ String get pluginConfigKey => WebPlugin.kConfigKey;
+
/// Whether this flutter project has a web sub-project.
+ @override
bool existsSync() {
return parent.directory.childDirectory('web').existsSync()
&& indexFile.existsSync();
@@ -810,12 +832,15 @@
}
/// The macOS sub project.
-class MacOSProject implements XcodeBasedProject {
+class MacOSProject extends FlutterProjectPlatform implements XcodeBasedProject {
MacOSProject._(this.parent);
@override
final FlutterProject parent;
+ @override
+ String get pluginConfigKey => MacOSPlugin.kConfigKey;
+
static const String _hostAppBundleName = 'Runner';
@override
@@ -895,11 +920,15 @@
}
/// The Windows sub project
-class WindowsProject {
+class WindowsProject extends FlutterProjectPlatform {
WindowsProject._(this.project);
final FlutterProject project;
+ @override
+ String get pluginConfigKey => WindowsPlugin.kConfigKey;
+
+ @override
bool existsSync() => _editableDirectory.existsSync();
Directory get _editableDirectory => project.directory.childDirectory('windows');
@@ -933,11 +962,14 @@
}
/// The Linux sub project.
-class LinuxProject {
+class LinuxProject extends FlutterProjectPlatform {
LinuxProject._(this.project);
final FlutterProject project;
+ @override
+ String get pluginConfigKey => LinuxPlugin.kConfigKey;
+
Directory get _editableDirectory => project.directory.childDirectory('linux');
/// The directory in the project that is managed by Flutter. As much as
@@ -950,6 +982,7 @@
/// checked in should live here.
Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral');
+ @override
bool existsSync() => _editableDirectory.existsSync();
/// The Linux project makefile.
diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart
index d6dc8c8..470f4ed 100644
--- a/packages/flutter_tools/lib/src/version.dart
+++ b/packages/flutter_tools/lib/src/version.dart
@@ -16,6 +16,8 @@
import 'convert.dart';
import 'globals.dart' as globals;
+FlutterVersion get flutterVersion => context.get<FlutterVersion>();
+
class FlutterVersion {
FlutterVersion([this._clock = const SystemClock()]) {
_frameworkRevision = _runGit(gitLog(<String>['-n', '1', '--pretty=format:%H']).join(' '));
diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart
index 635d0e9..2117656 100644
--- a/packages/flutter_tools/test/general.shard/plugins_test.dart
+++ b/packages/flutter_tools/test/general.shard/plugins_test.dart
@@ -2,13 +2,17 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:convert';
+
import 'package:file/file.dart';
import 'package:file/memory.dart';
+import 'package:flutter_tools/src/base/time.dart';
import 'package:flutter_tools/src/dart/package_map.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/plugins.dart';
import 'package:flutter_tools/src/project.dart';
+import 'package:flutter_tools/src/version.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
@@ -23,15 +27,22 @@
MockMacOSProject macosProject;
MockAndroidProject androidProject;
MockWebProject webProject;
+ MockWindowsProject windowsProject;
+ MockLinuxProject linuxProject;
File packagesFile;
Directory dummyPackageDirectory;
+ SystemClock mockClock;
+ FlutterVersion mockVersion;
setUp(() async {
fs = MemoryFileSystem();
+ mockClock = MockClock();
+ mockVersion = MockFlutterVersion();
// Add basic properties to the Flutter project and subprojects
flutterProject = MockFlutterProject();
when(flutterProject.directory).thenReturn(fs.directory('/'));
+ // TODO(franciscojma): Remove logic for .flutter-plugins it's deprecated.
when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.flutter-plugins'));
when(flutterProject.flutterPluginsDependenciesFile).thenReturn(flutterProject.directory.childFile('.flutter-plugins-dependencies'));
iosProject = MockIosProject();
@@ -39,18 +50,41 @@
when(iosProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('Runner'));
when(iosProject.podfile).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile'));
when(iosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile.lock'));
+ when(iosProject.pluginConfigKey).thenReturn('ios');
+ when(iosProject.existsSync()).thenReturn(false);
macosProject = MockMacOSProject();
when(flutterProject.macos).thenReturn(macosProject);
when(macosProject.podfile).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile'));
when(macosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile.lock'));
+ when(macosProject.pluginConfigKey).thenReturn('macos');
+ when(macosProject.existsSync()).thenReturn(false);
androidProject = MockAndroidProject();
when(flutterProject.android).thenReturn(androidProject);
when(androidProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('android').childDirectory('app'));
when(androidProject.hostAppGradleRoot).thenReturn(flutterProject.directory.childDirectory('android'));
+ when(androidProject.pluginConfigKey).thenReturn('android');
+ when(androidProject.existsSync()).thenReturn(false);
webProject = MockWebProject();
when(flutterProject.web).thenReturn(webProject);
when(webProject.libDirectory).thenReturn(flutterProject.directory.childDirectory('lib'));
when(webProject.existsSync()).thenReturn(true);
+ when(webProject.pluginConfigKey).thenReturn('web');
+ when(webProject.existsSync()).thenReturn(false);
+ windowsProject = MockWindowsProject();
+ when(flutterProject.windows).thenReturn(windowsProject);
+ when(windowsProject.pluginConfigKey).thenReturn('windows');
+ when(windowsProject.existsSync()).thenReturn(false);
+ linuxProject = MockLinuxProject();
+ when(flutterProject.linux).thenReturn(linuxProject);
+ when(linuxProject.pluginConfigKey).thenReturn('linux');
+ when(linuxProject.existsSync()).thenReturn(false);
+
+ when(mockClock.now()).thenAnswer(
+ (Invocation _) => DateTime(1970, 1, 1)
+ );
+ when(mockVersion.frameworkVersion).thenAnswer(
+ (Invocation _) => '1.0.0'
+ );
// Set up a simple .packages file for all the tests to use, pointing to one package.
dummyPackageDirectory = fs.directory('/pubcache/apackage/lib/');
@@ -67,6 +101,18 @@
platforms:
ios:
pluginClass: FLESomePlugin
+ macos:
+ pluginClass: FLESomePlugin
+ windows:
+ pluginClass: FLESomePlugin
+ linux:
+ pluginClass: FLESomePlugin
+ web:
+ pluginClass: SomePlugin
+ fileName: lib/SomeFile.dart
+ android:
+ pluginClass: SomePlugin
+ package: AndroidPackage
''');
}
@@ -239,8 +285,8 @@
testUsingContext('Refreshing the plugin list deletes the plugin file when there were plugins but no longer are', () {
flutterProject.flutterPluginsFile.createSync();
- when(iosProject.existsSync()).thenReturn(false);
- when(macosProject.existsSync()).thenReturn(false);
+ flutterProject.flutterPluginsDependenciesFile.createSync();
+
refreshPluginsList(flutterProject);
expect(flutterProject.flutterPluginsFile.existsSync(), false);
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), false);
@@ -251,8 +297,8 @@
testUsingContext('Refreshing the plugin list creates a plugin directory when there are plugins', () {
configureDummyPackageAsPlugin();
- when(iosProject.existsSync()).thenReturn(false);
- when(macosProject.existsSync()).thenReturn(false);
+ when(iosProject.existsSync()).thenReturn(true);
+
refreshPluginsList(flutterProject);
expect(flutterProject.flutterPluginsFile.existsSync(), true);
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
@@ -265,11 +311,20 @@
createPluginWithDependencies(name: 'plugin-a', dependencies: const <String>['plugin-b', 'plugin-c', 'random-package']);
createPluginWithDependencies(name: 'plugin-b', dependencies: const <String>['plugin-c']);
createPluginWithDependencies(name: 'plugin-c', dependencies: const <String>[]);
- when(iosProject.existsSync()).thenReturn(false);
- when(macosProject.existsSync()).thenReturn(false);
+ when(iosProject.existsSync()).thenReturn(true);
+
+ final DateTime dateCreated = DateTime(1970, 1, 1);
+ when(mockClock.now()).thenAnswer(
+ (Invocation _) => dateCreated
+ );
+ const String version = '1.0.0';
+ when(mockVersion.frameworkVersion).thenAnswer(
+ (Invocation _) => version
+ );
refreshPluginsList(flutterProject);
+ // Verify .flutter-plugins-dependencies is configured correctly.
expect(flutterProject.flutterPluginsFile.existsSync(), true);
expect(flutterProject.flutterPluginsDependenciesFile.existsSync(), true);
expect(flutterProject.flutterPluginsFile.readAsStringSync(),
@@ -279,28 +334,79 @@
'plugin-c=/.tmp_rand0/plugin.rand2/\n'
''
);
- expect(flutterProject.flutterPluginsDependenciesFile.readAsStringSync(),
- '{'
- '"_info":"// This is a generated file; do not edit or check into version control.",'
- '"dependencyGraph":['
- '{'
- '"name":"plugin-a",'
- '"dependencies":["plugin-b","plugin-c"]'
- '},'
- '{'
- '"name":"plugin-b",'
- '"dependencies":["plugin-c"]'
- '},'
- '{'
- '"name":"plugin-c",'
- '"dependencies":[]'
- '}'
- ']'
- '}'
- );
+
+ final String pluginsString = flutterProject.flutterPluginsDependenciesFile.readAsStringSync();
+ final Map<String, dynamic> jsonContent = json.decode(pluginsString) as Map<String, dynamic>;
+ expect(jsonContent['info'], 'This is a generated file; do not edit or check into version control.');
+
+ final Map<String, dynamic> plugins = jsonContent['plugins'] as Map<String, dynamic>;
+ final List<dynamic> expectedPlugins = <dynamic>[
+ <String, dynamic> {
+ 'name': 'plugin-a',
+ 'path': '/.tmp_rand0/plugin.rand0/',
+ 'dependencies': <String>[
+ 'plugin-b',
+ 'plugin-c'
+ ]
+ },
+ <String, dynamic> {
+ 'name': 'plugin-b',
+ 'path': '/.tmp_rand0/plugin.rand1/',
+ 'dependencies': <String>[
+ 'plugin-c'
+ ]
+ },
+ <String, dynamic> {
+ 'name': 'plugin-c',
+ 'path': '/.tmp_rand0/plugin.rand2/',
+ 'dependencies': <String>[]
+ },
+ ];
+ expect(plugins['ios'], expectedPlugins);
+ expect(plugins['android'], expectedPlugins);
+ expect(plugins['macos'], <dynamic>[]);
+ expect(plugins['windows'], <dynamic>[]);
+ expect(plugins['linux'], <dynamic>[]);
+ expect(plugins['web'], <dynamic>[]);
+
+ final List<dynamic> expectedDependencyGraph = <dynamic>[
+ <String, dynamic> {
+ 'name': 'plugin-a',
+ 'dependencies': <String>[
+ 'plugin-b',
+ 'plugin-c'
+ ]
+ },
+ <String, dynamic> {
+ 'name': 'plugin-b',
+ 'dependencies': <String>[
+ 'plugin-c'
+ ]
+ },
+ <String, dynamic> {
+ 'name': 'plugin-c',
+ 'dependencies': <String>[]
+ },
+ ];
+
+ expect(jsonContent['dependencyGraph'], expectedDependencyGraph);
+ expect(jsonContent['date_created'], dateCreated.toString());
+ expect(jsonContent['version'], version);
+
+ // Make sure tests are updated if a new object is added/removed.
+ final List<String> expectedKeys = <String>[
+ 'info',
+ 'plugins',
+ 'dependencyGraph',
+ 'date_created',
+ 'version',
+ ];
+ expect(jsonContent.keys, expectedKeys);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
+ SystemClock: () => mockClock,
+ FlutterVersion: () => mockVersion
});
testUsingContext('Changes to the plugin list invalidates the Cocoapod lockfiles', () {
@@ -315,6 +421,31 @@
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
+ SystemClock: () => mockClock,
+ FlutterVersion: () => mockVersion
+ });
+
+ testUsingContext('No changes to the plugin list does not invalidate the Cocoapod lockfiles', () {
+ configureDummyPackageAsPlugin();
+ when(iosProject.existsSync()).thenReturn(true);
+ when(macosProject.existsSync()).thenReturn(true);
+
+ // First call will create the .flutter-plugins-dependencies and the legacy .flutter-plugins file.
+ // Since there was no plugins list, the lock files will be invalidated.
+ // The second call is where the plugins list is compared to the existing one, and if there is no change,
+ // the podfiles shouldn't be invalidated.
+ refreshPluginsList(flutterProject);
+ simulatePodInstallRun(iosProject);
+ simulatePodInstallRun(macosProject);
+
+ refreshPluginsList(flutterProject);
+ expect(iosProject.podManifestLock.existsSync(), true);
+ expect(macosProject.podManifestLock.existsSync(), true);
+ }, overrides: <Type, Generator>{
+ FileSystem: () => fs,
+ ProcessManager: () => FakeProcessManager.any(),
+ SystemClock: () => mockClock,
+ FlutterVersion: () => mockVersion
});
});
@@ -600,6 +731,7 @@
testUsingContext('Registrant for web doesn\'t escape slashes in imports', () async {
when(flutterProject.isModule).thenReturn(true);
when(featureFlags.isWebEnabled).thenReturn(true);
+ when(webProject.existsSync()).thenReturn(true);
final Directory webPluginWithNestedFile =
fs.systemTempDirectory.createTempSync('web_plugin_with_nested');
@@ -648,3 +780,5 @@
class MockMacOSProject extends Mock implements MacOSProject {}
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
class MockWebProject extends Mock implements WebProject {}
+class MockWindowsProject extends Mock implements WindowsProject {}
+class MockLinuxProject extends Mock implements LinuxProject {}