blob: 96216ed957d91a68e66c9ac0a8713623501d09b0 [file] [log] [blame]
stuartmorganc32e5432019-09-18 21:42:57 -07001// Copyright 2019 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5import 'package:file/file.dart';
6import 'package:file/memory.dart';
7import 'package:flutter_tools/src/dart/package_map.dart';
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -07008import 'package:flutter_tools/src/features.dart';
9import 'package:flutter_tools/src/ios/xcodeproj.dart';
stuartmorganc32e5432019-09-18 21:42:57 -070010import 'package:flutter_tools/src/plugins.dart';
11import 'package:flutter_tools/src/project.dart';
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -070012
stuartmorganc32e5432019-09-18 21:42:57 -070013import 'package:mockito/mockito.dart';
14
15import '../src/common.dart';
16import '../src/context.dart';
17
stuartmorganc32e5432019-09-18 21:42:57 -070018void main() {
19 FileSystem fs;
20 MockFlutterProject flutterProject;
21 MockIosProject iosProject;
22 MockMacOSProject macosProject;
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -070023 MockAndroidProject androidProject;
stuartmorganc32e5432019-09-18 21:42:57 -070024 File packagesFile;
25 Directory dummyPackageDirectory;
26
27 setUp(() async {
28 fs = MemoryFileSystem();
29
30 // Add basic properties to the Flutter project and subprojects
31 flutterProject = MockFlutterProject();
32 when(flutterProject.directory).thenReturn(fs.directory('/'));
33 when(flutterProject.flutterPluginsFile).thenReturn(flutterProject.directory.childFile('.plugins'));
34 iosProject = MockIosProject();
35 when(flutterProject.ios).thenReturn(iosProject);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -070036 when(iosProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('Runner'));
37 when(iosProject.podfile).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile'));
stuartmorganc32e5432019-09-18 21:42:57 -070038 when(iosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('ios').childFile('Podfile.lock'));
39 macosProject = MockMacOSProject();
40 when(flutterProject.macos).thenReturn(macosProject);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -070041 when(macosProject.podfile).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile'));
stuartmorganc32e5432019-09-18 21:42:57 -070042 when(macosProject.podManifestLock).thenReturn(flutterProject.directory.childDirectory('macos').childFile('Podfile.lock'));
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -070043 androidProject = MockAndroidProject();
44 when(flutterProject.android).thenReturn(androidProject);
45 when(androidProject.pluginRegistrantHost).thenReturn(flutterProject.directory.childDirectory('android').childDirectory('app'));
46 when(androidProject.hostAppGradleRoot).thenReturn(flutterProject.directory.childDirectory('android'));
stuartmorganc32e5432019-09-18 21:42:57 -070047
48 // Set up a simple .packages file for all the tests to use, pointing to one package.
49 dummyPackageDirectory = fs.directory('/pubcache/apackage/lib/');
50 packagesFile = fs.file(fs.path.join(flutterProject.directory.path, PackageMap.globalPackagesPath));
51 packagesFile..createSync(recursive: true)
52 ..writeAsStringSync('apackage:file://${dummyPackageDirectory.path}');
53 });
54
55 // Makes the dummy package pointed to by packagesFile look like a plugin.
56 void configureDummyPackageAsPlugin() {
57 dummyPackageDirectory.parent.childFile('pubspec.yaml')..createSync(recursive: true)..writeAsStringSync('''
58flutter:
59 plugin:
60 platforms:
61 ios:
62 pluginClass: FLESomePlugin
63''');
64 }
65
66 // Creates the files that would indicate that pod install has run for the
67 // given project.
68 void simulatePodInstallRun(XcodeBasedProject project) {
69 project.podManifestLock.createSync(recursive: true);
70 }
71
72 group('refreshPlugins', () {
73 testUsingContext('Refreshing the plugin list is a no-op when the plugins list stays empty', () {
74 refreshPluginsList(flutterProject);
75 expect(flutterProject.flutterPluginsFile.existsSync(), false);
76 }, overrides: <Type, Generator>{
77 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -070078 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
stuartmorganc32e5432019-09-18 21:42:57 -070079 });
80
81 testUsingContext('Refreshing the plugin list deletes the plugin file when there were plugins but no longer are', () {
82 flutterProject.flutterPluginsFile.createSync();
83 when(iosProject.existsSync()).thenReturn(false);
84 when(macosProject.existsSync()).thenReturn(false);
85 refreshPluginsList(flutterProject);
86 expect(flutterProject.flutterPluginsFile.existsSync(), false);
87 }, overrides: <Type, Generator>{
88 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -070089 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
stuartmorganc32e5432019-09-18 21:42:57 -070090 });
91
92 testUsingContext('Refreshing the plugin list creates a plugin directory when there are plugins', () {
93 configureDummyPackageAsPlugin();
94 when(iosProject.existsSync()).thenReturn(false);
95 when(macosProject.existsSync()).thenReturn(false);
96 refreshPluginsList(flutterProject);
97 expect(flutterProject.flutterPluginsFile.existsSync(), true);
98 }, overrides: <Type, Generator>{
99 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700100 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
stuartmorganc32e5432019-09-18 21:42:57 -0700101 });
102
103 testUsingContext('Changes to the plugin list invalidates the Cocoapod lockfiles', () {
104 simulatePodInstallRun(iosProject);
105 simulatePodInstallRun(macosProject);
106 configureDummyPackageAsPlugin();
107 when(iosProject.existsSync()).thenReturn(true);
108 when(macosProject.existsSync()).thenReturn(true);
109 refreshPluginsList(flutterProject);
110 expect(iosProject.podManifestLock.existsSync(), false);
111 expect(macosProject.podManifestLock.existsSync(), false);
112 }, overrides: <Type, Generator>{
113 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700114 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
stuartmorganc32e5432019-09-18 21:42:57 -0700115 });
116 });
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700117
118 group('injectPlugins', () {
119 MockFeatureFlags featureFlags;
120 MockXcodeProjectInterpreter xcodeProjectInterpreter;
121
122 const String kAndroidManifestUsingOldEmbedding = '''
123<manifest>
124 <application>
125 </application>
126</manifest>
127''';
128 const String kAndroidManifestUsingNewEmbedding = '''
129<manifest>
130 <application>
131 <meta-data
132 android:name="flutterEmbedding"
133 android:value="2" />
134 </application>
135</manifest>
136''';
137
138 setUp(() {
139 featureFlags = MockFeatureFlags();
140 when(featureFlags.isLinuxEnabled).thenReturn(false);
141 when(featureFlags.isMacOSEnabled).thenReturn(false);
142 when(featureFlags.isWindowsEnabled).thenReturn(false);
143 when(featureFlags.isWebEnabled).thenReturn(false);
144
145 xcodeProjectInterpreter = MockXcodeProjectInterpreter();
146 when(xcodeProjectInterpreter.isInstalled).thenReturn(false);
147 });
148
149 testUsingContext('Registrant uses old embedding in app project', () async {
150 when(flutterProject.isModule).thenReturn(false);
Emmanuel Garcia0a93f4e2019-10-16 12:47:04 -0700151
152 final File androidManifest = flutterProject.directory
153 .childDirectory('android')
154 .childFile('AndroidManifest.xml')
155 ..createSync(recursive: true)
156 ..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
157 when(androidProject.appManifestFile).thenReturn(androidManifest);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700158
159 await injectPlugins(flutterProject);
160
161 final File registrant = flutterProject.directory
162 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
163 .childFile('GeneratedPluginRegistrant.java');
164
165 expect(registrant.existsSync(), isTrue);
166 expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
167 expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
168 }, overrides: <Type, Generator>{
169 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700170 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700171 FeatureFlags: () => featureFlags,
172 });
173
174 testUsingContext('Registrant uses new embedding if app uses new embedding', () async {
175 when(flutterProject.isModule).thenReturn(false);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700176
177 final File androidManifest = flutterProject.directory
178 .childDirectory('android')
179 .childFile('AndroidManifest.xml')
180 ..createSync(recursive: true)
181 ..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
182 when(androidProject.appManifestFile).thenReturn(androidManifest);
183
184 await injectPlugins(flutterProject);
185
186 final File registrant = flutterProject.directory
187 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
188 .childFile('GeneratedPluginRegistrant.java');
189
190 expect(registrant.existsSync(), isTrue);
191 expect(registrant.readAsStringSync(), contains('package dev.flutter.plugins'));
192 expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
193 }, overrides: <Type, Generator>{
194 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700195 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700196 FeatureFlags: () => featureFlags,
197 });
198
199 testUsingContext('Registrant uses shim for plugins using old embedding if app uses new embedding', () async {
200 when(flutterProject.isModule).thenReturn(false);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700201
202 final File androidManifest = flutterProject.directory
203 .childDirectory('android')
204 .childFile('AndroidManifest.xml')
205 ..createSync(recursive: true)
206 ..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
207 when(androidProject.appManifestFile).thenReturn(androidManifest);
208
209 final Directory pluginUsingJavaAndNewEmbeddingDir =
Ian Hicksoncdc2d992019-10-07 16:43:04 -0700210 fs.systemTempDirectory.createTempSync('flutter_plugin_using_java_and_new_embedding_dir.');
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700211 pluginUsingJavaAndNewEmbeddingDir
212 .childFile('pubspec.yaml')
213 .writeAsStringSync('''
214flutter:
215 plugin:
216 androidPackage: plugin1
217 pluginClass: UseNewEmbedding
218''');
219 pluginUsingJavaAndNewEmbeddingDir
220 .childDirectory('android')
221 .childDirectory('src')
222 .childDirectory('main')
223 .childDirectory('java')
224 .childDirectory('plugin1')
225 .childFile('UseNewEmbedding.java')
226 ..createSync(recursive: true)
227 ..writeAsStringSync('import io.flutter.embedding.engine.plugins.FlutterPlugin;');
228
229 final Directory pluginUsingKotlinAndNewEmbeddingDir =
Ian Hicksoncdc2d992019-10-07 16:43:04 -0700230 fs.systemTempDirectory.createTempSync('flutter_plugin_using_kotlin_and_new_embedding_dir.');
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700231 pluginUsingKotlinAndNewEmbeddingDir
232 .childFile('pubspec.yaml')
233 .writeAsStringSync('''
234flutter:
235 plugin:
236 androidPackage: plugin2
237 pluginClass: UseNewEmbedding
238''');
239 pluginUsingKotlinAndNewEmbeddingDir
240 .childDirectory('android')
241 .childDirectory('src')
242 .childDirectory('main')
243 .childDirectory('kotlin')
244 .childDirectory('plugin2')
245 .childFile('UseNewEmbedding.kt')
246 ..createSync(recursive: true)
247 ..writeAsStringSync('import io.flutter.embedding.engine.plugins.FlutterPlugin');
248
249 final Directory pluginUsingOldEmbeddingDir =
Ian Hicksoncdc2d992019-10-07 16:43:04 -0700250 fs.systemTempDirectory.createTempSync('flutter_plugin_using_old_embedding_dir.');
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700251 pluginUsingOldEmbeddingDir
252 .childFile('pubspec.yaml')
253 .writeAsStringSync('''
254flutter:
255 plugin:
256 androidPackage: plugin3
257 pluginClass: UseOldEmbedding
258''');
259 pluginUsingOldEmbeddingDir
260 .childDirectory('android')
261 .childDirectory('src')
262 .childDirectory('main')
263 .childDirectory('java')
264 .childDirectory('plugin3')
265 .childFile('UseOldEmbedding.java')
266 ..createSync(recursive: true);
267
268 flutterProject.directory
269 .childFile('.packages')
270 .writeAsStringSync('''
271plugin1:${pluginUsingJavaAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
272plugin2:${pluginUsingKotlinAndNewEmbeddingDir.childDirectory('lib').uri.toString()}
273plugin3:${pluginUsingOldEmbeddingDir.childDirectory('lib').uri.toString()}
274''');
275
276 await injectPlugins(flutterProject);
277
278 final File registrant = flutterProject.directory
279 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
280 .childFile('GeneratedPluginRegistrant.java');
281
282 expect(registrant.readAsStringSync(),
283 contains('flutterEngine.getPlugins().add(new plugin1.UseNewEmbedding());'));
284 expect(registrant.readAsStringSync(),
285 contains('flutterEngine.getPlugins().add(new plugin2.UseNewEmbedding());'));
286 expect(registrant.readAsStringSync(),
287 contains('plugin3.UseOldEmbedding.registerWith(shimPluginRegistry.registrarFor("plugin3.UseOldEmbedding"));'));
288
289 }, overrides: <Type, Generator>{
290 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700291 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700292 FeatureFlags: () => featureFlags,
293 XcodeProjectInterpreter: () => xcodeProjectInterpreter,
294 });
295
296 testUsingContext('Registrant doesn\'t use new embedding if app doesn\'t use new embedding', () async {
297 when(flutterProject.isModule).thenReturn(false);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700298
299 final File androidManifest = flutterProject.directory
300 .childDirectory('android')
301 .childFile('AndroidManifest.xml')
302 ..createSync(recursive: true)
303 ..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
304 when(androidProject.appManifestFile).thenReturn(androidManifest);
305
306 await injectPlugins(flutterProject);
307
308 final File registrant = flutterProject.directory
309 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
310 .childFile('GeneratedPluginRegistrant.java');
311
312 expect(registrant.existsSync(), isTrue);
313 expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
314 expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
315 }, overrides: <Type, Generator>{
316 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700317 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700318 FeatureFlags: () => featureFlags,
319 });
320
321 testUsingContext('Registrant uses old embedding in module project', () async {
322 when(flutterProject.isModule).thenReturn(true);
Emmanuel Garcia0a93f4e2019-10-16 12:47:04 -0700323
324 final File androidManifest = flutterProject.directory
325 .childDirectory('android')
326 .childFile('AndroidManifest.xml')
327 ..createSync(recursive: true)
328 ..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
329 when(androidProject.appManifestFile).thenReturn(androidManifest);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700330
331 await injectPlugins(flutterProject);
332
333 final File registrant = flutterProject.directory
334 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
335 .childFile('GeneratedPluginRegistrant.java');
336
337 expect(registrant.existsSync(), isTrue);
338 expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
339 expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
340 }, overrides: <Type, Generator>{
341 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700342 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700343 FeatureFlags: () => featureFlags,
344 });
345
346 testUsingContext('Registrant uses new embedding if module uses new embedding', () async {
347 when(flutterProject.isModule).thenReturn(true);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700348
349 final File androidManifest = flutterProject.directory
350 .childDirectory('android')
351 .childFile('AndroidManifest.xml')
352 ..createSync(recursive: true)
353 ..writeAsStringSync(kAndroidManifestUsingNewEmbedding);
354 when(androidProject.appManifestFile).thenReturn(androidManifest);
355
356 await injectPlugins(flutterProject);
357
358 final File registrant = flutterProject.directory
359 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'dev', 'flutter', 'plugins'))
360 .childFile('GeneratedPluginRegistrant.java');
361
362 expect(registrant.existsSync(), isTrue);
363 expect(registrant.readAsStringSync(), contains('package dev.flutter.plugins'));
364 expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
365 }, overrides: <Type, Generator>{
366 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700367 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700368 FeatureFlags: () => featureFlags,
369 });
370
371 testUsingContext('Registrant doesn\'t use new embedding if module doesn\'t use new embedding', () async {
372 when(flutterProject.isModule).thenReturn(true);
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700373
374 final File androidManifest = flutterProject.directory
375 .childDirectory('android')
376 .childFile('AndroidManifest.xml')
377 ..createSync(recursive: true)
378 ..writeAsStringSync(kAndroidManifestUsingOldEmbedding);
379 when(androidProject.appManifestFile).thenReturn(androidManifest);
380
381 await injectPlugins(flutterProject);
382
383 final File registrant = flutterProject.directory
384 .childDirectory(fs.path.join('android', 'app', 'src', 'main', 'java', 'io', 'flutter', 'plugins'))
385 .childFile('GeneratedPluginRegistrant.java');
386
387 expect(registrant.existsSync(), isTrue);
388 expect(registrant.readAsStringSync(), contains('package io.flutter.plugins'));
389 expect(registrant.readAsStringSync(), contains('class GeneratedPluginRegistrant'));
390 }, overrides: <Type, Generator>{
391 FileSystem: () => fs,
Ian Hickson08643c42019-10-11 11:23:12 -0700392 ProcessManager: () => FakeProcessManager(<FakeCommand>[]),
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700393 FeatureFlags: () => featureFlags,
394 });
395 });
stuartmorganc32e5432019-09-18 21:42:57 -0700396}
Emmanuel Garcia5961bcc2019-10-04 06:23:03 -0700397
398class MockAndroidProject extends Mock implements AndroidProject {}
399class MockFeatureFlags extends Mock implements FeatureFlags {}
400class MockFlutterProject extends Mock implements FlutterProject {}
401class MockIosProject extends Mock implements IosProject {}
402class MockMacOSProject extends Mock implements MacOSProject {}
403class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}