Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7a5322d
test: add Android emulator test for dotnet signal handling
jpnurmi Mar 12, 2026
09a5a3a
fix: wrap skipif conditions in bool() to avoid pytest string evaluation
jpnurmi Mar 12, 2026
43a5fe2
ci: install .NET SDK and Android workload for Android CI jobs
jpnurmi Mar 12, 2026
956ceb5
fix: only target net10.0-android when ANDROID_HOME is set
jpnurmi Mar 12, 2026
03a16f7
fix: use ANDROID_API instead of ANDROID_HOME for android TFM condition
jpnurmi Mar 12, 2026
236914d
test: print logcat output for Android test diagnostics
jpnurmi Mar 12, 2026
e2f516c
fix: use sh -c for run-as commands on Android
jpnurmi Mar 12, 2026
4206e14
test: include stdout/stderr in dotnet run returncode assertions
jpnurmi Mar 12, 2026
e0642e7
fix: use am start -W to avoid race in Android test
jpnurmi Mar 12, 2026
39361fc
fix: exclude Android sources from desktop dotnet build
jpnurmi Mar 12, 2026
e03edad
try arm64-v8a
jpnurmi Mar 12, 2026
8dc739f
try x86_64 on linux
jpnurmi Mar 12, 2026
5480f0a
Test 22-30
jpnurmi Mar 13, 2026
594bd41
fix: move test logic to OnResume to avoid am start -W hang
jpnurmi Mar 13, 2026
17714ed
ci: use default emulator target instead of google_apis
jpnurmi Mar 13, 2026
cdf3474
fix: replace sleeps with retry loops in Android test
jpnurmi Mar 13, 2026
23e5bfe
fix: add timeout to am start -W with logcat dump on failure
jpnurmi Mar 13, 2026
41a24dd
fix: run test directly on UI thread instead of background thread
jpnurmi Mar 13, 2026
ad29174
fix: use DecorView.Post + worker thread for Android test
jpnurmi Mar 13, 2026
dd06544
fix: simplify Android test app and handle am start -W timeout
jpnurmi Mar 13, 2026
d399edb
fix: emulate MAUI abort behavior for unhandled exceptions on Android
jpnurmi Mar 13, 2026
c4b63fa
fix: expect no crash for unhandled managed exceptions on Android
jpnurmi Mar 13, 2026
d51ab3f
ci: drop broken Android API 22 job
jpnurmi Mar 13, 2026
f45a661
ci: switch Android emulator to google_apis, drop API 27
jpnurmi Mar 13, 2026
6947607
test: skip Android dotnet signal test on API < 26
jpnurmi Mar 13, 2026
286b922
Try macos-15-large again, drop others but 26
jpnurmi Mar 13, 2026
cd82d46
test: clean up dotnet signal test assertions and comments
jpnurmi Mar 13, 2026
d337b49
ci: switch Android emulator back to default target
jpnurmi Mar 13, 2026
c03b5af
fix: use double quotes in run-as shell wrapper to preserve inner quotes
jpnurmi Mar 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 27 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,17 @@ jobs:
MINGW_ASM_MASM_COMPILER: llvm-ml
MINGW_ASM_MASM_FLAGS: -m64
- name: Android (API 21, NDK 23)
os: macos-15-large
os: ubuntu-latest
ANDROID_API: 21
ANDROID_NDK: 23.2.8568313
ANDROID_ARCH: x86_64
- name: Android (API 26, NDK 27)
os: ubuntu-latest
ANDROID_API: 26
ANDROID_NDK: 27.3.13750724
ANDROID_ARCH: x86_64
- name: Android (API 31, NDK 27)
os: macos-15-large
os: ubuntu-latest
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

ANDROID_API: 31
ANDROID_NDK: 27.3.13750724
ANDROID_ARCH: x86_64
Expand Down Expand Up @@ -242,12 +247,12 @@ jobs:
cache: "pip"

- name: Check Linux CC/CXX
if: ${{ runner.os == 'Linux' && !matrix.container }}
if: ${{ runner.os == 'Linux' && !env['ANDROID_API'] &&!matrix.container }}
run: |
[ -n "$CC" ] && [ -n "$CXX" ] || { echo "Ubuntu runner configurations require toolchain selection via CC and CXX" >&2; exit 1; }

- name: Installing Linux Dependencies
if: ${{ runner.os == 'Linux' && !env['TEST_X86'] && !matrix.container }}
if: ${{ runner.os == 'Linux' && !env['TEST_X86'] && !env['ANDROID_API'] && !matrix.container }}
run: |
sudo apt update
# Install common dependencies
Expand Down Expand Up @@ -278,7 +283,7 @@ jobs:
sudo make install

- name: Installing Linux 32-bit Dependencies
if: ${{ runner.os == 'Linux' && env['TEST_X86'] && !matrix.container }}
if: ${{ runner.os == 'Linux' && env['TEST_X86'] && !env['ANDROID_API'] &&!matrix.container }}
run: |
sudo dpkg --add-architecture i386
sudo apt update
Expand Down Expand Up @@ -357,6 +362,22 @@ jobs:
with:
gradle-home-cache-cleanup: true

- name: Setup .NET for Android
if: ${{ env['ANDROID_API'] }}
uses: actions/setup-dotnet@v5
with:
dotnet-version: '10.0.x'

- name: Install .NET Android workload
if: ${{ env['ANDROID_API'] }}
run: dotnet workload restore tests/fixtures/dotnet_signal/test_dotnet.csproj

- name: Enable KVM group perms
if: ${{ runner.os == 'Linux' && env['ANDROID_API'] }}
run: |
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
sudo udevadm control --reload-rules
sudo udevadm trigger --name-match=kvm

- name: Add sentry.native.test hostname
if: ${{ runner.os == 'Windows' }}
Expand Down Expand Up @@ -386,7 +407,7 @@ jobs:
api-level: ${{ env.ANDROID_API }}
ndk: ${{ env.ANDROID_NDK }}
arch: ${{ env.ANDROID_ARCH }}
target: google_apis
target: default
emulator-boot-timeout: 1200
script: |
# Sync emulator clock with host to avoid timestamp assertion failures
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures/dotnet_signal/Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<!-- Prevent MSBuild from using parent Directory.Build.props -->
<Project />
31 changes: 31 additions & 0 deletions tests/fixtures/dotnet_signal/Platforms/Android/MainActivity.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using Android.App;
using Android.OS;

// Required for "adb shell run-as" to access the app's data directory in Release builds
[assembly: Application(Debuggable = true)]

namespace dotnet_signal;

[Activity(Name = "dotnet_signal.MainActivity", MainLauncher = true)]
public class MainActivity : Activity
{
protected override void OnResume()
{
base.OnResume();

var arg = Intent?.GetStringExtra("arg");
if (!string.IsNullOrEmpty(arg))
{
var databasePath = FilesDir?.AbsolutePath + "/.sentry-native";

// Post to the message queue so the activity finishes starting
// before the crash test runs. Without this, "am start -W" may hang.
new Handler(Looper.MainLooper!).Post(() =>
{
Program.RunTest(new[] { arg }, databasePath);
FinishAndRemoveTask();
Java.Lang.JavaSystem.Exit(0);
});
}
}
}
25 changes: 19 additions & 6 deletions tests/fixtures/dotnet_signal/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ class Program
[DllImport("sentry", EntryPoint = "sentry_options_set_debug")]
static extern IntPtr sentry_options_set_debug(IntPtr options, int debug);

[DllImport("sentry", EntryPoint = "sentry_options_set_database_path")]
static extern void sentry_options_set_database_path(IntPtr options, string path);

[DllImport("sentry", EntryPoint = "sentry_init")]
static extern int sentry_init(IntPtr options);

static void Main(string[] args)
public static void RunTest(string[] args, string? databasePath = null)
{
var githubActions = Environment.GetEnvironmentVariable("GITHUB_ACTIONS") ?? string.Empty;
if (githubActions == "true") {
Expand All @@ -38,10 +41,13 @@ static void Main(string[] args)
var options = sentry_options_new();
sentry_options_set_handler_strategy(options, 1);
sentry_options_set_debug(options, 1);
if (databasePath != null)
{
sentry_options_set_database_path(options, databasePath);
}
sentry_init(options);

var doNativeCrash = args is ["native-crash"];
if (doNativeCrash)
if (args.Contains("native-crash"))
{
native_crash();
}
Expand All @@ -51,17 +57,24 @@ static void Main(string[] args)
{
Console.WriteLine("dereference a NULL object from managed code");
var s = default(string);
var c = s.Length;
var c = s!.Length;
}
catch (NullReferenceException exception)
catch (NullReferenceException)
{
}
}
else if (args.Contains("unhandled-managed-exception"))
{
Console.WriteLine("dereference a NULL object from managed code (unhandled)");
var s = default(string);
var c = s.Length;
var c = s!.Length;
}
}

#if !ANDROID
static void Main(string[] args)
{
RunTest(args);
}
#endif
}
17 changes: 16 additions & 1 deletion tests/fixtures/dotnet_signal/test_dotnet.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<TargetFrameworks>net10.0</TargetFrameworks>
<TargetFrameworks Condition="'$(ANDROID_API)' != ''">$(TargetFrameworks);net10.0-android</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup Condition="$(TargetFramework.Contains('-android'))">
<ApplicationId>io.sentry.ndk.dotnet.signal.test</ApplicationId>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<EmbedAssembliesIntoApk>true</EmbedAssembliesIntoApk>
</PropertyGroup>

<ItemGroup Condition="!$(TargetFramework.Contains('-android'))">
<Compile Remove="Platforms\Android\**" />
</ItemGroup>

<ItemGroup Condition="$(TargetFramework.Contains('-android'))">
<AndroidNativeLibrary Include="native\**\*.so" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions tests/test_build_static.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import sys
import os
import pytest
from .conditions import has_breakpad, has_crashpad, has_native
from .conditions import has_breakpad, has_crashpad, has_native, is_android


def test_static_lib(cmake):
Expand All @@ -16,7 +16,7 @@ def test_static_lib(cmake):
)

# on linux we can use `ldd` to check that we don’t link to `libsentry.so`
if sys.platform == "linux":
if sys.platform == "linux" and not is_android:
output = subprocess.check_output("ldd sentry_example", cwd=tmp_path, shell=True)
assert b"libsentry.so" not in output

Expand Down
Loading
Loading