[tool] Run config-only build for iOS/macOS native-test (#9080)

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.
This commit is contained in:
stuartmorgan-g
2025-04-15 15:23:54 -07:00
committed by GitHub
parent 2fcc4032dd
commit 26d11f40a5
2 changed files with 62 additions and 10 deletions

View File

@ -433,12 +433,12 @@ this command.
} }
Future<_PlatformResult> _testIOS(RepositoryPackage plugin, _TestMode mode) { Future<_PlatformResult> _testIOS(RepositoryPackage plugin, _TestMode mode) {
return _runXcodeTests(plugin, 'iOS', mode, return _runXcodeTests(plugin, FlutterPlatform.ios, mode,
extraFlags: _iOSDestinationFlags); extraFlags: _iOSDestinationFlags);
} }
Future<_PlatformResult> _testMacOS(RepositoryPackage plugin, _TestMode mode) { 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 /// Runs all applicable tests for [plugin], printing status and returning
@ -448,7 +448,7 @@ this command.
/// usually at "example/{ios,macos}/Runner.xcworkspace". /// usually at "example/{ios,macos}/Runner.xcworkspace".
Future<_PlatformResult> _runXcodeTests( Future<_PlatformResult> _runXcodeTests(
RepositoryPackage plugin, RepositoryPackage plugin,
String targetPlatform, FlutterPlatform targetPlatform,
_TestMode mode, { _TestMode mode, {
List<String> extraFlags = const <String>[], List<String> extraFlags = const <String>[],
}) async { }) async {
@ -459,6 +459,8 @@ this command.
} else if (mode.integrationOnly) { } else if (mode.integrationOnly) {
testTarget = 'RunnerUITests'; testTarget = 'RunnerUITests';
} }
final String targetPlatformString =
targetPlatform == FlutterPlatform.ios ? 'iOS' : 'macOS';
bool ranUnitTests = false; bool ranUnitTests = false;
// Assume skipped until at least one test has run. // Assume skipped until at least one test has run.
@ -472,8 +474,8 @@ this command.
bool exampleHasUnitTests = false; bool exampleHasUnitTests = false;
final String? targetToCheck = final String? targetToCheck =
testTarget ?? (mode.unit ? unitTestTarget : null); testTarget ?? (mode.unit ? unitTestTarget : null);
final Directory xcodeProject = example.directory final Directory xcodeProject = example
.childDirectory(targetPlatform.toLowerCase()) .platformDirectory(targetPlatform)
.childDirectory('Runner.xcodeproj'); .childDirectory('Runner.xcodeproj');
if (targetToCheck != null) { if (targetToCheck != null) {
final bool? hasTarget = 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( final int exitCode = await _xcode.runXcodeBuild(
example.directory, example.directory,
targetPlatform, targetPlatformString,
// Clean before testing to remove cached swiftmodules from previous // Clean before testing to remove cached swiftmodules from previous
// runs, which can cause conflicts. // runs, which can cause conflicts.
actions: <String>['clean', 'test'], actions: <String>['clean', 'test'],
workspace: '${targetPlatform.toLowerCase()}/Runner.xcworkspace', workspace: '${targetPlatformString.toLowerCase()}/Runner.xcworkspace',
scheme: 'Runner', scheme: 'Runner',
configuration: 'Debug', configuration: 'Debug',
hostPlatform: platform, hostPlatform: platform,
@ -513,10 +530,10 @@ this command.
const int xcodebuildNoTestExitCode = 66; const int xcodebuildNoTestExitCode = 66;
switch (exitCode) { switch (exitCode) {
case xcodebuildNoTestExitCode: case xcodebuildNoTestExitCode:
_printNoExampleTestsMessage(example, targetPlatform); _printNoExampleTestsMessage(example, targetPlatformString);
case 0: case 0:
printSuccess( 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 this is the first test, assume success until something fails.
if (overallResult == RunState.skipped) { if (overallResult == RunState.skipped) {
overallResult = RunState.succeeded; overallResult = RunState.succeeded;

View File

@ -135,6 +135,21 @@ void main() {
null); 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',
<String>[
'build',
if (platform == FlutterPlatform.ios) 'ios' else 'macos',
'--debug',
'--config-only',
],
package.path);
}
// Returns the ProcessCall to expect for running the tests in the // Returns the ProcessCall to expect for running the tests in the
// workspace [platform]/Runner.xcworkspace, with the given extra flags. // workspace [platform]/Runner.xcworkspace, with the given extra flags.
ProcessCall getRunTestCall( ProcessCall getRunTestCall(
@ -246,6 +261,8 @@ void main() {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'macos'), getTargetCheckCall(pluginExampleDirectory, 'macos'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.macos),
getRunTestCall(pluginExampleDirectory, 'macos', getRunTestCall(pluginExampleDirectory, 'macos',
extraFlags: <String>['-only-testing:RunnerUITests']), extraFlags: <String>['-only-testing:RunnerUITests']),
])); ]));
@ -317,6 +334,8 @@ void main() {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'ios'), getTargetCheckCall(pluginExampleDirectory, 'ios'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.ios),
getRunTestCall(pluginExampleDirectory, 'ios', getRunTestCall(pluginExampleDirectory, 'ios',
destination: 'foo_destination'), destination: 'foo_destination'),
])); ]));
@ -354,6 +373,8 @@ void main() {
], ],
null), null),
getTargetCheckCall(pluginExampleDirectory, 'ios'), getTargetCheckCall(pluginExampleDirectory, 'ios'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.ios),
getRunTestCall(pluginExampleDirectory, 'ios', getRunTestCall(pluginExampleDirectory, 'ios',
destination: 'id=$_simulatorDeviceId'), destination: 'id=$_simulatorDeviceId'),
])); ]));
@ -421,6 +442,8 @@ void main() {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'macos'), getTargetCheckCall(pluginExampleDirectory, 'macos'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.macos),
getRunTestCall(pluginExampleDirectory, 'macos'), getRunTestCall(pluginExampleDirectory, 'macos'),
])); ]));
}); });
@ -1305,6 +1328,8 @@ public class FlutterActivityTest {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'macos'), getTargetCheckCall(pluginExampleDirectory, 'macos'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.macos),
getRunTestCall(pluginExampleDirectory, 'macos', getRunTestCall(pluginExampleDirectory, 'macos',
extraFlags: <String>['-only-testing:RunnerTests']), extraFlags: <String>['-only-testing:RunnerTests']),
])); ]));
@ -1340,6 +1365,8 @@ public class FlutterActivityTest {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'macos'), getTargetCheckCall(pluginExampleDirectory, 'macos'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.macos),
getRunTestCall(pluginExampleDirectory, 'macos', getRunTestCall(pluginExampleDirectory, 'macos',
extraFlags: <String>['-only-testing:RunnerUITests']), extraFlags: <String>['-only-testing:RunnerUITests']),
])); ]));
@ -1609,9 +1636,13 @@ public class FlutterActivityTest {
], ],
androidFolder.path), androidFolder.path),
getTargetCheckCall(pluginExampleDirectory, 'ios'), getTargetCheckCall(pluginExampleDirectory, 'ios'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.ios),
getRunTestCall(pluginExampleDirectory, 'ios', getRunTestCall(pluginExampleDirectory, 'ios',
destination: 'foo_destination'), destination: 'foo_destination'),
getTargetCheckCall(pluginExampleDirectory, 'macos'), getTargetCheckCall(pluginExampleDirectory, 'macos'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.macos),
getRunTestCall(pluginExampleDirectory, 'macos'), getRunTestCall(pluginExampleDirectory, 'macos'),
])); ]));
}); });
@ -1648,6 +1679,8 @@ public class FlutterActivityTest {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'macos'), getTargetCheckCall(pluginExampleDirectory, 'macos'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.macos),
getRunTestCall(pluginExampleDirectory, 'macos'), getRunTestCall(pluginExampleDirectory, 'macos'),
])); ]));
}); });
@ -1684,6 +1717,8 @@ public class FlutterActivityTest {
processRunner.recordedCalls, processRunner.recordedCalls,
orderedEquals(<ProcessCall>[ orderedEquals(<ProcessCall>[
getTargetCheckCall(pluginExampleDirectory, 'ios'), getTargetCheckCall(pluginExampleDirectory, 'ios'),
getConfigOnlyDarwinBuildCall(
pluginExampleDirectory, FlutterPlatform.ios),
getRunTestCall(pluginExampleDirectory, 'ios', getRunTestCall(pluginExampleDirectory, 'ios',
destination: 'foo_destination'), destination: 'foo_destination'),
])); ]));