1 | // Copyright 2014 The Flutter 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 | |
5 | import 'package:file/file.dart'; |
6 | import 'package:file/memory.dart'; |
7 | import 'package:file_testing/file_testing.dart'; |
8 | import 'package:flutter_tools/src/base/logger.dart'; |
9 | import 'package:flutter_tools/src/build_info.dart'; |
10 | import 'package:flutter_tools/src/dart/pub.dart'; |
11 | import 'package:flutter_tools/src/flutter_manifest.dart'; |
12 | import 'package:flutter_tools/src/macos/cocoapod_utils.dart'; |
13 | import 'package:flutter_tools/src/macos/cocoapods.dart'; |
14 | import 'package:flutter_tools/src/project.dart'; |
15 | import 'package:test/fake.dart'; |
16 | |
17 | import '../../src/common.dart'; |
18 | import '../../src/context.dart'; |
19 | import '../../src/fake_pub_deps.dart'; |
20 | import '../../src/package_config.dart'; |
21 | |
22 | void main() { |
23 | group('processPodsIfNeeded', () { |
24 | late MemoryFileSystem fs; |
25 | late FakeCocoaPods cocoaPods; |
26 | late BufferLogger logger; |
27 | |
28 | // Adds basic properties to the flutterProject and its subprojects. |
29 | void setUpProject( |
30 | FakeFlutterProject flutterProject, |
31 | MemoryFileSystem fileSystem, { |
32 | List<String> pluginNames = const <String>[], |
33 | }) { |
34 | flutterProject |
35 | ..manifest = FakeFlutterManifest() |
36 | ..directory = fileSystem.systemTempDirectory.childDirectory('app') |
37 | ..flutterPluginsFile = flutterProject.directory.childFile('.flutter-plugins') |
38 | ..flutterPluginsDependenciesFile = flutterProject.directory.childFile( |
39 | '.flutter-plugins-dependencies', |
40 | ) |
41 | ..ios = FakeIosProject(fileSystem: fileSystem, parent: flutterProject) |
42 | ..macos = FakeMacOSProject(fileSystem: fileSystem, parent: flutterProject) |
43 | ..android = FakeAndroidProject() |
44 | ..web = FakeWebProject() |
45 | ..windows = FakeWindowsProject() |
46 | ..linux = FakeLinuxProject() |
47 | ..packageConfig = flutterProject.directory |
48 | .childDirectory('.dart_tool') |
49 | .childFile('package_config.json'); |
50 | |
51 | const String pluginYamlTemplate = ''' |
52 | flutter: |
53 | plugin: |
54 | platforms: |
55 | ios: |
56 | pluginClass: PLUGIN_CLASS |
57 | macos: |
58 | pluginClass: PLUGIN_CLASS |
59 | '''; |
60 | |
61 | final Directory fakePubCache = fileSystem.systemTempDirectory.childDirectory('cache'); |
62 | |
63 | writePackageConfigFile( |
64 | directory: flutterProject.directory, |
65 | mainLibName: 'my_app', |
66 | packages: <String, String>{ |
67 | for (final String plugin in pluginNames) |
68 | plugin: fakePubCache.childDirectory(plugin).uri.toString(), |
69 | }, |
70 | ); |
71 | |
72 | for (final String name in pluginNames) { |
73 | flutterProject.manifest.dependencies.add(name); |
74 | final Directory pluginDirectory = fakePubCache.childDirectory(name); |
75 | pluginDirectory.childFile('pubspec.yaml') |
76 | ..createSync(recursive: true) |
77 | ..writeAsStringSync(pluginYamlTemplate.replaceAll('PLUGIN_CLASS', name)); |
78 | } |
79 | } |
80 | |
81 | setUp(() async { |
82 | fs = MemoryFileSystem.test(); |
83 | cocoaPods = FakeCocoaPods(); |
84 | logger = BufferLogger.test(); |
85 | }); |
86 | |
87 | group('for iOS', () { |
88 | group('using CocoaPods only', () { |
89 | testUsingContext( |
90 | 'processes when there are plugins', |
91 | () async { |
92 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
93 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
94 | |
95 | await processPodsIfNeeded( |
96 | flutterProject.ios, |
97 | fs.currentDirectory.childDirectory('build').path, |
98 | BuildMode.debug, |
99 | ); |
100 | expect(cocoaPods.processedPods, isTrue); |
101 | }, |
102 | overrides: <Type, Generator>{ |
103 | FileSystem: () => fs, |
104 | ProcessManager: FakeProcessManager.empty, |
105 | Pub: FakePubWithPrimedDeps.new, |
106 | CocoaPods: () => cocoaPods, |
107 | }, |
108 | ); |
109 | |
110 | testUsingContext( |
111 | 'processes when no plugins but the project is a module and podfile exists', |
112 | () async { |
113 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
114 | setUpProject(flutterProject, fs); |
115 | flutterProject.isModule = true; |
116 | flutterProject.ios.podfile.createSync(recursive: true); |
117 | |
118 | await processPodsIfNeeded( |
119 | flutterProject.ios, |
120 | fs.currentDirectory.childDirectory('build').path, |
121 | BuildMode.debug, |
122 | ); |
123 | expect(cocoaPods.processedPods, isTrue); |
124 | }, |
125 | overrides: <Type, Generator>{ |
126 | FileSystem: () => fs, |
127 | ProcessManager: FakeProcessManager.empty, |
128 | Pub: FakePubWithPrimedDeps.new, |
129 | CocoaPods: () => cocoaPods, |
130 | }, |
131 | ); |
132 | |
133 | testUsingContext( |
134 | "skips when no plugins and the project is a module but podfile doesn't exist", |
135 | () async { |
136 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
137 | setUpProject(flutterProject, fs); |
138 | flutterProject.isModule = true; |
139 | |
140 | await processPodsIfNeeded( |
141 | flutterProject.ios, |
142 | fs.currentDirectory.childDirectory('build').path, |
143 | BuildMode.debug, |
144 | ); |
145 | expect(cocoaPods.processedPods, isFalse); |
146 | }, |
147 | overrides: <Type, Generator>{ |
148 | FileSystem: () => fs, |
149 | ProcessManager: FakeProcessManager.empty, |
150 | Pub: FakePubWithPrimedDeps.new, |
151 | CocoaPods: () => cocoaPods, |
152 | }, |
153 | ); |
154 | |
155 | testUsingContext( |
156 | 'skips when no plugins and project is not a module', |
157 | () async { |
158 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
159 | setUpProject(flutterProject, fs); |
160 | |
161 | await processPodsIfNeeded( |
162 | flutterProject.ios, |
163 | fs.currentDirectory.childDirectory('build').path, |
164 | BuildMode.debug, |
165 | ); |
166 | expect(cocoaPods.processedPods, isFalse); |
167 | }, |
168 | overrides: <Type, Generator>{ |
169 | FileSystem: () => fs, |
170 | ProcessManager: FakeProcessManager.empty, |
171 | Pub: FakePubWithPrimedDeps.new, |
172 | CocoaPods: () => cocoaPods, |
173 | }, |
174 | ); |
175 | }); |
176 | |
177 | group('using Swift Package Manager', () { |
178 | testUsingContext( |
179 | 'processes if podfile exists', |
180 | () async { |
181 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
182 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
183 | flutterProject.ios.usesSwiftPackageManager = true; |
184 | flutterProject.ios.podfile.createSync(recursive: true); |
185 | |
186 | await processPodsIfNeeded( |
187 | flutterProject.ios, |
188 | fs.currentDirectory.childDirectory('build').path, |
189 | BuildMode.debug, |
190 | ); |
191 | expect(cocoaPods.processedPods, isTrue); |
192 | }, |
193 | overrides: <Type, Generator>{ |
194 | FileSystem: () => fs, |
195 | ProcessManager: FakeProcessManager.empty, |
196 | Pub: FakePubWithPrimedDeps.new, |
197 | CocoaPods: () => cocoaPods, |
198 | }, |
199 | ); |
200 | |
201 | testUsingContext( |
202 | 'skip if podfile does not exists', |
203 | () async { |
204 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
205 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
206 | flutterProject.ios.usesSwiftPackageManager = true; |
207 | |
208 | await processPodsIfNeeded( |
209 | flutterProject.ios, |
210 | fs.currentDirectory.childDirectory('build').path, |
211 | BuildMode.debug, |
212 | ); |
213 | expect(cocoaPods.processedPods, isFalse); |
214 | }, |
215 | overrides: <Type, Generator>{ |
216 | FileSystem: () => fs, |
217 | ProcessManager: FakeProcessManager.empty, |
218 | CocoaPods: () => cocoaPods, |
219 | }, |
220 | ); |
221 | |
222 | testUsingContext( |
223 | 'process if podfile does not exists but forceCocoaPodsOnly is true', |
224 | () async { |
225 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
226 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
227 | flutterProject.ios.usesSwiftPackageManager = true; |
228 | final File generatedManifestFile = flutterProject.ios.flutterPluginSwiftPackageManifest; |
229 | generatedManifestFile.createSync(recursive: true); |
230 | |
231 | await processPodsIfNeeded( |
232 | flutterProject.ios, |
233 | fs.currentDirectory.childDirectory('build').path, |
234 | BuildMode.debug, |
235 | forceCocoaPodsOnly: true, |
236 | ); |
237 | expect(cocoaPods.processedPods, isTrue); |
238 | expect(cocoaPods.podfileSetup, isTrue); |
239 | expect( |
240 | logger.warningText, |
241 | 'Swift Package Manager does not yet support this command. ' |
242 | 'CocoaPods will be used instead.\n', |
243 | ); |
244 | expect(generatedManifestFile, exists); |
245 | const String emptyDependencies = 'dependencies: [\n \n ],\n'; |
246 | expect(generatedManifestFile.readAsStringSync(), contains(emptyDependencies)); |
247 | }, |
248 | overrides: <Type, Generator>{ |
249 | FileSystem: () => fs, |
250 | ProcessManager: FakeProcessManager.empty, |
251 | Pub: FakePubWithPrimedDeps.new, |
252 | CocoaPods: () => cocoaPods, |
253 | Logger: () => logger, |
254 | }, |
255 | ); |
256 | }); |
257 | }); |
258 | |
259 | group('for macOS', () { |
260 | group('using CocoaPods only', () { |
261 | testUsingContext( |
262 | 'processes when there are plugins', |
263 | () async { |
264 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
265 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
266 | |
267 | await processPodsIfNeeded( |
268 | flutterProject.macos, |
269 | fs.currentDirectory.childDirectory('build').path, |
270 | BuildMode.debug, |
271 | ); |
272 | expect(cocoaPods.processedPods, isTrue); |
273 | }, |
274 | overrides: <Type, Generator>{ |
275 | FileSystem: () => fs, |
276 | ProcessManager: FakeProcessManager.empty, |
277 | Pub: FakePubWithPrimedDeps.new, |
278 | CocoaPods: () => cocoaPods, |
279 | }, |
280 | ); |
281 | |
282 | testUsingContext( |
283 | 'processes when no plugins but the project is a module and podfile exists', |
284 | () async { |
285 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
286 | setUpProject(flutterProject, fs); |
287 | flutterProject.isModule = true; |
288 | flutterProject.macos.podfile.createSync(recursive: true); |
289 | |
290 | await processPodsIfNeeded( |
291 | flutterProject.macos, |
292 | fs.currentDirectory.childDirectory('build').path, |
293 | BuildMode.debug, |
294 | ); |
295 | expect(cocoaPods.processedPods, isTrue); |
296 | }, |
297 | overrides: <Type, Generator>{ |
298 | FileSystem: () => fs, |
299 | ProcessManager: FakeProcessManager.empty, |
300 | Pub: FakePubWithPrimedDeps.new, |
301 | CocoaPods: () => cocoaPods, |
302 | }, |
303 | ); |
304 | |
305 | testUsingContext( |
306 | "skips when no plugins and the project is a module but podfile doesn't exist", |
307 | () async { |
308 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
309 | setUpProject(flutterProject, fs); |
310 | flutterProject.isModule = true; |
311 | |
312 | await processPodsIfNeeded( |
313 | flutterProject.macos, |
314 | fs.currentDirectory.childDirectory('build').path, |
315 | BuildMode.debug, |
316 | ); |
317 | expect(cocoaPods.processedPods, isFalse); |
318 | }, |
319 | overrides: <Type, Generator>{ |
320 | FileSystem: () => fs, |
321 | ProcessManager: FakeProcessManager.empty, |
322 | Pub: FakePubWithPrimedDeps.new, |
323 | CocoaPods: () => cocoaPods, |
324 | }, |
325 | ); |
326 | |
327 | testUsingContext( |
328 | 'skips when no plugins and project is not a module', |
329 | () async { |
330 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
331 | setUpProject(flutterProject, fs); |
332 | |
333 | await processPodsIfNeeded( |
334 | flutterProject.macos, |
335 | fs.currentDirectory.childDirectory('build').path, |
336 | BuildMode.debug, |
337 | ); |
338 | expect(cocoaPods.processedPods, isFalse); |
339 | }, |
340 | overrides: <Type, Generator>{ |
341 | FileSystem: () => fs, |
342 | ProcessManager: FakeProcessManager.empty, |
343 | Pub: FakePubWithPrimedDeps.new, |
344 | CocoaPods: () => cocoaPods, |
345 | }, |
346 | ); |
347 | }); |
348 | |
349 | group('using Swift Package Manager', () { |
350 | testUsingContext( |
351 | 'processes if podfile exists', |
352 | () async { |
353 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
354 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
355 | flutterProject.macos.usesSwiftPackageManager = true; |
356 | flutterProject.macos.podfile.createSync(recursive: true); |
357 | |
358 | await processPodsIfNeeded( |
359 | flutterProject.macos, |
360 | fs.currentDirectory.childDirectory('build').path, |
361 | BuildMode.debug, |
362 | ); |
363 | expect(cocoaPods.processedPods, isTrue); |
364 | }, |
365 | overrides: <Type, Generator>{ |
366 | FileSystem: () => fs, |
367 | ProcessManager: FakeProcessManager.empty, |
368 | Pub: FakePubWithPrimedDeps.new, |
369 | CocoaPods: () => cocoaPods, |
370 | }, |
371 | ); |
372 | |
373 | testUsingContext( |
374 | 'skip if podfile does not exists', |
375 | () async { |
376 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
377 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
378 | flutterProject.macos.usesSwiftPackageManager = true; |
379 | |
380 | await processPodsIfNeeded( |
381 | flutterProject.macos, |
382 | fs.currentDirectory.childDirectory('build').path, |
383 | BuildMode.debug, |
384 | ); |
385 | expect(cocoaPods.processedPods, isFalse); |
386 | }, |
387 | overrides: <Type, Generator>{ |
388 | FileSystem: () => fs, |
389 | ProcessManager: FakeProcessManager.empty, |
390 | CocoaPods: () => cocoaPods, |
391 | }, |
392 | ); |
393 | |
394 | testUsingContext( |
395 | 'process if podfile does not exists but forceCocoaPodsOnly is true', |
396 | () async { |
397 | final FakeFlutterProject flutterProject = FakeFlutterProject(); |
398 | setUpProject(flutterProject, fs, pluginNames: <String>['plugin_one', 'plugin_two']); |
399 | flutterProject.macos.usesSwiftPackageManager = true; |
400 | final File generatedManifestFile = |
401 | flutterProject.macos.flutterPluginSwiftPackageManifest; |
402 | generatedManifestFile.createSync(recursive: true); |
403 | |
404 | await processPodsIfNeeded( |
405 | flutterProject.macos, |
406 | fs.currentDirectory.childDirectory('build').path, |
407 | BuildMode.debug, |
408 | forceCocoaPodsOnly: true, |
409 | ); |
410 | expect(cocoaPods.processedPods, isTrue); |
411 | expect(cocoaPods.podfileSetup, isTrue); |
412 | expect( |
413 | logger.warningText, |
414 | 'Swift Package Manager does not yet support this command. ' |
415 | 'CocoaPods will be used instead.\n', |
416 | ); |
417 | |
418 | expect(generatedManifestFile, exists); |
419 | const String emptyDependencies = 'dependencies: [\n \n ],\n'; |
420 | expect(generatedManifestFile.readAsStringSync(), contains(emptyDependencies)); |
421 | }, |
422 | overrides: <Type, Generator>{ |
423 | FileSystem: () => fs, |
424 | ProcessManager: FakeProcessManager.empty, |
425 | Pub: FakePubWithPrimedDeps.new, |
426 | CocoaPods: () => cocoaPods, |
427 | Logger: () => logger, |
428 | }, |
429 | ); |
430 | }); |
431 | }); |
432 | }); |
433 | } |
434 | |
435 | class FakeFlutterManifest extends Fake implements FlutterManifest { |
436 | @override |
437 | Set<String> get dependencies => <String>{}; |
438 | } |
439 | |
440 | class FakeFlutterProject extends Fake implements FlutterProject { |
441 | @override |
442 | bool isModule = false; |
443 | |
444 | @override |
445 | late FlutterManifest manifest; |
446 | |
447 | @override |
448 | late Directory directory; |
449 | |
450 | @override |
451 | late File flutterPluginsFile; |
452 | |
453 | @override |
454 | late File flutterPluginsDependenciesFile; |
455 | |
456 | @override |
457 | late FakeIosProject ios; |
458 | |
459 | @override |
460 | late FakeMacOSProject macos; |
461 | |
462 | @override |
463 | late AndroidProject android; |
464 | |
465 | @override |
466 | late WebProject web; |
467 | |
468 | @override |
469 | late LinuxProject linux; |
470 | |
471 | @override |
472 | late WindowsProject windows; |
473 | |
474 | @override |
475 | late File packageConfig; |
476 | } |
477 | |
478 | class FakeMacOSProject extends Fake implements MacOSProject { |
479 | FakeMacOSProject({required MemoryFileSystem fileSystem, required this.parent}) |
480 | : hostAppRoot = fileSystem.directory('app_name').childDirectory( 'ios'); |
481 | |
482 | @override |
483 | String pluginConfigKey = 'macos'; |
484 | |
485 | @override |
486 | final FlutterProject parent; |
487 | |
488 | @override |
489 | Directory hostAppRoot; |
490 | |
491 | bool exists = true; |
492 | |
493 | @override |
494 | bool existsSync() => exists; |
495 | |
496 | @override |
497 | File get podfile => hostAppRoot.childFile('Podfile'); |
498 | |
499 | @override |
500 | File get xcodeProjectInfoFile => |
501 | hostAppRoot.childDirectory('Runner.xcodeproj').childFile( 'project.pbxproj'); |
502 | |
503 | @override |
504 | File get flutterPluginSwiftPackageManifest => hostAppRoot |
505 | .childDirectory('Flutter') |
506 | .childDirectory('ephemeral') |
507 | .childDirectory('Packages') |
508 | .childDirectory('FlutterGeneratedPluginSwiftPackage') |
509 | .childFile('Package.swift'); |
510 | |
511 | @override |
512 | bool usesSwiftPackageManager = false; |
513 | |
514 | @override |
515 | bool get flutterPluginSwiftPackageInProjectSettings => usesSwiftPackageManager; |
516 | } |
517 | |
518 | class FakeIosProject extends Fake implements IosProject { |
519 | FakeIosProject({required MemoryFileSystem fileSystem, required this.parent}) |
520 | : hostAppRoot = fileSystem.directory('app_name').childDirectory( 'ios'); |
521 | |
522 | @override |
523 | String pluginConfigKey = 'ios'; |
524 | |
525 | @override |
526 | final FlutterProject parent; |
527 | |
528 | @override |
529 | Directory hostAppRoot; |
530 | |
531 | @override |
532 | bool exists = true; |
533 | |
534 | @override |
535 | bool existsSync() => exists; |
536 | |
537 | @override |
538 | File get podfile => hostAppRoot.childFile('Podfile'); |
539 | |
540 | @override |
541 | File get xcodeProjectInfoFile => |
542 | hostAppRoot.childDirectory('Runner.xcodeproj').childFile( 'project.pbxproj'); |
543 | |
544 | @override |
545 | File get flutterPluginSwiftPackageManifest => hostAppRoot |
546 | .childDirectory('Flutter') |
547 | .childDirectory('ephemeral') |
548 | .childDirectory('Packages') |
549 | .childDirectory('FlutterGeneratedPluginSwiftPackage') |
550 | .childFile('Package.swift'); |
551 | |
552 | @override |
553 | bool usesSwiftPackageManager = false; |
554 | |
555 | @override |
556 | bool get flutterPluginSwiftPackageInProjectSettings => usesSwiftPackageManager; |
557 | } |
558 | |
559 | class FakeAndroidProject extends Fake implements AndroidProject { |
560 | @override |
561 | String pluginConfigKey = 'android'; |
562 | |
563 | @override |
564 | bool existsSync() => false; |
565 | } |
566 | |
567 | class FakeWebProject extends Fake implements WebProject { |
568 | @override |
569 | String pluginConfigKey = 'web'; |
570 | |
571 | @override |
572 | bool existsSync() => false; |
573 | } |
574 | |
575 | class FakeWindowsProject extends Fake implements WindowsProject { |
576 | @override |
577 | String pluginConfigKey = 'windows'; |
578 | |
579 | @override |
580 | bool existsSync() => false; |
581 | } |
582 | |
583 | class FakeLinuxProject extends Fake implements LinuxProject { |
584 | @override |
585 | String pluginConfigKey = 'linux'; |
586 | |
587 | @override |
588 | bool existsSync() => false; |
589 | } |
590 | |
591 | class FakeCocoaPods extends Fake implements CocoaPods { |
592 | bool podfileSetup = false; |
593 | bool processedPods = false; |
594 | |
595 | @override |
596 | Future<bool> processPods({ |
597 | required XcodeBasedProject xcodeProject, |
598 | required BuildMode buildMode, |
599 | bool dependenciesChanged = true, |
600 | }) async { |
601 | processedPods = true; |
602 | return true; |
603 | } |
604 | |
605 | @override |
606 | Future<void> setupPodfile(XcodeBasedProject xcodeProject) async { |
607 | podfileSetup = true; |
608 | } |
609 | |
610 | @override |
611 | void invalidatePodInstallOutput(XcodeBasedProject xcodeProject) {} |
612 | } |
613 |
Definitions
- main
- setUpProject
- FakeFlutterManifest
- dependencies
- FakeFlutterProject
- FakeMacOSProject
- FakeMacOSProject
- existsSync
- podfile
- xcodeProjectInfoFile
- flutterPluginSwiftPackageManifest
- flutterPluginSwiftPackageInProjectSettings
- FakeIosProject
- FakeIosProject
- existsSync
- podfile
- xcodeProjectInfoFile
- flutterPluginSwiftPackageManifest
- flutterPluginSwiftPackageInProjectSettings
- FakeAndroidProject
- existsSync
- FakeWebProject
- existsSync
- FakeWindowsProject
- existsSync
- FakeLinuxProject
- existsSync
- FakeCocoaPods
- processPods
- setupPodfile
Learn more about Flutter for embedded and desktop on industrialflutter.com