From 1fe6f09110f15031d707641be64cf4b40547f744 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 15 Apr 2025 10:10:14 -0400 Subject: [PATCH] [tool] Run config-only build for iOS/macOS native-test Follow-up to https://github.com/flutter/packages/pull/9075. On iOS and macOS, `native-test` also requires an unconditional project file generatino in debug mode now, to ensure that the debug Xcode build of the tests will not fail due to a build mode mismatch. Unblocks the flutter/flutter->flutter/packages roller. --- script/tool/lib/src/native_test_command.dart | 37 ++++++++++++++----- .../tool/test/native_test_command_test.dart | 35 ++++++++++++++++++ 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/script/tool/lib/src/native_test_command.dart b/script/tool/lib/src/native_test_command.dart index 6714b68f607..39630189ae7 100644 --- a/script/tool/lib/src/native_test_command.dart +++ b/script/tool/lib/src/native_test_command.dart @@ -433,12 +433,12 @@ this command. } Future<_PlatformResult> _testIOS(RepositoryPackage plugin, _TestMode mode) { - return _runXcodeTests(plugin, 'iOS', mode, + return _runXcodeTests(plugin, FlutterPlatform.ios, mode, extraFlags: _iOSDestinationFlags); } Future<_PlatformResult> _testMacOS(RepositoryPackage plugin, _TestMode mode) { - return _runXcodeTests(plugin, 'macOS', mode); + return _runXcodeTests(plugin, FlutterPlatform.macos, mode); } /// Runs all applicable tests for [plugin], printing status and returning @@ -448,7 +448,7 @@ this command. /// usually at "example/{ios,macos}/Runner.xcworkspace". Future<_PlatformResult> _runXcodeTests( RepositoryPackage plugin, - String targetPlatform, + FlutterPlatform targetPlatform, _TestMode mode, { List extraFlags = const [], }) async { @@ -459,6 +459,8 @@ this command. } else if (mode.integrationOnly) { testTarget = 'RunnerUITests'; } + final String targetPlatformString = + targetPlatform == FlutterPlatform.ios ? 'iOS' : 'macOS'; bool ranUnitTests = false; // Assume skipped until at least one test has run. @@ -472,8 +474,8 @@ this command. bool exampleHasUnitTests = false; final String? targetToCheck = testTarget ?? (mode.unit ? unitTestTarget : null); - final Directory xcodeProject = example.directory - .childDirectory(targetPlatform.toLowerCase()) + final Directory xcodeProject = example + .platformDirectory(targetPlatform) .childDirectory('Runner.xcodeproj'); if (targetToCheck != null) { final bool? hasTarget = @@ -490,14 +492,29 @@ this command. } } - _printRunningExampleTestsMessage(example, targetPlatform); + // Ensure that the native project files are configured for a debug build, + // otherwise the Xcode build step will fail due to mode mismatch. + final bool buildSuccess = await runConfigOnlyBuild( + example, + processRunner, + platform, + targetPlatform, + buildDebug: true, + ); + if (!buildSuccess) { + printError('Unable to generate debug Xcode project files'); + overallResult = RunState.failed; + continue; + } + + _printRunningExampleTestsMessage(example, targetPlatformString); final int exitCode = await _xcode.runXcodeBuild( example.directory, - targetPlatform, + targetPlatformString, // Clean before testing to remove cached swiftmodules from previous // runs, which can cause conflicts. actions: ['clean', 'test'], - workspace: '${targetPlatform.toLowerCase()}/Runner.xcworkspace', + workspace: '${targetPlatformString.toLowerCase()}/Runner.xcworkspace', scheme: 'Runner', configuration: 'Debug', hostPlatform: platform, @@ -513,10 +530,10 @@ this command. const int xcodebuildNoTestExitCode = 66; switch (exitCode) { case xcodebuildNoTestExitCode: - _printNoExampleTestsMessage(example, targetPlatform); + _printNoExampleTestsMessage(example, targetPlatformString); case 0: printSuccess( - 'Successfully ran $targetPlatform xctest for $exampleName'); + 'Successfully ran $targetPlatformString xctest for $exampleName'); // If this is the first test, assume success until something fails. if (overallResult == RunState.skipped) { overallResult = RunState.succeeded; diff --git a/script/tool/test/native_test_command_test.dart b/script/tool/test/native_test_command_test.dart index 0a9924727ef..b5b298d2fea 100644 --- a/script/tool/test/native_test_command_test.dart +++ b/script/tool/test/native_test_command_test.dart @@ -135,6 +135,21 @@ void main() { null); } + // Returns the ProcessCall to expect for generating the native project files + // with a --config-only build on iOS or macOS. + ProcessCall getConfigOnlyDarwinBuildCall( + Directory package, FlutterPlatform platform) { + return ProcessCall( + 'flutter', + [ + 'build', + if (platform == FlutterPlatform.ios) 'ios' else 'macos', + '--debug', + '--config-only', + ], + package.path); + } + // Returns the ProcessCall to expect for running the tests in the // workspace [platform]/Runner.xcworkspace, with the given extra flags. ProcessCall getRunTestCall( @@ -246,6 +261,8 @@ void main() { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'macos'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.macos), getRunTestCall(pluginExampleDirectory, 'macos', extraFlags: ['-only-testing:RunnerUITests']), ])); @@ -317,6 +334,8 @@ void main() { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'ios'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.ios), getRunTestCall(pluginExampleDirectory, 'ios', destination: 'foo_destination'), ])); @@ -354,6 +373,8 @@ void main() { ], null), getTargetCheckCall(pluginExampleDirectory, 'ios'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.ios), getRunTestCall(pluginExampleDirectory, 'ios', destination: 'id=$_simulatorDeviceId'), ])); @@ -421,6 +442,8 @@ void main() { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'macos'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.macos), getRunTestCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1305,6 +1328,8 @@ public class FlutterActivityTest { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'macos'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.macos), getRunTestCall(pluginExampleDirectory, 'macos', extraFlags: ['-only-testing:RunnerTests']), ])); @@ -1340,6 +1365,8 @@ public class FlutterActivityTest { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'macos'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.macos), getRunTestCall(pluginExampleDirectory, 'macos', extraFlags: ['-only-testing:RunnerUITests']), ])); @@ -1609,9 +1636,13 @@ public class FlutterActivityTest { ], androidFolder.path), getTargetCheckCall(pluginExampleDirectory, 'ios'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.ios), getRunTestCall(pluginExampleDirectory, 'ios', destination: 'foo_destination'), getTargetCheckCall(pluginExampleDirectory, 'macos'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.macos), getRunTestCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1648,6 +1679,8 @@ public class FlutterActivityTest { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'macos'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.macos), getRunTestCall(pluginExampleDirectory, 'macos'), ])); }); @@ -1684,6 +1717,8 @@ public class FlutterActivityTest { processRunner.recordedCalls, orderedEquals([ getTargetCheckCall(pluginExampleDirectory, 'ios'), + getConfigOnlyDarwinBuildCall( + pluginExampleDirectory, FlutterPlatform.ios), getRunTestCall(pluginExampleDirectory, 'ios', destination: 'foo_destination'), ]));