diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 5e2ae792111b..85b1789deae2 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -24,7 +24,7 @@
"rollForward": false
},
"microsoft.dotnet.xharness.cli": {
- "version": "10.0.0-prerelease.25575.2",
+ "version": "11.0.0-prerelease.25603.1",
"commands": [
"xharness"
],
diff --git a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Apple/Simulator.cs b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Apple/Simulator.cs
index 64288dd8f7d0..30abd07e0a31 100644
--- a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Apple/Simulator.cs
+++ b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Apple/Simulator.cs
@@ -19,7 +19,34 @@ public string GetUDID()
public bool Launch()
{
ToolRunner.Run(XCRunTool, $"simctl boot {GetUDID()}", out int exitCode, timeoutInSeconds: 30);
- return exitCode == 0;
+ if (exitCode != 0)
+ return false;
+
+ // Wait for the simulator to be fully booted before returning
+ // The boot command can return before the simulator is ready to accept app launches
+ return WaitForSimulatorReady(timeoutInSeconds: 60);
+ }
+
+ bool WaitForSimulatorReady(int timeoutInSeconds = 60)
+ {
+ var udid = GetUDID();
+ var startTime = DateTime.UtcNow;
+ var timeout = TimeSpan.FromSeconds(timeoutInSeconds);
+
+ while (DateTime.UtcNow - startTime < timeout)
+ {
+ var output = ToolRunner.Run(XCRunTool, $"simctl bootstatus {udid}", out int exitCode, timeoutInSeconds: 10);
+ if (exitCode == 0)
+ return true;
+
+ // Print diagnostic output to help troubleshoot boot issues
+ TestContext.WriteLine($"Simulator not ready yet: {output}");
+
+ // Wait a bit before checking again
+ System.Threading.Thread.Sleep(2000);
+ }
+
+ return false;
}
public bool Shutdown()
diff --git a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs
index a5b5dd44a3d7..cb9a6370397d 100644
--- a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs
+++ b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/AppleTemplateTests.cs
@@ -51,7 +51,6 @@ public void RunOniOS(string id, string config, string framework, string runtimeI
buildProps.Add("PublishAot=true");
buildProps.Add("PublishAotUsingRuntimePack=true"); // TODO: This parameter will become obsolete https://github.com/dotnet/runtime/issues/87060
buildProps.Add("_IsPublishing=true"); // using dotnet build with -p:_IsPublishing=true enables targeting simulators
- buildProps.Add($"RuntimeIdentifier={runtimeIdentifier}");
buildProps.Add("IlcTreatWarningsAsErrors=false"); // TODO: Remove this once all warnings are fixed https://github.com/dotnet/maui/issues/19397
}
@@ -61,12 +60,13 @@ public void RunOniOS(string id, string config, string framework, string runtimeI
buildProps.Add("TrimmerSingleWarn=false"); // Disable trimmer warnings for iOS full trimming builds due to ObjCRuntime issues
}
- Assert.IsTrue(DotnetInternal.Build(projectFile, config, framework: $"{framework}-ios", properties: buildProps),
+ Assert.IsTrue(DotnetInternal.Build(projectFile, config, framework: $"{framework}-ios", properties: buildProps, runtimeIdentifier: runtimeIdentifier),
$"Project {Path.GetFileName(projectFile)} failed to build. Check test output/attachments for errors.");
var appFile = Path.Combine(projectDir, "bin", config, $"{framework}-ios", runtimeIdentifier, $"{Path.GetFileName(projectDir)}.app");
- Assert.IsTrue(XHarness.RunAppleForTimeout(appFile, Path.Combine(projectDir, "xh-results"), TestSimulator.XHarnessID),
+ // Pass the simulator UDID to help XHarness use our already-booted simulator
+ Assert.IsTrue(XHarness.RunAppleForTimeout(appFile, Path.Combine(projectDir, "xh-results"), TestSimulator.XHarnessID, TestSimulator.GetUDID()),
$"Project {Path.GetFileName(projectFile)} failed to run. Check test output/attachments for errors.");
}
}
diff --git a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/XHarness.cs b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/XHarness.cs
index 03e023525465..abecbc292e7a 100644
--- a/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/XHarness.cs
+++ b/src/TestUtils/src/Microsoft.Maui.IntegrationTests/Utilities/XHarness.cs
@@ -21,12 +21,14 @@ public static bool RunAndroid(string packageName, string resultDir, int expected
///
///
///
+ /// Optional UDID of a specific already-booted simulator to use.
///
/// True if the app launch command timed out, false if it exits early.
- public static bool RunAppleForTimeout(string appPath, string resultDir, string targetDevice, int launchTimeoutSeconds = 75)
+ public static bool RunAppleForTimeout(string appPath, string resultDir, string targetDevice, string? deviceUDID = null, int launchTimeoutSeconds = 75)
{
var timeoutString = TimeSpan.FromSeconds(launchTimeoutSeconds).ToString();
- var args = $"apple run --app=\"{appPath}\" --output-directory=\"{resultDir}\" --target={targetDevice} --timeout=\"{timeoutString}\" --verbosity=Debug";
+ var deviceArg = string.IsNullOrEmpty(deviceUDID) ? "" : $" --device=\"{deviceUDID}\"";
+ var args = $"apple run --app=\"{appPath}\" --output-directory=\"{resultDir}\" --target={targetDevice}{deviceArg} --timeout=\"{timeoutString}\" --verbosity=Debug";
var xhOutput = RunForOutput(args, out int exitCode, launchTimeoutSeconds + 30);
var launchLogMatch = false;