From e9d8ba0d7b33ca8a31282b44afde6ed965aa5dbd Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 14 Apr 2026 11:26:20 -0500 Subject: [PATCH 1/4] Use Assert.Inconclusive for emulator acquisition failures Emulator acquisition failures are infrastructure issues, not test failures. Using Assert.Inconclusive instead of Assert.IsTrue marks these as skipped/inconclusive rather than turning the build red. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs index d5744ce96d4..f2a70afb973 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs @@ -174,7 +174,9 @@ public static void RestartDevice () // shell out to msbuild and start the emulator again var dotnet = new DotNetCLI (Path.Combine (XABuildPaths.TopDirectory, "src", "Xamarin.Android.Build.Tasks", "Tests", "Xamarin.Android.Build.Tests", "Emulator.csproj")); dotnet.ProjectDirectory = XABuildPaths.TestAssemblyOutputDirectory; - Assert.IsTrue (dotnet.Build ("AcquireAndroidTarget", parameters: new string[] { "TestAvdForceCreation=false", $"Configuration={XABuildPaths.Configuration}" }), "Failed to acquire emulator."); + if (!dotnet.Build ("AcquireAndroidTarget", parameters: new string[] { "TestAvdForceCreation=false", $"Configuration={XABuildPaths.Configuration}" })) { + Assert.Inconclusive ("Failed to acquire emulator."); + } WaitFor ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); } From 5cab7b61c6ac08b8c9372f4677988ad960da7e12 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 14 Apr 2026 12:23:21 -0500 Subject: [PATCH 2/4] Only Assert.Inconclusive for known transient emulator errors Check LastBuildOutput for known transient patterns before marking as Inconclusive. Unknown failures still Assert.Fail with the full build output for investigation. Transient patterns: - 'did not finish launching' (boot timeout) - 'Emulator failed to start' (start failure) - 'failed to exit within the timeout' (adb timeout) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs index f2a70afb973..e9262672a10 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs @@ -175,7 +175,13 @@ public static void RestartDevice () var dotnet = new DotNetCLI (Path.Combine (XABuildPaths.TopDirectory, "src", "Xamarin.Android.Build.Tasks", "Tests", "Xamarin.Android.Build.Tests", "Emulator.csproj")); dotnet.ProjectDirectory = XABuildPaths.TestAssemblyOutputDirectory; if (!dotnet.Build ("AcquireAndroidTarget", parameters: new string[] { "TestAvdForceCreation=false", $"Configuration={XABuildPaths.Configuration}" })) { - Assert.Inconclusive ("Failed to acquire emulator."); + var output = string.Join (Environment.NewLine, dotnet.LastBuildOutput); + if (output.Contains ("did not finish launching") || + output.Contains ("Emulator failed to start") || + output.Contains ("failed to exit within the timeout")) { + Assert.Inconclusive ("Failed to acquire emulator due to transient infrastructure issue."); + } + Assert.Fail ($"Failed to acquire emulator:{Environment.NewLine}{output}"); } WaitFor ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); } From 64aeecf3c39495d5e74c60f6ba6786115acc9925 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 14 Apr 2026 12:24:37 -0500 Subject: [PATCH 3/4] Use case-insensitive string comparison for error patterns Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs index e9262672a10..193192fa884 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs @@ -176,9 +176,9 @@ public static void RestartDevice () dotnet.ProjectDirectory = XABuildPaths.TestAssemblyOutputDirectory; if (!dotnet.Build ("AcquireAndroidTarget", parameters: new string[] { "TestAvdForceCreation=false", $"Configuration={XABuildPaths.Configuration}" })) { var output = string.Join (Environment.NewLine, dotnet.LastBuildOutput); - if (output.Contains ("did not finish launching") || - output.Contains ("Emulator failed to start") || - output.Contains ("failed to exit within the timeout")) { + if (output.Contains ("did not finish launching", StringComparison.OrdinalIgnoreCase) || + output.Contains ("Emulator failed to start", StringComparison.OrdinalIgnoreCase) || + output.Contains ("failed to exit within the timeout", StringComparison.OrdinalIgnoreCase)) { Assert.Inconclusive ("Failed to acquire emulator due to transient infrastructure issue."); } Assert.Fail ($"Failed to acquire emulator:{Environment.NewLine}{output}"); From 89d165f9fcda534720c806ffdb7415701a2e06e4 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Tue, 14 Apr 2026 16:40:31 -0500 Subject: [PATCH 4/4] Iterate lines instead of joining into one large string Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Utilities/DeviceTest.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs index 193192fa884..bb4870821b5 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs @@ -175,13 +175,14 @@ public static void RestartDevice () var dotnet = new DotNetCLI (Path.Combine (XABuildPaths.TopDirectory, "src", "Xamarin.Android.Build.Tasks", "Tests", "Xamarin.Android.Build.Tests", "Emulator.csproj")); dotnet.ProjectDirectory = XABuildPaths.TestAssemblyOutputDirectory; if (!dotnet.Build ("AcquireAndroidTarget", parameters: new string[] { "TestAvdForceCreation=false", $"Configuration={XABuildPaths.Configuration}" })) { - var output = string.Join (Environment.NewLine, dotnet.LastBuildOutput); - if (output.Contains ("did not finish launching", StringComparison.OrdinalIgnoreCase) || - output.Contains ("Emulator failed to start", StringComparison.OrdinalIgnoreCase) || - output.Contains ("failed to exit within the timeout", StringComparison.OrdinalIgnoreCase)) { + bool isTransient = dotnet.LastBuildOutput.Any (line => + line.Contains ("did not finish launching", StringComparison.OrdinalIgnoreCase) || + line.Contains ("Emulator failed to start", StringComparison.OrdinalIgnoreCase) || + line.Contains ("failed to exit within the timeout", StringComparison.OrdinalIgnoreCase)); + if (isTransient) { Assert.Inconclusive ("Failed to acquire emulator due to transient infrastructure issue."); } - Assert.Fail ($"Failed to acquire emulator:{Environment.NewLine}{output}"); + Assert.Fail ("Failed to acquire emulator."); } WaitFor ((int)TimeSpan.FromSeconds (5).TotalMilliseconds); }