// 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/xcode_analyze_command.dart'; import 'package:test/test.dart'; import 'mocks.dart'; import 'util.dart'; // TODO(stuartmorgan): Rework these tests to use a mock Xcode instead of // doing all the process mocking and validation. void main() { group('test xcode_analyze_command', () { late FileSystem fileSystem; late MockPlatform mockPlatform; late Directory packagesDir; late CommandRunner runner; late RecordingProcessRunner processRunner; setUp(() { fileSystem = MemoryFileSystem(); mockPlatform = MockPlatform(isMacOS: true); packagesDir = createPackagesDirectory(fileSystem: fileSystem); processRunner = RecordingProcessRunner(); final XcodeAnalyzeCommand command = XcodeAnalyzeCommand(packagesDir, processRunner: processRunner, platform: mockPlatform); runner = CommandRunner( 'xcode_analyze_command', 'Test for xcode_analyze_command'); runner.addCommand(command); }); test('Fails if no platforms are provided', () async { Error? commandError; final List output = await runCapturingPrint( runner, ['xcode-analyze'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('At least one platform flag must be provided'), ]), ); }); group('iOS', () { test('skip if iOS is not supported', () async { createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final List output = await runCapturingPrint(runner, ['xcode-analyze', '--ios']); expect(output, contains(contains('Not implemented for target platform(s).'))); expect(processRunner.recordedCalls, orderedEquals([])); }); test('skip if iOS is implemented in a federated package', () async { createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.federated) }); final List output = await runCapturingPrint(runner, ['xcode-analyze', '--ios']); expect(output, contains(contains('Not implemented for target platform(s).'))); expect(processRunner.recordedCalls, orderedEquals([])); }); test('runs for iOS plugin', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', '--ios', ]); expect( output, containsAllInOrder([ contains('Running for plugin'), contains('plugin/example (iOS) passed analysis.') ])); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'ios/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', '-destination', 'generic/platform=iOS Simulator', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('passes min iOS deployment version when requested', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, ['xcode-analyze', '--ios', '--ios-min-version=14.0']); expect( output, containsAllInOrder([ contains('Running for plugin'), contains('plugin/example (iOS) passed analysis.') ])); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'ios/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', '-destination', 'generic/platform=iOS Simulator', 'IPHONEOS_DEPLOYMENT_TARGET=14.0', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); processRunner.mockProcessesForExecutable['xcrun'] = [ FakeProcessInfo(MockProcess(exitCode: 1)) ]; Error? commandError; final List output = await runCapturingPrint( runner, [ 'xcode-analyze', '--ios', ], errorHandler: (Error e) { commandError = e; }, ); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('The following packages had errors:'), contains(' plugin'), ])); }); }); group('macOS', () { test('skip if macOS is not supported', () async { createFakePlugin( 'plugin', packagesDir, ); final List output = await runCapturingPrint( runner, ['xcode-analyze', '--macos']); expect(output, contains(contains('Not implemented for target platform(s).'))); expect(processRunner.recordedCalls, orderedEquals([])); }); test('skip if macOS is implemented in a federated package', () async { createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.federated), }); final List output = await runCapturingPrint( runner, ['xcode-analyze', '--macos']); expect(output, contains(contains('Not implemented for target platform(s).'))); expect(processRunner.recordedCalls, orderedEquals([])); }); test('runs for macOS plugin', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', '--macos', ]); expect(output, contains(contains('plugin/example (macOS) passed analysis.'))); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'macos/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('passes min macOS deployment version when requested', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, ['xcode-analyze', '--macos', '--macos-min-version=12.0']); expect(output, contains(contains('plugin/example (macOS) passed analysis.'))); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'macos/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', 'MACOSX_DEPLOYMENT_TARGET=12.0', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('fails if xcrun fails', () async { createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); processRunner.mockProcessesForExecutable['xcrun'] = [ FakeProcessInfo(MockProcess(exitCode: 1)) ]; Error? commandError; final List output = await runCapturingPrint( runner, ['xcode-analyze', '--macos'], errorHandler: (Error e) { commandError = e; }); expect(commandError, isA()); expect( output, containsAllInOrder([ contains('The following packages had errors:'), contains(' plugin'), ]), ); }); }); group('combined', () { test('runs both iOS and macOS when supported', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline), platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', '--ios', '--macos', ]); expect( output, containsAll([ contains('plugin/example (iOS) passed analysis.'), contains('plugin/example (macOS) passed analysis.'), ])); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'ios/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', '-destination', 'generic/platform=iOS Simulator', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'macos/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('runs only macOS for a macOS plugin', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformMacOS: const PlatformDetails(PlatformSupport.inline), }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', '--ios', '--macos', ]); expect( output, containsAllInOrder([ contains('plugin/example (macOS) passed analysis.'), ])); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'macos/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('runs only iOS for a iOS plugin', () async { final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, platformSupport: { platformIOS: const PlatformDetails(PlatformSupport.inline) }); final Directory pluginExampleDirectory = getExampleDir(plugin); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', '--ios', '--macos', ]); expect( output, containsAllInOrder( [contains('plugin/example (iOS) passed analysis.')])); expect( processRunner.recordedCalls, orderedEquals([ ProcessCall( 'xcrun', const [ 'xcodebuild', 'clean', 'analyze', '-workspace', 'ios/Runner.xcworkspace', '-scheme', 'Runner', '-configuration', 'Debug', '-destination', 'generic/platform=iOS Simulator', 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], pluginExampleDirectory.path), ])); }); test('skips when neither are supported', () async { createFakePlugin('plugin', packagesDir); final List output = await runCapturingPrint(runner, [ 'xcode-analyze', '--ios', '--macos', ]); expect( output, containsAllInOrder([ contains('SKIPPING: Not implemented for target platform(s).'), ])); expect(processRunner.recordedCalls, orderedEquals([])); }); }); }); }