Skip to content
Merged
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
46bbbfe
WIP: iOS integration test
jpnurmi Sep 24, 2025
43b57c7
Split common.ps1 vs. setup.ps1
jpnurmi Sep 24, 2025
d79436c
Fix empty --device argument
jpnurmi Sep 24, 2025
a916900
Temp speed up
jpnurmi Sep 24, 2025
67fd410
Try BeforeAll
jpnurmi Sep 24, 2025
83c3084
Restore, retry, artifacts
jpnurmi Sep 24, 2025
6cc7696
Add test/Sentry.Maui.Device.IntegrationTestApp
jpnurmi Sep 24, 2025
abdc057
Update solution filter
jpnurmi Sep 24, 2025
7ac73d0
Speed up
jpnurmi Sep 25, 2025
adf7b68
Partial revert
jpnurmi Sep 25, 2025
e190195
Managed vs. Native vs. NullRefenceException
jpnurmi Sep 25, 2025
aff8901
Fixup
jpnurmi Sep 25, 2025
29bef65
Move to integration-test/ios.Tests.ps1
jpnurmi Sep 25, 2025
2c516c2
Fixup
jpnurmi Sep 25, 2025
30b08fd
Use shared Install-XHarness
jpnurmi Sep 25, 2025
6389645
Fix Install-XHarness
jpnurmi Sep 25, 2025
44a190e
Add RunTestApp helper func
jpnurmi Sep 25, 2025
26e6d05
Fix Install-XHarness - attempt 2
jpnurmi Sep 25, 2025
5f6eb95
Prefer booted sims
jpnurmi Sep 25, 2025
28a57ae
Convert tabs to spaces
jpnurmi Sep 25, 2025
ba70ab2
Fix PreferredStates
jpnurmi Sep 25, 2025
64d2827
Fix Install-XHarness
jpnurmi Sep 25, 2025
90cda33
Rename to mobile.Tests.ps1 and prepare iOS context
jpnurmi Sep 25, 2025
c58bd4f
Leave scripts/device-test-utils.ps1 in the sparse checkout for integr…
jpnurmi Sep 25, 2025
5e01b48
Add missing timeout
jpnurmi Sep 25, 2025
8121c4c
Fix Android tfm
jpnurmi Sep 25, 2025
d508359
Prepare predictable Activity name on Android
jpnurmi Sep 25, 2025
76eeddf
Fix path
jpnurmi Sep 25, 2025
4931fcb
Add missing continue-on-error: true
jpnurmi Sep 25, 2025
f438a74
Apply suggestions from code review
jpnurmi Sep 26, 2025
459508a
Move the app to integration-test/mobile-app
jpnurmi Sep 26, 2025
24646df
restore ios 18.5
jpnurmi Sep 26, 2025
9b924e3
MSBUILD : error MSB1009: Project file does not exist.
jpnurmi Sep 26, 2025
ad8f805
.gitignore :x
jpnurmi Sep 26, 2025
6b88514
WIP: add integration tests for Android
jpnurmi Sep 29, 2025
8716532
Run Android integration tests in CI
jpnurmi Sep 30, 2025
2b3bb23
try catch for adb devices
jpnurmi Sep 30, 2025
73274fb
Get-AndroidEmulatorId
jpnurmi Sep 30, 2025
fea7c1e
Split mobile.Tests.ps1 -> ios.Tests.ps1 vs. android.Tests.ps1
jpnurmi Sep 30, 2025
3a4e786
Try checkout ../github-workflows
jpnurmi Sep 30, 2025
1131252
modules/github-workflows
jpnurmi Sep 30, 2025
bab2235
setup env
jpnurmi Sep 30, 2025
88b40b1
checkout submodules
jpnurmi Sep 30, 2025
7c4f0b9
Try selecting Java 17 for integration tests
jpnurmi Sep 30, 2025
8144e87
Clean up & use the new YAML anchors to reduce repeat
jpnurmi Sep 30, 2025
2f2b515
Simplify iOS integration test skip condition
jpnurmi Oct 1, 2025
185dbf2
Simplify and stabilize
jpnurmi Oct 2, 2025
8f3b094
Move test assertions outside output groups
jpnurmi Oct 3, 2025
97062d5
Replace unamge with cross-platform OS arch checks
jpnurmi Oct 3, 2025
e1cc0c5
Replace direct `adb` calls with `xharness android adb`
jpnurmi Oct 3, 2025
7d72059
Pre-install & xharness adb devices
jpnurmi Oct 3, 2025
5ae455d
Don't try to run adb devices in the CI
jpnurmi Oct 3, 2025
7089ea5
Update android.Tests.ps1
jamescrosswell Oct 6, 2025
18c7fc8
Get-AndroidEmulatorId: fix env:ANDROID_SERIAL check
jpnurmi Oct 6, 2025
4e2cf5d
Apply suggestion from @Flash0ver
jpnurmi Oct 6, 2025
c26d952
Merge remote-tracking branch 'upstream/main' into test/ios-integration
jpnurmi Oct 6, 2025
a8c25a7
Try-finally for adb reverse
jpnurmi Oct 7, 2025
fed73a4
Use --remove-all for port-forwarding cleanup
jpnurmi Oct 7, 2025
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
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ jobs:
# integration tests, e.g. Directory.Build.props, nuget.config, ...
sparse-checkout: |
integration-test
scripts
.github

- name: Fetch NuGet Packages
Expand Down
43 changes: 38 additions & 5 deletions .github/workflows/device-tests-android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ jobs:

- name: Checkout
uses: actions/checkout@v5
with:
submodules: recursive

- name: Download test app artifact
uses: actions/download-artifact@v5
Expand All @@ -94,11 +96,11 @@ jobs:
# Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md

- name: Run Tests
id: first-run
id: first-test-run
continue-on-error: true
timeout-minutes: 40
uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # Tag: v2.34.0
with:
with: &android-emulator-test
api-level: ${{ matrix.api-level }}
target: ${{ env.ANDROID_EMULATOR_TARGET }}
force-avd-creation: false
Expand All @@ -110,10 +112,33 @@ jobs:
script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }}

- name: Retry Tests (if previous failed to run)
if: steps.first-run.outcome == 'failure'
if: steps.first-test-run.outcome == 'failure'
timeout-minutes: 40
uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # Tag: v2.34.0
with: *android-emulator-test

- name: Setup Environment
uses: ./.github/actions/environment

- name: Select Java 17
uses: actions/setup-java@dded0888837ed1f317902acf8a20df0ad188d165 # v5.0.0
with:
distribution: ${{ runner.os == 'Windows' && runner.arch == 'ARM64' && 'microsoft' || 'temurin' }}
java-version: '17'

- name: Checkout github-workflows
uses: actions/checkout@v5
with:
repository: getsentry/github-workflows
ref: a5e409bd5bad4c295201cdcfe862b17c50b29ab7 # v2.14.1
path: modules/github-workflows

- name: Run Integration Tests
id: first-integration-test-run
continue-on-error: true
timeout-minutes: 40
uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # v2.34.0
with: &android-emulator-integration-test
api-level: ${{ matrix.api-level }}
target: ${{ env.ANDROID_EMULATOR_TARGET }}
force-avd-creation: false
Expand All @@ -122,11 +147,19 @@ jobs:
disk-size: ${{ env.ANDROID_EMULATOR_DISK_SIZE }}
emulator-options: ${{ env.ANDROID_EMULATOR_OPTIONS }}
disable-animations: false
script: pwsh scripts/device-test.ps1 android -Run -Tfm ${{ matrix.tfm }}
script: pwsh integration-test/android.Tests.ps1

- name: Retry Integration Tests (if previous failed to run)
if: steps.first-integration-test-run.outcome == 'failure'
timeout-minutes: 40
uses: reactivecircus/android-emulator-runner@1dcd0090116d15e7c562f8db72807de5e036a4ed # v2.34.0
with: *android-emulator-integration-test

- name: Upload results
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: device-test-android-${{ matrix.api-level }}-${{ matrix.tfm }}-results
path: test_output
path: |
test_output
integration-test/mobile-app/test_output
21 changes: 18 additions & 3 deletions .github/workflows/device-tests-ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,32 @@ jobs:
run: pwsh ./scripts/device-test.ps1 ios -Build

- name: Run Tests
id: first-run
id: first-test-run
continue-on-error: true
run: pwsh scripts/device-test.ps1 ios -Run

- name: Retry Tests (if previous failed to run)
if: steps.first-run.outcome == 'failure'
if: steps.first-test-run.outcome == 'failure'
run: pwsh scripts/device-test.ps1 ios -Run

- name: Run Integration Tests
id: first-integration-test-run
continue-on-error: true
uses: getsentry/github-workflows/sentry-cli/integration-test/@a5e409bd5bad4c295201cdcfe862b17c50b29ab7 # v2.14.1
with:
path: integration-test/ios.Tests.ps1

- name: Retry Integration Tests (if previous failed to run)
if: steps.first-integration-test-run.outcome == 'failure'
uses: getsentry/github-workflows/sentry-cli/integration-test/@a5e409bd5bad4c295201cdcfe862b17c50b29ab7 # v2.14.1
with:
path: integration-test/ios.Tests.ps1

- name: Upload results
if: success() || failure()
uses: actions/upload-artifact@v4
with:
name: device-test-ios-results
path: test_output
path: |
test_output
integration-test/mobile-app/test_output
145 changes: 145 additions & 0 deletions integration-test/android.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# This file contains test cases for https://pester.dev/
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
. $PSScriptRoot/pester.ps1
. $PSScriptRoot/../scripts/device-test-utils.ps1

BeforeDiscovery {
# Skip Android integration tests unless an emulator has been already started
# by Android Device Tests, or manually when testing locally. This avoids
# slowing down non-Device Test CI builds further.
Install-XHarness
$script:emulator = Get-AndroidEmulatorId
Copy link
Collaborator

Choose a reason for hiding this comment

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

TIL about $script scoped variables in PWSH!

}

Describe 'MAUI app' -ForEach @(
@{ tfm = "net9.0-android35.0" }
) -Skip:(-not $script:emulator) {
BeforeAll {
Remove-Item -Path "$PSScriptRoot/mobile-app" -Recurse -Force -ErrorAction SilentlyContinue
Copy-Item -Path "$PSScriptRoot/net9-maui" -Destination "$PSScriptRoot/mobile-app" -Recurse -Force
Push-Location $PSScriptRoot/mobile-app

function InstallAndroidApp
{
param([string] $Dsn)
$dsn = $Dsn.Replace('http://', 'http://key@') + '/0'

# replace {{SENTRY_DSN}} in MauiProgram.cs
(Get-Content MauiProgram.cs) `
-replace '\{\{SENTRY_DSN\}\}', $dsn `
| Set-Content MauiProgram.cs

$arch = [System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString().ToLower()
$rid = "android-$arch"

Write-Host "::group::Build Sentry.Maui.Device.IntegrationTestApp.csproj"
dotnet build Sentry.Maui.Device.IntegrationTestApp.csproj `
--configuration Release `
--framework $tfm `
--runtime $rid
| ForEach-Object { Write-Host $_ }
Write-Host '::endgroup::'
$LASTEXITCODE | Should -Be 0

Write-Host "::group::Install bin/Release/$tfm/$rid/io.sentry.dotnet.maui.device.integrationtestapp-Signed.apk"
xharness android install -v `
--app bin/Release/$tfm/$rid/io.sentry.dotnet.maui.device.integrationtestapp-Signed.apk `
--package-name io.sentry.dotnet.maui.device.integrationtestapp `
--output-directory=test_output
| ForEach-Object { Write-Host $_ }
Write-Host '::endgroup::'
$LASTEXITCODE | Should -Be 0
}

function RunAndroidApp
{
param(
[string] $Dsn,
[string] $TestArg = 'None'
)

# Setup port forwarding for accessing sentry-server at 127.0.0.1:8000 from the emulator
$port = $Dsn.Split(':')[2].Split('/')[0]
xharness android adb -v -- reverse tcp:$port tcp:$port
Copy link
Collaborator

Choose a reason for hiding this comment

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

🥇 Nice!


Write-Host "::group::Run Android app (TestArg=$TestArg)"
xharness android adb -v `
-- shell am start -S -n io.sentry.dotnet.maui.device.integrationtestapp/.MainActivity `
-e SENTRY_TEST_ARG $TestArg
| ForEach-Object { Write-Host $_ }
Write-Host '::endgroup::'
$LASTEXITCODE | Should -Be 0

do
{
Write-Host "Waiting for app..."
Start-Sleep -Seconds 1

$procid = (& xharness android adb -- shell pidof "io.sentry.dotnet.maui.device.integrationtestapp") -replace '\s', ''
$activity = (& xharness android adb -- shell dumpsys activity activities) -match "io\.sentry\.dotnet\.maui\.device\.integrationtestapp"

} while ($procid -and $activity)

xharness android adb -v -- reverse --remove tcp:$port
}

function UninstallAndroidApp
{
Write-Host "::group::Uninstall io.sentry.dotnet.maui.device.integrationtestapp"
xharness android uninstall -v `
--package-name io.sentry.dotnet.maui.device.integrationtestapp
| ForEach-Object { Write-Host $_ }
$LASTEXITCODE | Should -Be 0
Write-Host '::endgroup::'
}
}

AfterAll {
Pop-Location
}

AfterEach {
UninstallAndroidApp
}

It 'Managed crash' {
$result = Invoke-SentryServer {
param([string]$url)
InstallAndroidApp -Dsn $url
RunAndroidApp -Dsn $url -TestArg "Managed"
RunAndroidApp -Dsn $url
}

$result.HasErrors() | Should -BeFalse
$result.Envelopes() | Should -AnyElementMatch "`"type`":`"System.ApplicationException`""
$result.Envelopes() | Should -Not -AnyElementMatch "`"type`":`"SIGABRT`""
}

It 'Java crash' {
$result = Invoke-SentryServer {
param([string]$url)
InstallAndroidApp -Dsn $url
RunAndroidApp -Dsn $url -TestArg "Java"
RunAndroidApp -Dsn $url
}

$result.HasErrors() | Should -BeFalse
$result.Envelopes() | Should -AnyElementMatch "`"type`":`"RuntimeException`""
$result.Envelopes() | Should -Not -AnyElementMatch "`"type`":`"System.\w+Exception`""
}

It 'Null reference exception' {
$result = Invoke-SentryServer {
param([string]$url)
InstallAndroidApp -Dsn $url
RunAndroidApp -Dsn $url -TestArg "NullReferenceException"
RunAndroidApp -Dsn $url
}

$result.HasErrors() | Should -BeFalse
$result.Envelopes() | Should -AnyElementMatch "`"type`":`"System.NullReferenceException`""
# TODO: fix redundant RuntimeException (#3954)
{ $result.Envelopes() | Should -Not -AnyElementMatch "`"type`":`"SIGSEGV`"" } | Should -Throw
}
}
52 changes: 1 addition & 51 deletions integration-test/common.ps1
Original file line number Diff line number Diff line change
@@ -1,54 +1,4 @@
# So that this works in VS Code testing integration. Otherwise the script is run within its directory.

# In CI, the module is loaded automatically
if (!(Test-Path env:CI ))
{
Import-Module $PSScriptRoot/../../github-workflows/sentry-cli/integration-test/action.psm1 -Force
}

function ShouldAnyElementMatch ($ActualValue, [string]$ExpectedValue, [switch] $Negate, [string] $Because)
{
<#
.SYNOPSIS
Asserts whether any item in the collection matches the expected value
.EXAMPLE
'foo','bar','foobar' | Should -AnyElementMatch 'oob'

This should pass because 'oob' is a substring of 'foobar'.
#>

$filtered = $ActualValue | Where-Object { $_ -match $ExpectedValue }
[bool] $succeeded = @($filtered).Count -gt 0
if ($Negate) { $succeeded = -not $succeeded }

if (-not $succeeded)
{
if ($Negate)
{
$failureMessage = "Expected string '$ExpectedValue' to match no elements in collection @($($ActualValue -join ', '))$(if($Because) { " because $Because"})."
}
else
{
$failureMessage = "Expected string '$ExpectedValue' to match any element in collection @($($ActualValue -join ', '))$(if($Because) { " because $Because"})."
}
}
else
{
$failureMessage = $null
}

return [pscustomobject]@{
Succeeded = $succeeded
FailureMessage = $failureMessage
}
}

BeforeDiscovery {
Add-ShouldOperator -Name AnyElementMatch `
-InternalName 'ShouldAnyElementMatch' `
-Test ${function:ShouldAnyElementMatch} `
-SupportsArrayInput
}
. $PSScriptRoot/pester.ps1

AfterAll {
Pop-Location
Expand Down
Loading
Loading