Files
packages/script/tool/test/fetch_deps_command_test.dart
Jenn Magder 0009545dd2 [ci] Run 'flutter build --config-only for iOS and macOS during fetch deps (#5804)
For iOS and macOS move fetch deps from explicity running `pub get` and `pod install` to instead run `flutter build x --config-only` which smartly fetches deps and lays down the required native pieces.  I didn't guard it on whether certain files are present since the command already has similar guards, and runs pretty fast if the native code has already been generated.

This fixes an issue where `pod install` is run before the Flutter native bits and migrations are run.

Fixes issues seen in the Flutter roll https://github.com/flutter/packages/pull/5792#issuecomment-1876103613
```
Running command: "pod install" in /Volumes/Work/s/w/ir/x/w/packages/packages/camera/camera_avfoundation/example/ios
Analyzing dependencies
[!] CocoaPods could not find compatible versions for pod "Flutter":
  In Podfile:
    Flutter (from `Flutter`)

Specs satisfying the `Flutter (from `Flutter`)` dependency were found, but they required a higher minimum deployment target.

[!] Automatically assigning platform `iOS` with version `11.0` on target `Runner` because no platform was specified. Please specify a platform for this target in your Podfile. See `https://guides.cocoapods.org/syntax/podfile.html#platform`.
Unable to "pod install"
```

Caused by https://github.com/flutter/flutter/pull/140478
2024-01-05 00:45:12 +00:00

598 lines
19 KiB
Dart

// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:args/command_runner.dart';
import 'package:file/file.dart';
import 'package:file/memory.dart';
import 'package:flutter_plugin_tools/src/common/core.dart';
import 'package:flutter_plugin_tools/src/common/plugin_utils.dart';
import 'package:flutter_plugin_tools/src/fetch_deps_command.dart';
import 'package:test/test.dart';
import 'mocks.dart';
import 'util.dart';
void main() {
group('FetchDepsCommand', () {
FileSystem fileSystem;
late Directory packagesDir;
late CommandRunner<void> runner;
late MockPlatform mockPlatform;
late RecordingProcessRunner processRunner;
setUp(() {
fileSystem = MemoryFileSystem();
packagesDir = createPackagesDirectory(fileSystem: fileSystem);
mockPlatform = MockPlatform();
processRunner = RecordingProcessRunner();
final FetchDepsCommand command = FetchDepsCommand(
packagesDir,
processRunner: processRunner,
platform: mockPlatform,
);
runner =
CommandRunner<void>('fetch_deps_test', 'Test for $FetchDepsCommand');
runner.addCommand(command);
});
group('dart', () {
test('runs pub get', () async {
final RepositoryPackage plugin = createFakePlugin(
'plugin1', packagesDir, platformSupport: <String, PlatformDetails>{
platformIOS: const PlatformDetails(PlatformSupport.inline)
});
final List<String> output =
await runCapturingPrint(runner, <String>['fetch-deps']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall(
'flutter',
const <String>['pub', 'get'],
plugin.directory.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('fails if pub get fails', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformIOS: const PlatformDetails(PlatformSupport.inline)
});
processRunner
.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1)),
];
Error? commandError;
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps'], errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Failed to "pub get"'),
],
));
});
test('skips unsupported packages when any platforms are passed',
() async {
final RepositoryPackage packageWithBoth = createFakePackage(
'supports_both', packagesDir, extraFiles: <String>[
'example/linux/placeholder',
'example/windows/placeholder'
]);
final RepositoryPackage packageWithOne = createFakePackage(
'supports_one', packagesDir,
extraFiles: <String>['example/linux/placeholder']);
createFakePackage('supports_neither', packagesDir);
await runCapturingPrint(runner, <String>[
'fetch-deps',
'--linux',
'--windows',
'--supporting-target-platforms-only'
]);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall(
'dart',
const <String>['pub', 'get'],
packageWithBoth.path,
),
ProcessCall(
'dart',
const <String>['pub', 'get'],
packageWithOne.path,
),
]),
);
});
});
group('android', () {
test('runs pub get before gradlew dependencies', () async {
final RepositoryPackage plugin =
createFakePlugin('plugin1', packagesDir, extraFiles: <String>[
'example/android/gradlew',
], platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline)
});
final Directory androidDir = plugin
.getExamples()
.first
.platformDirectory(FlutterPlatform.android);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--android']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall(
'flutter',
const <String>['pub', 'get'],
plugin.directory.path,
),
ProcessCall(
androidDir.childFile('gradlew').path,
const <String>['plugin1:dependencies'],
androidDir.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('runs gradlew dependencies', () async {
final RepositoryPackage plugin =
createFakePlugin('plugin1', packagesDir, extraFiles: <String>[
'example/android/gradlew',
], platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline)
});
final Directory androidDir = plugin
.getExamples()
.first
.platformDirectory(FlutterPlatform.android);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall(
androidDir.childFile('gradlew').path,
const <String>['plugin1:dependencies'],
androidDir.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('runs on all examples', () async {
final List<String> examples = <String>['example1', 'example2'];
final RepositoryPackage plugin = createFakePlugin(
'plugin1', packagesDir,
examples: examples,
extraFiles: <String>[
'example/example1/android/gradlew',
'example/example2/android/gradlew',
],
platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline)
});
final Iterable<Directory> exampleAndroidDirs = plugin.getExamples().map(
(RepositoryPackage example) =>
example.platformDirectory(FlutterPlatform.android));
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
for (final Directory directory in exampleAndroidDirs)
ProcessCall(
directory.childFile('gradlew').path,
const <String>['plugin1:dependencies'],
directory.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('runs --config-only build if gradlew is missing', () async {
final RepositoryPackage plugin = createFakePlugin(
'plugin1', packagesDir, platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline)
});
final Directory androidDir = plugin
.getExamples()
.first
.platformDirectory(FlutterPlatform.android);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall(
getFlutterCommand(mockPlatform),
const <String>['build', 'apk', '--config-only'],
plugin.getExamples().first.directory.path,
),
ProcessCall(
androidDir.childFile('gradlew').path,
const <String>['plugin1:dependencies'],
androidDir.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('fails if gradlew generation fails', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline)
});
processRunner
.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1)),
];
Error? commandError;
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android'],
errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Unable to configure Gradle project'),
],
));
});
test('fails if dependency download finds issues', () async {
final RepositoryPackage plugin =
createFakePlugin('plugin1', packagesDir, extraFiles: <String>[
'example/android/gradlew',
], platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.inline)
});
final String gradlewPath = plugin
.getExamples()
.first
.platformDirectory(FlutterPlatform.android)
.childFile('gradlew')
.path;
processRunner.mockProcessesForExecutable[gradlewPath] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(exitCode: 1)),
];
Error? commandError;
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android'],
errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(
<Matcher>[
contains('The following packages had errors:'),
],
));
});
test('skips non-Android plugins', () async {
createFakePlugin('plugin1', packagesDir);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android']);
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Package does not have native Android dependencies.')
],
));
});
test('skips non-inline plugins', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformAndroid: const PlatformDetails(PlatformSupport.federated)
});
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--android']);
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Package does not have native Android dependencies.')
],
));
});
});
group('ios', () {
test('runs on all examples', () async {
final List<String> examples = <String>['example1', 'example2'];
final RepositoryPackage plugin = createFakePlugin(
'plugin1', packagesDir,
examples: examples,
platformSupport: <String, PlatformDetails>{
platformIOS: const PlatformDetails(PlatformSupport.inline)
});
final Iterable<Directory> exampleDirs = plugin.getExamples().map(
(RepositoryPackage example) => example.directory);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--ios']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
const ProcessCall(
'flutter',
<String>['precache', '--ios'],
null,
),
for (final Directory directory in exampleDirs)
ProcessCall(
'flutter',
const <String>['build', 'ios', '--config-only'],
directory.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('fails if flutter build --config-only fails', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformIOS: const PlatformDetails(PlatformSupport.inline)
});
processRunner
.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(), <String>['precache']),
FakeProcessInfo(MockProcess(exitCode: 1), <String>['build', 'ios', '--config-only']),
];
Error? commandError;
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--ios'],
errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(
<Matcher>[
contains('The following packages had errors:'),
],
));
});
test('skips non-iOS plugins', () async {
createFakePlugin('plugin1', packagesDir);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--ios']);
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Package does not have native iOS dependencies.')
],
));
});
test('skips non-inline plugins', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformIOS: const PlatformDetails(PlatformSupport.federated)
});
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--ios']);
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Package does not have native iOS dependencies.')
],
));
});
});
group('macos', () {
test('runs on all examples', () async {
final List<String> examples = <String>['example1', 'example2'];
final RepositoryPackage plugin = createFakePlugin(
'plugin1', packagesDir,
examples: examples,
platformSupport: <String, PlatformDetails>{
platformMacOS: const PlatformDetails(PlatformSupport.inline)
});
final Iterable<Directory> exampleDirs = plugin.getExamples().map(
(RepositoryPackage example) => example.directory);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--macos']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
const ProcessCall(
'flutter',
<String>['precache', '--macos'],
null,
),
for (final Directory directory in exampleDirs)
ProcessCall(
'flutter',
const <String>['build', 'macos', '--config-only'],
directory.path,
),
]),
);
expect(
output,
containsAllInOrder(<Matcher>[
contains('Running for plugin1'),
contains('No issues found!'),
]));
});
test('fails if flutter build --config-only fails', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformMacOS: const PlatformDetails(PlatformSupport.inline)
});
processRunner
.mockProcessesForExecutable[getFlutterCommand(mockPlatform)] =
<FakeProcessInfo>[
FakeProcessInfo(MockProcess(), <String>['precache']),
FakeProcessInfo(MockProcess(exitCode: 1), <String>['build', 'macos', '--config-only']),
];
Error? commandError;
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--macos'],
errorHandler: (Error e) {
commandError = e;
});
expect(commandError, isA<ToolExit>());
expect(
output,
containsAllInOrder(
<Matcher>[
contains('The following packages had errors:'),
],
));
});
test('skips non-macOS plugins', () async {
createFakePlugin('plugin1', packagesDir);
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--macos']);
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Package does not have native macOS dependencies.')
],
));
});
test('skips non-inline plugins', () async {
createFakePlugin('plugin1', packagesDir,
platformSupport: <String, PlatformDetails>{
platformMacOS: const PlatformDetails(PlatformSupport.federated)
});
final List<String> output = await runCapturingPrint(
runner, <String>['fetch-deps', '--no-dart', '--macos']);
expect(
output,
containsAllInOrder(
<Matcher>[
contains('Package does not have native macOS dependencies.')
],
));
});
});
});
}