test: add Android emulator test for dotnet signal handling#1574
Merged
jpnurmi merged 29 commits intojpnurmi/fix/sa-nodefer-chain-at-startfrom Mar 13, 2026
Merged
Conversation
f683589 to
482b343
Compare
Extends the existing dotnet_signal fixture to build as an Android APK and run on an emulator. The test verifies CHAIN_AT_START signal handling with Mono: - Handled managed exception (NRE): no native crash registered - Unhandled managed exception: Mono aborts, native crash registered - Native crash (SIGSEGV in libcrash.so): crash envelope produced The fixture csproj now multi-targets net10.0 and net10.0-android. Program.cs exposes RunTest() for the Android MainActivity entry point. Database state is checked directly on-device via adb run-as. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CI sets TEST_X86/ANDROID_API/RUN_ANALYZER to empty strings. Python's `or` chain returns the last falsy value (empty string ""), which pytest then tries to compile() as a legacy condition expression, causing SyntaxError. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Desktop CI runners have .NET 10 but no android workload. MSBuild validates all TFMs even when building for a specific one, causing NETSDK1147. Conditionally add the android TFM only when ANDROID_HOME is present. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
run-as can't find the `test` binary on older API levels. Use sh -c with a single quoted command string so shell builtins are available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
am start is asynchronous — it returns before the process starts. The pidof poll could find no process yet and break immediately, causing assertions to run before sentry_init, and the finally block's adb uninstall to kill the app mid-startup (deletePackageX). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Microsoft.NET.Sdk (plain) compiles all .cs files regardless of directory conventions. Exclude Platforms/Android/ when not building for an Android target framework. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
OnCreate fires before the activity reports launch completion, so if the test crashes the app, am start -W blocks indefinitely on older API levels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use wait_for() with polling instead of fixed sleeps to handle timing variations across emulator API levels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Post ensures OnResume returns before the test runs (so am start -W completes). Worker thread avoids Android's main thread uncaught exception handler killing the process with SIGKILL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move test back to OnResume with worker thread (no DecorView.Post). Silently handle am start -W timeout since older API levels may not report activity launch completion before the app exits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Run the test on the main thread via Handler.Post and catch unhandled managed exceptions with try-catch + abort(), matching how MAUI handles them. This ensures sentry-native's signal handler captures the crash across all Android API levels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Unhandled managed exceptions on Android go through Mono's exit(1), not a catchable signal. sentry-dotnet handles these at the managed layer via UnhandledExceptionRaiser, so sentry-native should not register a crash. Remove the abort() workaround and align expectations with the desktop test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Switch emulator target from default to google_apis to fix stuck emulator boots on API 23-25. Drop API 27 which has no google_apis x86_64 system image. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pre-tombstoned Android (API < 26) uses debuggerd which kills the process before sentry-native's signal handler can run when using CHAIN_AT_START strategy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9cdb64a to
d337b49
Compare
jpnurmi
commented
Mar 13, 2026
| ANDROID_ARCH: x86_64 | ||
| - name: Android (API 31, NDK 27) | ||
| os: macos-15-large | ||
| os: ubuntu-latest |
Collaborator
Author
There was a problem hiding this comment.
I left one of the Android builds (API 35) running with macos-15-large and changed the rest to use ubuntu-latest, which is much cheaper, faster, and works fine with KVM. The host doesn't really matter, but it's nice to have both in the CI to ensure the test suite works on either.
The find command uses single-quoted '*.envelope' glob pattern which broke when wrapped in single-quoted sh -c. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
9547e67
into
jpnurmi/fix/sa-nodefer-chain-at-start
49 checks passed
jpnurmi
added a commit
that referenced
this pull request
Mar 14, 2026
* test: add Android emulator test for dotnet signal handling Extends the existing dotnet_signal fixture to build as an Android APK and run on an emulator. The test verifies CHAIN_AT_START signal handling with Mono: - Handled managed exception (NRE): no native crash registered - Unhandled managed exception: Mono aborts, native crash registered - Native crash (SIGSEGV in libcrash.so): crash envelope produced The fixture csproj now multi-targets net10.0 and net10.0-android. Program.cs exposes RunTest() for the Android MainActivity entry point. Database state is checked directly on-device via adb run-as. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: wrap skipif conditions in bool() to avoid pytest string evaluation CI sets TEST_X86/ANDROID_API/RUN_ANALYZER to empty strings. Python's `or` chain returns the last falsy value (empty string ""), which pytest then tries to compile() as a legacy condition expression, causing SyntaxError. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: install .NET SDK and Android workload for Android CI jobs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: only target net10.0-android when ANDROID_HOME is set Desktop CI runners have .NET 10 but no android workload. MSBuild validates all TFMs even when building for a specific one, causing NETSDK1147. Conditionally add the android TFM only when ANDROID_HOME is present. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use ANDROID_API instead of ANDROID_HOME for android TFM condition Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: print logcat output for Android test diagnostics Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use sh -c for run-as commands on Android run-as can't find the `test` binary on older API levels. Use sh -c with a single quoted command string so shell builtins are available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: include stdout/stderr in dotnet run returncode assertions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use am start -W to avoid race in Android test am start is asynchronous — it returns before the process starts. The pidof poll could find no process yet and break immediately, causing assertions to run before sentry_init, and the finally block's adb uninstall to kill the app mid-startup (deletePackageX). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: exclude Android sources from desktop dotnet build Microsoft.NET.Sdk (plain) compiles all .cs files regardless of directory conventions. Exclude Platforms/Android/ when not building for an Android target framework. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * try arm64-v8a * try x86_64 on linux * Test 22-30 * fix: move test logic to OnResume to avoid am start -W hang OnCreate fires before the activity reports launch completion, so if the test crashes the app, am start -W blocks indefinitely on older API levels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: use default emulator target instead of google_apis Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: replace sleeps with retry loops in Android test Use wait_for() with polling instead of fixed sleeps to handle timing variations across emulator API levels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: add timeout to am start -W with logcat dump on failure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: run test directly on UI thread instead of background thread Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use DecorView.Post + worker thread for Android test Post ensures OnResume returns before the test runs (so am start -W completes). Worker thread avoids Android's main thread uncaught exception handler killing the process with SIGKILL. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: simplify Android test app and handle am start -W timeout Move test back to OnResume with worker thread (no DecorView.Post). Silently handle am start -W timeout since older API levels may not report activity launch completion before the app exits. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: emulate MAUI abort behavior for unhandled exceptions on Android Run the test on the main thread via Handler.Post and catch unhandled managed exceptions with try-catch + abort(), matching how MAUI handles them. This ensures sentry-native's signal handler captures the crash across all Android API levels. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: expect no crash for unhandled managed exceptions on Android Unhandled managed exceptions on Android go through Mono's exit(1), not a catchable signal. sentry-dotnet handles these at the managed layer via UnhandledExceptionRaiser, so sentry-native should not register a crash. Remove the abort() workaround and align expectations with the desktop test. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: drop broken Android API 22 job Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: switch Android emulator to google_apis, drop API 27 Switch emulator target from default to google_apis to fix stuck emulator boots on API 23-25. Drop API 27 which has no google_apis x86_64 system image. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test: skip Android dotnet signal test on API < 26 Pre-tombstoned Android (API < 26) uses debuggerd which kills the process before sentry-native's signal handler can run when using CHAIN_AT_START strategy. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Try macos-15-large again, drop others but 26 * test: clean up dotnet signal test assertions and comments Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * ci: switch Android emulator back to default target Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: use double quotes in run-as shell wrapper to preserve inner quotes The find command uses single-quoted '*.envelope' glob pattern which broke when wrapped in single-quoted sh -c. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Extends the existing
test_dotnet_signalswith an Android emulator test for:Builds as an Android APK, run on an emulator, and verify
CHAIN_AT_STARTsignal handling with Mono:SIGSEGVand converts it to aNullReferenceException, no native crash registeredexit(1), no native crash registered (sentry-dotnet handles this at the managed layer viaUnhandledExceptionRaiser)SIGSEGVin libcrash.so): crash envelope producedReproduced the following test failure (before the base was changed to #1572):
Note
Turns out
CHAIN_AT_STARTrequires API 26+ (tombstoned). With pre-API 26,debuggerdkills the process before the signal handler can run.