// 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 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('fetch_deps_test', 'Test for $FetchDepsCommand'); runner.addCommand(command); }); group('dart', () { test('runs pub get', () async { final RepositoryPackage plugin = createFakePlugin( 'plugin1', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); final List output = await runCapturingPrint(runner, ['fetch-deps']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'flutter', const ['pub', 'get'], plugin.directory.path, ), ]), ); expect( output, containsAllInOrder([ contains('Running for plugin1'), contains('No issues found!'), ])); }); test('fails if pub get fails', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ FakeProcessInfo(MockProcess(exitCode: 1)), ]; Error? commandError; final List output = await runCapturingPrint( runner, ['fetch-deps'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('Failed to "pub get"'), ], )); }); test('skips unsupported packages when any platforms are passed', () async { final RepositoryPackage packageWithBoth = createFakePackage( 'supports_both', packagesDir, extraFiles: [ 'example/linux/placeholder', 'example/windows/placeholder' ]); final RepositoryPackage packageWithOne = createFakePackage( 'supports_one', packagesDir, extraFiles: ['example/linux/placeholder']); createFakePackage('supports_neither', packagesDir); await runCapturingPrint(runner, [ 'fetch-deps', '--linux', '--windows', '--supporting-target-platforms-only' ]); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'dart', const ['pub', 'get'], packageWithBoth.path, ), ProcessCall( 'dart', const ['pub', 'get'], packageWithOne.path, ), ]), ); }); }); group('android', () { test('runs pub get before gradlew dependencies', () async { final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); final Directory androidDir = plugin .getExamples() .first .platformDirectory(FlutterPlatform.android); final List output = await runCapturingPrint( runner, ['fetch-deps', '--android']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'flutter', const ['pub', 'get'], plugin.directory.path, ), ProcessCall( androidDir.childFile('gradlew').path, const ['plugin1:dependencies'], androidDir.path, ), ]), ); expect( output, containsAllInOrder([ contains('Running for plugin1'), contains('No issues found!'), ])); }); test('runs gradlew dependencies', () async { final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); final Directory androidDir = plugin .getExamples() .first .platformDirectory(FlutterPlatform.android); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( androidDir.childFile('gradlew').path, const ['plugin1:dependencies'], androidDir.path, ), ]), ); expect( output, containsAllInOrder([ contains('Running for plugin1'), contains('No issues found!'), ])); }); test('runs on all examples', () async { final List examples = ['example1', 'example2']; final RepositoryPackage plugin = createFakePlugin( 'plugin1', packagesDir, examples: examples, extraFiles: [ 'example/example1/android/gradlew', 'example/example2/android/gradlew', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); final Iterable exampleAndroidDirs = plugin.getExamples().map( (RepositoryPackage example) => example.platformDirectory(FlutterPlatform.android)); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android']); expect( processRunner.recordedCalls, orderedEquals([ for (final Directory directory in exampleAndroidDirs) ProcessCall( directory.childFile('gradlew').path, const ['plugin1:dependencies'], directory.path, ), ]), ); expect( output, containsAllInOrder([ 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: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); final Directory androidDir = plugin .getExamples() .first .platformDirectory(FlutterPlatform.android); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android']); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( getFlutterCommand(mockPlatform), const ['build', 'apk', '--config-only'], plugin.getExamples().first.directory.path, ), ProcessCall( androidDir.childFile('gradlew').path, const ['plugin1:dependencies'], androidDir.path, ), ]), ); expect( output, containsAllInOrder([ contains('Running for plugin1'), contains('No issues found!'), ])); }); test('fails if gradlew generation fails', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ FakeProcessInfo(MockProcess(exitCode: 1)), ]; Error? commandError; final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('Unable to configure Gradle project'), ], )); }); test('fails if dependency download finds issues', () async { final RepositoryPackage plugin = createFakePlugin('plugin1', packagesDir, extraFiles: [ 'example/android/gradlew', ], platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.inline) }); final String gradlewPath = plugin .getExamples() .first .platformDirectory(FlutterPlatform.android) .childFile('gradlew') .path; processRunner.mockProcessesForExecutable[gradlewPath] = [ FakeProcessInfo(MockProcess(exitCode: 1)), ]; Error? commandError; final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('The following packages had errors:'), ], )); }); test('skips non-Android plugins', () async { createFakePlugin('plugin1', packagesDir); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android']); expect( output, containsAllInOrder( [ contains('Package does not have native Android dependencies.') ], )); }); test('skips non-inline plugins', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformAndroid: const PlatformDetails(PlatformSupport.federated) }); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--android']); expect( output, containsAllInOrder( [ contains('Package does not have native Android dependencies.') ], )); }); }); group('ios', () { test('runs on all examples', () async { final List examples = ['example1', 'example2']; final RepositoryPackage plugin = createFakePlugin( 'plugin1', packagesDir, examples: examples, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Iterable exampleDirs = plugin .getExamples() .map((RepositoryPackage example) => example.directory); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--ios']); expect( processRunner.recordedCalls, orderedEquals([ const ProcessCall( 'flutter', ['precache', '--ios'], null, ), for (final Directory directory in exampleDirs) ProcessCall( 'flutter', const ['build', 'ios', '--config-only'], directory.path, ), ]), ); expect( output, containsAllInOrder([ contains('Running for plugin1'), contains('No issues found!'), ])); }); test('fails if flutter build --config-only fails', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ FakeProcessInfo(MockProcess(), ['precache']), FakeProcessInfo(MockProcess(exitCode: 1), ['build', 'ios', '--config-only']), ]; Error? commandError; final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--ios'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('The following packages had errors:'), ], )); }); test('skips non-iOS plugins', () async { createFakePlugin('plugin1', packagesDir); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--ios']); expect( output, containsAllInOrder( [ contains('Package does not have native iOS dependencies.') ], )); }); test('skips non-inline plugins', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.federated) }); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--ios']); expect( output, containsAllInOrder( [ contains('Package does not have native iOS dependencies.') ], )); }); }); group('macos', () { test('runs on all examples', () async { final List examples = ['example1', 'example2']; final RepositoryPackage plugin = createFakePlugin( 'plugin1', packagesDir, examples: examples, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline) }); final Iterable exampleDirs = plugin .getExamples() .map((RepositoryPackage example) => example.directory); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--macos']); expect( processRunner.recordedCalls, orderedEquals([ const ProcessCall( 'flutter', ['precache', '--macos'], null, ), for (final Directory directory in exampleDirs) ProcessCall( 'flutter', const ['build', 'macos', '--config-only'], directory.path, ), ]), ); expect( output, containsAllInOrder([ contains('Running for plugin1'), contains('No issues found!'), ])); }); test('fails if flutter build --config-only fails', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline) }); processRunner .mockProcessesForExecutable[getFlutterCommand(mockPlatform)] = [ FakeProcessInfo(MockProcess(), ['precache']), FakeProcessInfo(MockProcess(exitCode: 1), ['build', 'macos', '--config-only']), ]; Error? commandError; final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--macos'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder( [ contains('The following packages had errors:'), ], )); }); test('skips non-macOS plugins', () async { createFakePlugin('plugin1', packagesDir); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--macos']); expect( output, containsAllInOrder( [ contains('Package does not have native macOS dependencies.') ], )); }); test('skips non-inline plugins', () async { createFakePlugin('plugin1', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.federated) }); final List output = await runCapturingPrint( runner, ['fetch-deps', '--no-dart', '--macos']); expect( output, containsAllInOrder( [ contains('Package does not have native macOS dependencies.') ], )); }); }); }); }