diff --git a/.ci/targets/ios_platform_tests.yaml b/.ci/targets/ios_platform_tests.yaml index bc962e41ecea..90b61b1a0df7 100644 --- a/.ci/targets/ios_platform_tests.yaml +++ b/.ci/targets/ios_platform_tests.yaml @@ -14,15 +14,15 @@ tasks: args: ["build-examples", "--ios", "--swift-package-manager"] - name: xcode analyze script: .ci/scripts/tool_runner.sh - args: ["xcode-analyze", "--ios"] + args: ["xcode-analyze", "--ios", "--exclude=script/configs/xcode_warnings_exceptions.yaml"] - name: xcode analyze deprecation # Ensure we don't accidentally introduce deprecated code. script: .ci/scripts/tool_runner.sh - args: ["xcode-analyze", "--ios", "--ios-min-version=14.0", "--exclude=script/configs/exclude_xcode_deprecation.yaml"] + args: ["xcode-analyze", "--ios", "--ios-min-version=14.0", "--exclude=script/configs/exclude_xcode_deprecation.yaml,script/configs/xcode_warnings_exceptions.yaml"] - name: native test script: .ci/scripts/tool_runner.sh # Simulator name and version must match name and version in create_simulator.sh - args: ["native-test", "--ios", "--ios-destination", "platform=iOS Simulator,name=Flutter-iPhone,OS=17.0"] + args: ["native-test", "--ios", "--ios-destination", "platform=iOS Simulator,name=Flutter-iPhone,OS=17.0", "--xcode-warnings-exceptions=script/configs/xcode_warnings_exceptions.yaml"] - name: boot simulator # Ensure simulator is still booted script: .ci/scripts/boot_simulator.sh diff --git a/.ci/targets/macos_platform_tests.yaml b/.ci/targets/macos_platform_tests.yaml index c3a5d83b11e2..55701d14f0e9 100644 --- a/.ci/targets/macos_platform_tests.yaml +++ b/.ci/targets/macos_platform_tests.yaml @@ -11,14 +11,14 @@ tasks: args: ["build-examples", "--macos", "--swift-package-manager"] - name: xcode analyze script: .ci/scripts/tool_runner.sh - args: ["xcode-analyze", "--macos"] + args: ["xcode-analyze", "--macos", "--exclude=script/configs/xcode_warnings_exceptions.yaml"] - name: xcode analyze deprecation # Ensure we don't accidentally introduce deprecated code. script: .ci/scripts/tool_runner.sh - args: ["xcode-analyze", "--macos", "--macos-min-version=14.0", "--exclude=script/configs/exclude_xcode_deprecation.yaml"] + args: ["xcode-analyze", "--macos", "--macos-min-version=14.0", "--exclude=script/configs/exclude_xcode_deprecation.yaml,script/configs/xcode_warnings_exceptions.yaml"] - name: native test script: .ci/scripts/tool_runner.sh - args: ["native-test", "--macos"] + args: ["native-test", "--macos", "--xcode-warnings-exceptions=script/configs/xcode_warnings_exceptions.yaml"] - name: drive examples script: .ci/scripts/tool_runner.sh args: ["drive-examples", "--macos", "--exclude=script/configs/exclude_integration_macos.yaml"] diff --git a/script/configs/xcode_warnings_exceptions.yaml b/script/configs/xcode_warnings_exceptions.yaml new file mode 100644 index 000000000000..0c51c4abcf43 --- /dev/null +++ b/script/configs/xcode_warnings_exceptions.yaml @@ -0,0 +1,5 @@ +# The list of plugins that are known to produce Xcode warnings. +# These should be excluded from Xcode analysis and "treat warnings as errors" +# should be disabled when running their native tests. +# +# All entries here should have an explanation for why they are here. diff --git a/script/tool/lib/src/native_test_command.dart b/script/tool/lib/src/native_test_command.dart index 47fd57998765..508071c57e3f 100644 --- a/script/tool/lib/src/native_test_command.dart +++ b/script/tool/lib/src/native_test_command.dart @@ -21,6 +21,8 @@ const String _integrationTestFlag = 'integration'; const String _iOSDestinationFlag = 'ios-destination'; +const String _xcodeWarningsExceptionsFlag = 'xcode-warnings-exceptions'; + const int _exitNoIOSSimulators = 3; /// The error message logged when a FlutterTestRunner test is not annotated with @@ -65,6 +67,14 @@ class NativeTestCommand extends PackageLoopingCommand { help: 'Runs native unit tests', defaultsTo: true); argParser.addFlag(_integrationTestFlag, help: 'Runs native integration (UI) tests', defaultsTo: true); + + argParser.addMultiOption( + _xcodeWarningsExceptionsFlag, + help: 'A list of packages that are allowed to have Xcode warnings.\n\n' + 'Alternately, a list of one or more YAML files that contain a list ' + 'of packages to allow Xcode warnings.', + defaultsTo: [], + ); } // The ABI of the host. @@ -100,6 +110,8 @@ this command. List _requestedPlatforms = []; + Set _xcodeWarningsExceptions = {}; + @override Future initializeRun() async { _platforms = { @@ -151,6 +163,8 @@ this command. destination, ]; } + + _xcodeWarningsExceptions = getYamlListArg(_xcodeWarningsExceptionsFlag); } @override @@ -487,7 +501,8 @@ this command. extraFlags: [ if (testTarget != null) '-only-testing:$testTarget', ...extraFlags, - 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', + if (!_xcodeWarningsExceptions.contains(plugin.directory.basename)) + 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], ); diff --git a/script/tool/test/native_test_command_test.dart b/script/tool/test/native_test_command_test.dart index 02cd5c2c43ea..cdaa2a748bd4 100644 --- a/script/tool/test/native_test_command_test.dart +++ b/script/tool/test/native_test_command_test.dart @@ -27,6 +27,8 @@ const String _androidIntegrationTestFilter = '-Pandroid.testInstrumentationRunnerArguments.' 'notAnnotation=io.flutter.plugins.DartIntegrationTest'; +const String _simulatorDeviceId = '1E76A0FD-38AC-4537-A989-EA639D7D012A'; + final Map _kDeviceListMap = { 'runtimes': >[ { @@ -137,6 +139,7 @@ void main() { String platform, { String? destination, List extraFlags = const [], + bool treatWarningsAsErrors = true, }) { return ProcessCall( 'xcrun', @@ -152,7 +155,7 @@ void main() { 'Debug', if (destination != null) ...['-destination', destination], ...extraFlags, - 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', + if (treatWarningsAsErrors) 'GCC_TREAT_WARNINGS_AS_ERRORS=YES', ], package.path); } @@ -349,7 +352,7 @@ void main() { null), getTargetCheckCall(pluginExampleDirectory, 'ios'), getRunTestCall(pluginExampleDirectory, 'ios', - destination: 'id=1E76A0FD-38AC-4537-A989-EA639D7D012A'), + destination: 'id=$_simulatorDeviceId'), ])); }); }); @@ -1450,6 +1453,98 @@ public class FlutterActivityTest { getTargetCheckCall(pluginExampleDirectory, 'macos'), ])); }); + + test('Xcode warnings exceptions list', () async { + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + processRunner.mockProcessesForExecutable['xcrun'] = [ + FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)), + ['simctl', 'list']), + getMockXcodebuildListProcess( + ['RunnerTests', 'RunnerUITests']), + ]; + + await runCapturingPrint(runner, [ + 'native-test', + '--ios', + '--xcode-warnings-exceptions=plugin' + ]); + + expect( + processRunner.recordedCalls, + contains( + getRunTestCall(pluginExampleDirectory, 'ios', + destination: 'id=$_simulatorDeviceId', + treatWarningsAsErrors: false), + )); + }); + + test('Xcode warnings exceptions file', () async { + final File configFile = packagesDir.childFile('exceptions.yaml'); + await configFile.writeAsString('- plugin'); + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + processRunner.mockProcessesForExecutable['xcrun'] = [ + FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)), + ['simctl', 'list']), + getMockXcodebuildListProcess( + ['RunnerTests', 'RunnerUITests']), + ]; + + await runCapturingPrint(runner, [ + 'native-test', + '--ios', + '--xcode-warnings-exceptions=${configFile.path}' + ]); + + expect( + processRunner.recordedCalls, + contains( + getRunTestCall(pluginExampleDirectory, 'ios', + destination: 'id=$_simulatorDeviceId', + treatWarningsAsErrors: false), + )); + }); + + test('treat warnings as errors if plugin not on exceptions list', + () async { + final RepositoryPackage plugin = createFakePlugin('plugin', packagesDir, + platformSupport: { + platformIOS: const PlatformDetails(PlatformSupport.inline) + }); + + final Directory pluginExampleDirectory = getExampleDir(plugin); + + processRunner.mockProcessesForExecutable['xcrun'] = [ + FakeProcessInfo(MockProcess(stdout: jsonEncode(_kDeviceListMap)), + ['simctl', 'list']), + getMockXcodebuildListProcess( + ['RunnerTests', 'RunnerUITests']), + ]; + + await runCapturingPrint(runner, [ + 'native-test', + '--ios', + '--xcode-warnings-exceptions=foo,bar' + ]); + + expect( + processRunner.recordedCalls, + contains( + getRunTestCall(pluginExampleDirectory, 'ios', + destination: 'id=$_simulatorDeviceId'), + )); + }); }); group('multiplatform', () {