diff --git a/packages/flutter_tools/lib/src/flutter_plugins.dart b/packages/flutter_tools/lib/src/flutter_plugins.dart index 7901773e10..3d11a0cf4b 100644 --- a/packages/flutter_tools/lib/src/flutter_plugins.dart +++ b/packages/flutter_tools/lib/src/flutter_plugins.dart @@ -244,7 +244,15 @@ bool _writeFlutterPluginsList( final String? oldPluginsFileStringContent = _readFileContent(pluginsFile); bool pluginsChanged = true; if (oldPluginsFileStringContent != null) { - pluginsChanged = oldPluginsFileStringContent.contains(pluginsMap.toString()); + Object? decodedJson; + try { + decodedJson = jsonDecode(oldPluginsFileStringContent); + if (decodedJson is Map) { + final String jsonOfNewPluginsMap = jsonEncode(pluginsMap); + final String jsonOfOldPluginsMap = jsonEncode(decodedJson[_kFlutterPluginsPluginListKey]); + pluginsChanged = jsonOfNewPluginsMap != jsonOfOldPluginsMap; + } + } on FormatException catch (_) {} } final String pluginFileContent = json.encode(result); pluginsFile.writeAsStringSync(pluginFileContent, flush: true); diff --git a/packages/flutter_tools/test/general.shard/plugins_test.dart b/packages/flutter_tools/test/general.shard/plugins_test.dart index 6652210e3d..39ff645ecc 100644 --- a/packages/flutter_tools/test/general.shard/plugins_test.dart +++ b/packages/flutter_tools/test/general.shard/plugins_test.dart @@ -20,6 +20,7 @@ import 'package:flutter_tools/src/flutter_manifest.dart'; import 'package:flutter_tools/src/flutter_plugins.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/ios/xcodeproj.dart'; +import 'package:flutter_tools/src/macos/cocoapods.dart'; import 'package:flutter_tools/src/macos/darwin_dependency_management.dart'; import 'package:flutter_tools/src/platform_plugins.dart'; import 'package:flutter_tools/src/plugins.dart'; @@ -102,6 +103,8 @@ void main() { late FakeLinuxProject linuxProject; late FakeSystemClock systemClock; late FlutterVersion flutterVersion; + late FakeCocoaPodsCapturesInvalidate cocoaPods; + // A Windows-style filesystem. This is not populated by default, so tests // using it instead of fs must re-run any necessary setup (e.g., // setUpProject). @@ -190,6 +193,7 @@ void main() { fsWindows = MemoryFileSystem(style: FileSystemStyle.windows); systemClock = FakeSystemClock()..currentTime = DateTime(1970); flutterVersion = FakeFlutterVersion(frameworkVersion: '1.0.0'); + cocoaPods = FakeCocoaPodsCapturesInvalidate(); // Add basic properties to the Flutter project and subprojects setUpProject(fs); @@ -628,6 +632,78 @@ dependencies: }, ); + testUsingContext( + 'Refreshing the plugin list for iOS/macOS projects invokes invalidatePodInstallOutput if the plugins changed', + () async { + // Refresh the plugin list (we have no plugins). + await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true); + expect( + cocoaPods.capturedInvocations, + isEmpty, + reason: 'No plugins exist, so no invalidatePodInstallOutput calls expected.', + ); + + // Create an initial plugin (we previously had none). + createPlugin( + name: 'plugin-a', + platforms: const { + 'ios': _PluginPlatformInfo( + pluginClass: 'Foo', + dartPluginClass: 'Bar', + sharedDarwinSource: true, + ), + }, + ); + await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true); + expect( + cocoaPods.capturedInvocations, + containsAll([isA(), isA()]), + reason: 'A new plugin was added, so it should cause invalidatePodInstallOutput calls.', + ); + cocoaPods.capturedInvocations.clear(); + + // Add a new plugin. + createPlugin( + name: 'plugin-b', + platforms: const { + 'ios': _PluginPlatformInfo( + pluginClass: 'Foo', + dartPluginClass: 'Bar', + sharedDarwinSource: true, + ), + }, + ); + await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true); + expect( + cocoaPods.capturedInvocations, + containsAll([isA(), isA()]), + reason: 'A new plugin was added, so it should cause invalidatePodInstallOutput calls.', + ); + cocoaPods.capturedInvocations.clear(); + + // Do not add new plugins. + await refreshPluginsList(flutterProject, iosPlatform: true, macOSPlatform: true); + expect( + cocoaPods.capturedInvocations, + isEmpty, + reason: 'No plugins changed, so no updates expected', + ); + }, + overrides: { + CocoaPods: () => cocoaPods, + FileSystem: () => fs, + ProcessManager: () => FakeProcessManager.any(), + SystemClock: () => systemClock, + FlutterVersion: () => flutterVersion, + Pub: FakePubWithPrimedDeps.new, + // TODO(matanlurey): Remove as part of https://github.com/flutter/flutter/issues/160257. + // Not necessary, you can observe this bug by calling `generateLegacyPlugins: false`, + // but since this flag is about to be enabled, and enabling it implicitly sets that + // argument to false, this is a more "honest" test. + FeatureFlags: enableExplicitPackageDependencies, + }, + ); + testUsingContext( '.flutter-plugins-dependencies contains plugin platform info', () async { @@ -3004,3 +3080,13 @@ class FakeDarwinDependencyManagement extends Fake implements DarwinDependencyMan setupPlatforms.add(platform); } } + +/// A fake of [CocoaPods] that writes calls to [invalidatePodInstallOutput] to [capturedInvocations]. +final class FakeCocoaPodsCapturesInvalidate extends Fake implements CocoaPods { + final List capturedInvocations = []; + + @override + void invalidatePodInstallOutput(XcodeBasedProject xcodeProject) { + capturedInvocations.add(xcodeProject); + } +}