From 344fe90a24e7567d50944d5c7584d6829b663e70 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 1 Jul 2026 10:51:18 +0200 Subject: [PATCH 1/3] [tests] Capture device state on device-test failure When an on-device test fails, DeviceTest.CleanupTest already attaches a screenshot, logcat, and UI dump. Add a best-effort device-state snapshot (device-state-failed.log) so on-device install/deploy failures can be classified from CI artifacts instead of guessed: connectivity (adb devices/get-state), disk pressure (df, dumpsys diskstats), storage-service readiness (dumpsys storaged - the StorageStatsManager NPE seen during install-create), boot completion, and how many test apps have accumulated. This covers the InstallAndRunTests/InstallTests/FastDevTest families (e.g. DeployToDevice), which all derive from DeviceTest. Context: dotnet/android#11830 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Utilities/DeviceTest.cs | 37 +++++++++++++++++++ 1 file changed, 37 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 e7a4dd5031c..80b2180b199 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 @@ -6,6 +6,7 @@ using System.Linq; using System.Diagnostics; using System.IO; +using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; @@ -159,11 +160,47 @@ protected override void CleanupTest () } else { TestContext.WriteLine ($"{localUi} did not exist!"); } + + CaptureDeviceState (outputDir); } base.CleanupTest (); } + // Best-effort device-state snapshot captured on test failure so that on-device + // install/deploy failures can be classified from CI artifacts instead of guessed: + // connectivity (adb devices/get-state), disk pressure (df, dumpsys diskstats), + // storage-service readiness (dumpsys storaged - the StorageStatsManager NPE seen + // during install-create), boot completion, and how many test apps have piled up. + // See dotnet/android#11830. + static void CaptureDeviceState (string outputDir) + { + var sb = new StringBuilder (); + foreach (var (title, command) in new [] { + ("adb devices -l", "devices -l"), + ("adb get-state", "get-state"), + ("getprop sys.boot_completed", "shell getprop sys.boot_completed"), + ("getprop dev.bootcomplete", "shell getprop dev.bootcomplete"), + ("df /data", "shell df /data"), + ("df /storage/emulated/0", "shell df /storage/emulated/0"), + ("dumpsys diskstats", "shell dumpsys diskstats"), + ("dumpsys storaged", "shell dumpsys storaged"), + ("pm list packages -3", "shell pm list packages -3"), + }) { + sb.AppendLine ($"===== {title} ====="); + sb.AppendLine (RunAdbCommand (command)); + sb.AppendLine (); + } + + string localState = Path.Combine (outputDir, "device-state-failed.log"); + File.WriteAllText (localState, sb.ToString ()); + if (File.Exists (localState)) { + TestContext.AddTestAttachment (localState); + } else { + TestContext.WriteLine ($"{localState} did not exist!"); + } + } + protected int GetSdkVersion () { var command = $"shell getprop ro.build.version.sdk"; From 60d9c610042e921f5f8608e545e5ba849c1414cb Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 1 Jul 2026 11:02:54 +0200 Subject: [PATCH 2/3] [tests] Re-check device attachment before capturing state Address review feedback: the cached IsDeviceAttached() value can be stale if the device disconnected mid-test, in which case each adb command in CaptureDeviceState would wait the full timeout against an unresponsive device. Re-check with a fresh probe and, when no device is attached, log 'No device attached; skipping device-state capture.' instead of silently doing nothing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs | 8 ++++++++ 1 file changed, 8 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 80b2180b199..116222c5072 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,6 +175,14 @@ protected override void CleanupTest () // See dotnet/android#11830. static void CaptureDeviceState (string outputDir) { + // Re-check attachment (the cached value can be stale if the device + // disconnected mid-test); otherwise each adb command below would wait + // the full timeout against an unresponsive device. + if (!IsDeviceAttached (refreshCachedValue: true)) { + TestContext.WriteLine ("No device attached; skipping device-state capture."); + return; + } + var sb = new StringBuilder (); foreach (var (title, command) in new [] { ("adb devices -l", "devices -l"), From a3a69e28989fa75912cf082faf16601c8f631a63 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 1 Jul 2026 11:45:52 +0200 Subject: [PATCH 3/3] Fix CA1305 in CaptureDeviceState Pass CultureInfo.InvariantCulture to StringBuilder.AppendLine to satisfy the CA1305 analyzer for the interpolated section header. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../Tests/Xamarin.Android.Build.Tests/Utilities/DeviceTest.cs | 2 +- 1 file changed, 1 insertion(+), 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 116222c5072..5b0f5111993 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 @@ -195,7 +195,7 @@ static void CaptureDeviceState (string outputDir) ("dumpsys storaged", "shell dumpsys storaged"), ("pm list packages -3", "shell pm list packages -3"), }) { - sb.AppendLine ($"===== {title} ====="); + sb.AppendLine (CultureInfo.InvariantCulture, $"===== {title} ====="); sb.AppendLine (RunAdbCommand (command)); sb.AppendLine (); }