Reland "[flutter_tools] Removes the need of a no-op plugin implementations #48614" (#49085)
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 {}