From 3bd5b1e6c84823212ad5da26c40f8ca2b5a7f06e Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 3 Jul 2026 09:28:05 +0200 Subject: [PATCH] [tests] Fail fast when the emulator package service is wedged Device integration tests only gate on "adb shell echo OK" before running. That succeeds even when the emulator's system_server / package manager service has crashed, so the health check passes, the test proceeds to build and install an app, and then fails deep inside the build with a cryptic bundletool "Failure calling service package: Broken pipe (32)" (XABAS0000) - reported as N unrelated per-test failures long after the emulator died. Probe the package manager service (the same "pm list features" path that bundletool "build-apks --connected-device" and "adb install" use) in the shared SetUp. When it is wedged, restart the emulator once; if it is still broken, mark the run inconclusive with a clear "package manager service is unresponsive" message so an emulator failure is surfaced immediately and is not mistaken for a product regression. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Utilities/DeviceTest.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) 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 5b0f5111993..5a639c3e12e 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 @@ -69,6 +69,59 @@ public void AssertHasDevices (bool fail = true) } } + /// + /// Probes the Android package manager service the same way `bundletool build-apks + /// --connected-device` (which runs `pm list features`) and `adb install` do. On CI the + /// emulator's system_server sometimes wedges while `adb shell echo OK` still succeeds, so + /// reports the device as healthy even though every subsequent + /// build/install fails deep inside a test with a cryptic + /// "Failure calling service package: Broken pipe" (XABAS0000). Retries a few times so a + /// momentary hiccup does not trigger an unnecessary emulator restart. + /// + protected static bool IsPackageManagerResponsive (out string diagnostic) + { + string output = ""; + for (int attempt = 0; attempt < 3; attempt++) { + output = (RunAdbCommand ("shell pm list features", timeout: 30) ?? "").Trim (); + bool broken = output.Contains ("Failure calling service", StringComparison.OrdinalIgnoreCase) || + output.Contains ("Broken pipe", StringComparison.OrdinalIgnoreCase) || + output.Contains ("Can't find service", StringComparison.OrdinalIgnoreCase); + // A responsive package service always lists the framework's own features. + if (!broken && output.Contains ("feature:", StringComparison.OrdinalIgnoreCase)) { + diagnostic = output; + return true; + } + WaitFor ((int) TimeSpan.FromSeconds (2).TotalMilliseconds); + } + diagnostic = output; + return false; + } + + /// + /// Fails fast, with a clear reason, when the emulator's package manager service is + /// unusable — instead of letting the app build/install fail cryptically later and + /// reporting an emulator crash as N unrelated test failures. Attempts one emulator + /// restart first; if the service is still broken the run is marked inconclusive + /// (an infrastructure problem, not a product regression). + /// + protected void AssertPackageManagerResponsive () + { + if (IsPackageManagerResponsive (out _)) { + return; + } + + TestContext.Out.WriteLine ("Android package manager service is unresponsive; restarting the emulator."); + RestartDevice (); + AssertHasDevices (); + + if (!IsPackageManagerResponsive (out string diagnostic)) { + Assert.Inconclusive ( + "Android package manager service is unresponsive even after restarting the emulator " + + "(system_server likely crashed). Skipping so an emulator failure is not reported as a test failure. " + + $"Last 'adb shell pm list features' output: {diagnostic}"); + } + } + [OneTimeSetUp] public void DeviceSetup () { @@ -126,6 +179,11 @@ public virtual void SetupTest () AssertHasDevices (); } + // `adb shell echo OK` can succeed while the package manager service is wedged, so + // verify it is responsive before a test wastes time building/installing an app that + // would otherwise fail with a cryptic "Failure calling service package" error. + AssertPackageManagerResponsive (); + // We want to start with a clean logcat buffer for each test ClearAdbLogcat (); }