blob: 2a6a02ab4c5d9972aaa59783c2ee969c6104841b [file]
// Copyright 2013 The Flutter Authors
// 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: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:git/git.dart';
import 'package:test/test.dart';
import 'mocks.dart';
import 'util.dart';
void main() {
group('FetchDepsCommand', () {
late Directory packagesDir;
late CommandRunner<void> runner;
late MockPlatform mockPlatform;
late RecordingProcessRunner processRunner;
setUp(() {
mockPlatform = MockPlatform();
final GitDir gitDir;
(:packagesDir, :processRunner, gitProcessRunner: _, :gitDir) = configureBaseCommandMocks(
platform: mockPlatform,
);
final command = FetchDepsCommand(
packagesDir,
processRunner: processRunner,
platform: mockPlatform,
gitDir: gitDir,
);
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('runs pub get in non-example sub-packages', () async {
final RepositoryPackage plugin = createFakePlugin(
'plugin1',
packagesDir,
platformSupport: <String, PlatformDetails>{
platformIOS: const PlatformDetails(PlatformSupport.inline),
},
);
final RepositoryPackage subpackage = createFakePackage(
'subpackage',
plugin.directory.childDirectory('extras'),
);
final List<String> output = await runCapturingPrint(runner, <String>['fetch-deps']);
expect(
processRunner.recordedCalls,
orderedEquals(<ProcessCall>[
ProcessCall('flutter', const <String>['pub', 'get'], plugin.directory.path),
ProcessCall('dart', const <String>['pub', 'get'], subpackage.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 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 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),
const ProcessCall('pod', <String>['repo', 'update'], 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 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),
const ProcessCall('pod', <String>['repo', 'update'], 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.'),
]),
);
});
});
group('swift-package-manager', () {
for (final platformName in <String>[platformIOS, platformMacOS]) {
group(platformName, () {
test('is not set by default', () async {
mockPlatform.isMacOS = true;
final RepositoryPackage plugin = createFakePlugin(
'plugin1',
packagesDir,
platformSupport: <String, PlatformDetails>{
platformName: const PlatformDetails(PlatformSupport.inline),
},
);
final RepositoryPackage example = plugin.getExamples().first;
final String originalPubspecContents = example.pubspecFile.readAsStringSync();
String? buildTimePubspecContents;
processRunner.mockProcessesForExecutable[getFlutterCommand(
mockPlatform,
)] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(), <String>['precache']),
FakeProcessInfo(MockProcess(), <String>['build'], () {
buildTimePubspecContents = example.pubspecFile.readAsStringSync();
}),
];
await runCapturingPrint(runner, <String>['fetch-deps', '--no-dart', '--$platformName']);
// Ensure that SwiftPM was not set at build time.
expect(buildTimePubspecContents, isNot(contains('enable-swift-package-manager')));
// And that the pubspec wasn't changed at all.
expect(example.pubspecFile.readAsStringSync().trim(), originalPubspecContents.trim());
});
test('can be enabled', () async {
mockPlatform.isMacOS = true;
final RepositoryPackage plugin = createFakePlugin(
'plugin1',
packagesDir,
platformSupport: <String, PlatformDetails>{
platformName: const PlatformDetails(PlatformSupport.inline),
},
);
final RepositoryPackage example = plugin.getExamples().first;
final String originalPubspecContents = example.pubspecFile.readAsStringSync();
String? buildTimePubspecContents;
processRunner.mockProcessesForExecutable[getFlutterCommand(
mockPlatform,
)] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(), <String>['build'], () {
buildTimePubspecContents = example.pubspecFile.readAsStringSync();
}),
];
await runCapturingPrint(runner, <String>[
'fetch-deps',
'--no-dart',
'--$platformName',
'--swift-package-manager',
]);
// Ensure that SwiftPM was enabled for the package.
expect(originalPubspecContents, isNot(contains('enable-swift-package-manager: true')));
expect(buildTimePubspecContents, contains('enable-swift-package-manager: true'));
// And that it was undone after.
expect(example.pubspecFile.readAsStringSync().trim(), originalPubspecContents.trim());
});
test('can be disabled', () async {
mockPlatform.isMacOS = true;
final RepositoryPackage plugin = createFakePlugin(
'plugin1',
packagesDir,
platformSupport: <String, PlatformDetails>{
platformName: const PlatformDetails(PlatformSupport.inline),
},
);
final RepositoryPackage example = plugin.getExamples().first;
final String originalPubspecContents = example.pubspecFile.readAsStringSync();
String? buildTimePubspecContents;
processRunner.mockProcessesForExecutable[getFlutterCommand(
mockPlatform,
)] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(), <String>['precache']),
FakeProcessInfo(MockProcess(), <String>['build'], () {
buildTimePubspecContents = example.pubspecFile.readAsStringSync();
}),
];
await runCapturingPrint(runner, <String>[
'fetch-deps',
'--no-dart',
'--$platformName',
'--no-swift-package-manager',
]);
// Ensure that SwiftPM was disabled for the package.
expect(originalPubspecContents, isNot(contains('enable-swift-package-manager: false')));
expect(buildTimePubspecContents, contains('enable-swift-package-manager: false'));
// And that it was undone after.
expect(example.pubspecFile.readAsStringSync().trim(), originalPubspecContents.trim());
});
test('is set before running pub get, and includes the plugin package', () async {
mockPlatform.isMacOS = true;
final RepositoryPackage plugin = createFakePlugin(
'plugin1',
packagesDir,
platformSupport: <String, PlatformDetails>{
platformName: const PlatformDetails(PlatformSupport.inline),
},
);
final RepositoryPackage example = plugin.getExamples().first;
final String originalPluginPubspecContents = plugin.pubspecFile.readAsStringSync();
final String originalExamplePubspecContents = example.pubspecFile.readAsStringSync();
String? buildTimePluginPubspecContents;
String? buildTimeExamplePubspecContents;
processRunner.mockProcessesForExecutable[getFlutterCommand(
mockPlatform,
)] = <FakeProcessInfo>[
FakeProcessInfo(MockProcess(), <String>['pub', 'get'], () {
buildTimePluginPubspecContents = plugin.pubspecFile.readAsStringSync();
buildTimeExamplePubspecContents = example.pubspecFile.readAsStringSync();
}),
];
await runCapturingPrint(runner, <String>[
'fetch-deps',
'--$platformName',
'--swift-package-manager',
]);
// Ensure that SwiftPM was enabled for the plugin and the example.
expect(
originalPluginPubspecContents,
isNot(contains('enable-swift-package-manager: true')),
);
expect(
originalExamplePubspecContents,
isNot(contains('enable-swift-package-manager: true')),
);
expect(buildTimePluginPubspecContents, contains('enable-swift-package-manager: true'));
expect(buildTimeExamplePubspecContents, contains('enable-swift-package-manager: true'));
// And that it was undone after.
expect(
plugin.pubspecFile.readAsStringSync().trim(),
originalPluginPubspecContents.trim(),
);
expect(
example.pubspecFile.readAsStringSync().trim(),
originalExamplePubspecContents.trim(),
);
});
});
}
});
});
}