Unverified Commit fcf341e4 authored by Francisco Magdaleno's avatar Francisco Magdaleno Committed by GitHub

Reland "[flutter_tools] Removes the need of a no-op plugin implementations #48614" (#49085)

parent 2b998405
......@@ -3,6 +3,7 @@
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter_devicelab/framework/framework.dart';
......@@ -74,10 +75,6 @@ Future<void> main() async {
);
});
// https://github.com/flutter/flutter/issues/46898
// https://github.com/flutter/flutter/issues/39657
File(path.join(pluginCDirectory.path, 'android', 'build.gradle')).deleteSync();
final File pluginCpubspec = File(path.join(pluginCDirectory.path, 'pubspec.yaml'));
await pluginCpubspec.writeAsString('''
name: plugin_c
......@@ -177,30 +174,32 @@ public class DummyPluginAClass {
}
final String flutterPluginsDependenciesFileContent = flutterPluginsDependenciesFile.readAsStringSync();
final Map<String, dynamic> jsonContent = json.decode(flutterPluginsDependenciesFileContent) as Map<String, dynamic>;
// Verify the dependencyGraph object is valid. The rest of the contents of this file are not relevant to the
// dependency graph and are tested by unit tests.
final List<dynamic> dependencyGraph = jsonContent['dependencyGraph'] as List<dynamic>;
const String kExpectedPluginsDependenciesContent =
'{'
'\"_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\":[]'
'},'
'{'
'\"name\":\"plugin_c\",'
'\"dependencies\":[]'
'}'
']'
'}';
if (flutterPluginsDependenciesFileContent != kExpectedPluginsDependenciesContent) {
'['
'{'
'\"name\":\"plugin_a\",'
'\"dependencies\":[\"plugin_b\",\"plugin_c\"]'
'},'
'{'
'\"name\":\"plugin_b\",'
'\"dependencies\":[]'
'},'
'{'
'\"name\":\"plugin_c\",'
'\"dependencies\":[]'
'}'
']';
final String graphString = json.encode(dependencyGraph);
if (graphString != kExpectedPluginsDependenciesContent) {
return TaskResult.failure(
'Unexpected file content in ${flutterPluginsDependenciesFile.path}: '
'Found "$flutterPluginsDependenciesFileContent" instead of '
'"$kExpectedPluginsDependenciesContent"'
'Found "$graphString" instead of "$kExpectedPluginsDependenciesContent"'
);
}
......
......@@ -11,6 +11,7 @@ import 'package:yaml/yaml.dart';
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 'globals.dart' as globals;
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 @@ class Plugin {
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 @@ List<Plugin> findPlugins(FlutterProject project) {
}
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 @@ List<Plugin> findPlugins(FlutterProject project) {
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.
/// 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 = 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 .flutter-plugins or .flutter-plugins-dependencies have changed,
/// otherwise returns [false].
///
/// Finally, returns [true] if the plugins list has 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');
final File pluginsFile = project.flutterPluginsDependenciesFile;
if (plugins.isEmpty) {
if (pluginsFile.existsSync()) {
pluginsFile.deleteSync();
return true;
}
return false;
}
final Set<String> pluginNames = <String>{};
for (final Plugin plugin in plugins) {
pluginNames.add(plugin.name);
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');
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)
|| oldDependenciesFileContent != _readFileContent(dependenciesFile);
return oldPluginFileContent != _readFileContent(pluginsFile);
}
/// Returns the contents of [File] or [null] if that file does not exist.
......@@ -782,8 +892,13 @@ Future<void> _writeWebPluginRegistrant(FlutterProject project, List<Plugin> plug
/// 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);
}
......
......@@ -20,6 +20,7 @@ import 'flutter_manifest.dart';
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 @@ class FlutterProject {
}
}
/// 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 @@ abstract class XcodeBasedProject {
///
/// 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 @@ class IosProject implements XcodeBasedProject {
///
/// 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 @@ class AndroidProject {
}
/// Whether the current flutter project has an Android sub-project.
@override
bool existsSync() {
return parent.isModule || _editableHostAppDirectory.existsSync();
}
......@@ -760,12 +778,16 @@ enum AndroidEmbeddingVersion {
}
/// 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 @@ Match _firstMatchInFile(File file, RegExp regExp) {
}
/// 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 @@ class MacOSProject implements XcodeBasedProject {
}
/// 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 @@ class WindowsProject {
}
/// 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 @@ class LinuxProject {
/// checked in should live here.
Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral');
@override
bool existsSync() => _editableDirectory.existsSync();
/// The Linux project makefile.
......
......@@ -16,6 +16,8 @@ import 'cache.dart';
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(' '));
......
......@@ -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 @@ void main() {
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 @@ void main() {
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 @@ void main() {
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 @@ dependencies:
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 @@ dependencies:
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 @@ dependencies:
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 @@ dependencies:
'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 @@ dependencies:
}, 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 @@ dependencies:
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 MockIosProject extends Mock implements IosProject {}
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 {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment