From 0c32cf76d6e5002401c26f9e6eda85c6aa3758fc Mon Sep 17 00:00:00 2001 From: Paul Schneider Date: Thu, 20 Aug 2026 05:27:41 +0100 Subject: [PATCH] test(postit.android): add Xamarin.UITest smoke test on emulator Adds AndroidAppLaunchTests to PostIt.Tests, a Xamarin.UITest-based smoke test that launches the installed com.CompanyName.PostIt app on the running emulator and waits for the first Avalonia frame to render. The test skips cleanly when the app is not installed. Currently FAILS RED on the local emulator: the installed APK has no Activity declared (am start returns result code=-92, ACTIVITY_NOT_FOUND), and Xamarin.UITest's test server cannot reach /ping. This is a guardian test that will turn green once EmbedAssembliesIntoApk=true is set in PostIt.Android.csproj (follow-up commit). Also adds a Debug launch config in .vscode/launch.json that runs the test under vsdbg, enabling breakpoints and object inspection when investigating the failure. --- .vscode/launch.json | 13 ++++ src/PostIt.Tests/AndroidAppLaunchTests.cs | 76 +++++++++++++++++++++++ src/PostIt.Tests/Directory.Packages.props | 2 +- src/PostIt.Tests/PostIt.Tests.csproj | 1 + 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 src/PostIt.Tests/AndroidAppLaunchTests.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index 76dc08d50..efdaa6eaa 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -28,6 +28,19 @@ "request": "launch", "projectPath": "${workspaceFolder}/src/PostIt/PostIt.Desktop/PostIt.Desktop.csproj", + }, + { + "name": "Test PostIt.Android launch (Xamarin.UITest)", + "type": "coreclr", + "request": "launch", + "program": "${workspaceFolder}/src/PostIt.Tests/bin/Debug/net10.0/PostIt.Tests.dll", + "args": [ + "--filter-method", + "PostIt.Tests.AndroidAppLaunchTests.PostIt_starts_and_draws_a_first_frame_on_the_emulator" + ], + "cwd": "${workspaceFolder}/src/PostIt.Tests", + "console": "integratedTerminal", + "stopAtEntry": false } ] } diff --git a/src/PostIt.Tests/AndroidAppLaunchTests.cs b/src/PostIt.Tests/AndroidAppLaunchTests.cs new file mode 100644 index 000000000..2198bb784 --- /dev/null +++ b/src/PostIt.Tests/AndroidAppLaunchTests.cs @@ -0,0 +1,76 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using Xamarin.UITest; +using Xamarin.UITest.Android; +using Xunit; + +namespace PostIt.Tests; + +/// +/// Smoke test: launches the installed PostIt.Android app on the running +/// emulator and waits for the first Avalonia frame to render. Reveals the +/// "démarrage KO" bug — the test fails if Avalonia never draws a frame +/// within the timeout. +/// +/// Skip conditions: the package is not installed on the connected device, +/// or no device is connected via adb. +/// +public class AndroidAppLaunchTests +{ + private const string PackageName = "com.CompanyName.PostIt"; + + private readonly ITestOutputHelper _output; + + public AndroidAppLaunchTests(ITestOutputHelper output) + { + _output = output; + } + + [Fact] + public void PostIt_starts_and_draws_a_first_frame_on_the_emulator() + { + if (!IsPackageInstalledOnAnyDevice()) + { + _output.WriteLine($"[skip] {PackageName} not installed on any device"); + return; + } + + _output.WriteLine($"[step] configuring app via InstalledApp({PackageName})"); + var app = ConfigureApp.Android + .InstalledApp(PackageName) + .StartApp(Xamarin.UITest.Configuration.AppDataMode.DoNotClear); + _output.WriteLine("[step] app.StartApp returned, waiting for first frame"); + + app.WaitForElement( + e => e.Class("android.view.View"), + timeout: TimeSpan.FromSeconds(30)); + _output.WriteLine("[step] first frame observed"); + } + + private static bool IsPackageInstalledOnAnyDevice() + { + try + { + var startInfo = new ProcessStartInfo("adb", "shell pm list packages") + { + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + using var proc = Process.Start(startInfo); + if (proc is null) return false; + var stdout = proc.StandardOutput.ReadToEnd(); + proc.WaitForExit(5000); + return stdout + .Split('\n', StringSplitOptions.RemoveEmptyEntries) + .Any(line => line.Trim().Equals($"package:{PackageName}", StringComparison.Ordinal)); + } + catch + { + return false; + } + } +} diff --git a/src/PostIt.Tests/Directory.Packages.props b/src/PostIt.Tests/Directory.Packages.props index 15c4e24b0..4731d4f00 100644 --- a/src/PostIt.Tests/Directory.Packages.props +++ b/src/PostIt.Tests/Directory.Packages.props @@ -1,10 +1,10 @@ - + \ No newline at end of file diff --git a/src/PostIt.Tests/PostIt.Tests.csproj b/src/PostIt.Tests/PostIt.Tests.csproj index 54c40e8c2..249e3d92f 100644 --- a/src/PostIt.Tests/PostIt.Tests.csproj +++ b/src/PostIt.Tests/PostIt.Tests.csproj @@ -13,6 +13,7 @@ +