From ebef31720a2e6b9ad332f33c0e6b6697c7ea2994 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 11 Feb 2026 20:00:22 +0200 Subject: [PATCH 1/6] Add integration tests for metrics --- integration-test/CMakeLists.txt | 2 +- .../Integration.Android.Tests.ps1 | 71 ++++++++++++++++ .../Integration.Desktop.Tests.ps1 | 83 +++++++++++++++++++ .../SentryPlaygroundGameInstance.cpp | 26 ++++++ .../SentryPlaygroundGameInstance.h | 1 + 5 files changed, 182 insertions(+), 1 deletion(-) diff --git a/integration-test/CMakeLists.txt b/integration-test/CMakeLists.txt index e19c70aaf..15c7b43d0 100644 --- a/integration-test/CMakeLists.txt +++ b/integration-test/CMakeLists.txt @@ -7,7 +7,7 @@ include(FetchContent) FetchContent_Declare( app-runner GIT_REPOSITORY https://github.com/getsentry/app-runner.git - GIT_TAG 34ca65885de1ff976a797acdf319459cc60254fd + GIT_TAG cbda853bdddb956a076a4e5dc8394ee5db23c105 ) FetchContent_MakeAvailable(app-runner) diff --git a/integration-test/Integration.Android.Tests.ps1 b/integration-test/Integration.Android.Tests.ps1 index 9bcff26c5..c9868ba8a 100644 --- a/integration-test/Integration.Android.Tests.ps1 +++ b/integration-test/Integration.Android.Tests.ps1 @@ -167,6 +167,16 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa $global:AndroidLogResult = Invoke-DeviceApp -ExecutablePath $script:ActivityName -Arguments $logIntentArgs Write-Host "Log test exit code: $($global:AndroidLogResult.ExitCode)" -ForegroundColor Cyan + + # ========================================== + # RUN 4: Metric test - captures custom metric + # ========================================== + + Write-Host "Running metric-capture test on $Platform..." -ForegroundColor Yellow + $metricIntentArgs = "-e cmdline '-metric-capture -ini:Engine:[/Script/Sentry.SentrySettings]:EnableMetrics=True'" + $global:AndroidMetricResult = Invoke-DeviceApp -ExecutablePath $script:ActivityName -Arguments $metricIntentArgs + + Write-Host "Metric test exit code: $($global:AndroidMetricResult.ExitCode)" -ForegroundColor Cyan } AfterAll { @@ -378,6 +388,67 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa $log.'test_id' | Should -Be $script:TestId } } + + Context "Metrics Capture Tests" { + BeforeAll { + $script:MetricResult = $global:AndroidMetricResult + $script:CapturedMetrics = @() + $script:TestId = $null + + # Parse test ID from output (format: METRIC_TRIGGERED: ) + $metricTriggeredLines = @($script:MetricResult.Output | Where-Object { $_ -match 'METRIC_TRIGGERED: ' }) + if ($metricTriggeredLines.Count -gt 0) { + $script:TestId = ($metricTriggeredLines[0] -split 'METRIC_TRIGGERED: ')[-1].Trim() + Write-Host "Captured Test ID: $($script:TestId)" -ForegroundColor Cyan + + # Fetch metrics from Sentry with automatic polling + try { + $script:CapturedMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId + } + catch { + Write-Host "Warning: $_" -ForegroundColor Red + } + } + else { + Write-Host "Warning: No METRIC_TRIGGERED line found in output" -ForegroundColor Yellow + } + } + + It "Should output METRIC_TRIGGERED with test ID" { + $script:TestId | Should -Not -BeNullOrEmpty + } + + It "Should output TEST_RESULT with success" { + $testResultLine = $script:MetricResult.Output | Where-Object { $_ -match 'TEST_RESULT:' } + $testResultLine | Should -Not -BeNullOrEmpty + $testResultLine | Should -Match '"success"\s*:\s*true' + } + + It "Should capture metric in Sentry" { + $script:CapturedMetrics | Should -Not -BeNullOrEmpty + $script:CapturedMetrics.Count | Should -BeGreaterThan 0 + } + + It "Should have correct metric name" { + $metric = $script:CapturedMetrics[0] + $metric.'metric.name' | Should -Be 'test.integration.counter' + } + + It "Should have correct metric type" { + $metric = $script:CapturedMetrics[0] + $metric.'metric.type' | Should -Be 'counter' + } + + It "Should have correct metric value" { + $metric = $script:CapturedMetrics[0] + $metric.value | Should -Be 1.0 + } + + It "Should have test_id attribute matching captured ID" { + $metric = $script:CapturedMetrics[0] + $metric.test_id | Should -Be $script:TestId + } + } } AfterAll { diff --git a/integration-test/Integration.Desktop.Tests.ps1 b/integration-test/Integration.Desktop.Tests.ps1 index b2b9e9f7d..cd7f1c58d 100644 --- a/integration-test/Integration.Desktop.Tests.ps1 +++ b/integration-test/Integration.Desktop.Tests.ps1 @@ -394,6 +394,89 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa $log.test_id | Should -Be $script:TestId } } + + Context "Metrics Capture Tests" { + BeforeAll { + $script:MetricResult = $null + $script:CapturedMetrics = @() + $script:TestId = $null + + Write-Host "Running metrics capture test..." -ForegroundColor Yellow + + $appArgs = @( + '-nullrhi', # Runs without graphics rendering (headless mode) + '-unattended', # Disables user prompts and interactive dialogs + '-stdout', # Ensures logs are written to stdout on Linux/Unix systems + '-nosplash' # Prevents splash screen and dialogs + ) + + # Override default project settings + $appArgs += "-ini:Engine:[/Script/Sentry.SentrySettings]:Dsn=$script:DSN" + $appArgs += "-ini:Engine:[/Script/Sentry.SentrySettings]:EnableMetrics=True" + + # -metric-capture triggers integration test metric scenario in the sample app + $script:MetricResult = Invoke-DeviceApp -ExecutablePath $script:AppPath -Arguments ((@('-metric-capture') + $appArgs) -join ' ') + + Write-Host "Metric test executed. Exit code: $($script:MetricResult.ExitCode)" -ForegroundColor Cyan + + # Parse test ID from output (format: METRIC_TRIGGERED: ) + $metricTriggeredLines = @($script:MetricResult.Output | Where-Object { $_ -match 'METRIC_TRIGGERED: ' }) + if ($metricTriggeredLines.Count -gt 0) { + $script:TestId = ($metricTriggeredLines[0] -split 'METRIC_TRIGGERED: ')[-1].Trim() + Write-Host "Captured Test ID: $($script:TestId)" -ForegroundColor Cyan + + # Fetch metrics from Sentry with automatic polling + try { + $script:CapturedMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId + } + catch { + Write-Host "Warning: $_" -ForegroundColor Red + } + } + else { + Write-Host "Warning: No METRIC_TRIGGERED line found in output" -ForegroundColor Yellow + } + } + + It "Should exit cleanly" { + $script:MetricResult.ExitCode | Should -Be 0 + } + + It "Should output METRIC_TRIGGERED with test ID" { + $script:TestId | Should -Not -BeNullOrEmpty + } + + It "Should output TEST_RESULT with success" { + $testResultLine = $script:MetricResult.Output | Where-Object { $_ -match 'TEST_RESULT:' } + $testResultLine | Should -Not -BeNullOrEmpty + $testResultLine | Should -Match '"success"\s*:\s*true' + } + + It "Should capture metric in Sentry" { + $script:CapturedMetrics | Should -Not -BeNullOrEmpty + $script:CapturedMetrics.Count | Should -BeGreaterThan 0 + } + + It "Should have correct metric name" { + $metric = $script:CapturedMetrics[0] + $metric.'metric.name' | Should -Be 'test.integration.counter' + } + + It "Should have correct metric type" { + $metric = $script:CapturedMetrics[0] + $metric.'metric.type' | Should -Be 'counter' + } + + It "Should have correct metric value" { + $metric = $script:CapturedMetrics[0] + $metric.value | Should -Be 1.0 + } + + It "Should have test_id attribute matching captured ID" { + $metric = $script:CapturedMetrics[0] + $metric.test_id | Should -Be $script:TestId + } + } } AfterAll { diff --git a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp index 8d333c9d3..92112a60e 100644 --- a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp +++ b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp @@ -29,6 +29,7 @@ void USentryPlaygroundGameInstance::Init() FParse::Param(*CommandLine, TEXT("crash-memory-corruption")) || FParse::Param(*CommandLine, TEXT("message-capture")) || FParse::Param(*CommandLine, TEXT("log-capture")) || + FParse::Param(*CommandLine, TEXT("metric-capture")) || FParse::Param(*CommandLine, TEXT("init-only"))) { RunIntegrationTest(CommandLine); @@ -80,6 +81,10 @@ void USentryPlaygroundGameInstance::RunIntegrationTest(const FString& CommandLin { RunLogTest(); } + else if (FParse::Param(*CommandLine, TEXT("metric-capture"))) + { + RunMetricTest(); + } else if (FParse::Param(*CommandLine, TEXT("init-only"))) { RunInitOnly(); @@ -157,6 +162,27 @@ void USentryPlaygroundGameInstance::RunLogTest() CompleteTestWithResult(TEXT("log-capture"), true, TEXT("Test complete")); } +void USentryPlaygroundGameInstance::RunMetricTest() +{ + USentrySubsystem* SentrySubsystem = GEngine->GetEngineSubsystem(); + + FString TestId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); + + TMap Attributes; + Attributes.Add(TEXT("test_id"), FSentryVariant(TestId)); + + SentrySubsystem->AddCountWithAttributes(TEXT("test.integration.counter"), 1, Attributes); + + UE_LOG(LogSentrySample, Display, TEXT("METRIC_TRIGGERED: %s\n"), *TestId); + + // Ensure events were flushed + SentrySubsystem->Close(); + + FPlatformProcess::Sleep(1.0f); + + CompleteTestWithResult(TEXT("metric-capture"), true, TEXT("Test complete")); +} + void USentryPlaygroundGameInstance::RunInitOnly() { USentrySubsystem* SentrySubsystem = GEngine->GetEngineSubsystem(); diff --git a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.h b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.h index d633321b3..c9fa34fe3 100644 --- a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.h +++ b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.h @@ -24,6 +24,7 @@ class SENTRYPLAYGROUND_API USentryPlaygroundGameInstance : public UGameInstance void RunCrashTest(ESentryAppTerminationType CrashType); void RunMessageTest(); void RunLogTest(); + void RunMetricTest(); void RunInitOnly(); void ConfigureTestContext(); From 061a92bc5d70acb66bcc226665f941d3e940004f Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 11 Feb 2026 20:01:30 +0200 Subject: [PATCH 2/6] Skip metrics tests on Mac --- integration-test/Integration.Desktop.Tests.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/integration-test/Integration.Desktop.Tests.ps1 b/integration-test/Integration.Desktop.Tests.ps1 index cd7f1c58d..5bed86e8b 100644 --- a/integration-test/Integration.Desktop.Tests.ps1 +++ b/integration-test/Integration.Desktop.Tests.ps1 @@ -395,7 +395,8 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa } } - Context "Metrics Capture Tests" { + # Metrics are not supported on Apple platforms (macOS/iOS) + Context "Metrics Capture Tests" -Skip:$IsMacOS { BeforeAll { $script:MetricResult = $null $script:CapturedMetrics = @() From 31738abebd4f6393352083fc6c8716c0d437cc39 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 11 Feb 2026 20:52:34 +0200 Subject: [PATCH 3/6] Add gauge and distribution checks --- .../Integration.Android.Tests.ps1 | 91 +++++++++++++++---- .../Integration.Desktop.Tests.ps1 | 90 ++++++++++++++---- .../CppBeforeMetricHandler.cpp | 13 +++ .../SentryPlayground/CppBeforeMetricHandler.h | 18 ++++ .../SentryPlaygroundGameInstance.cpp | 18 +++- 5 files changed, 194 insertions(+), 36 deletions(-) create mode 100644 sample/Source/SentryPlayground/CppBeforeMetricHandler.cpp create mode 100644 sample/Source/SentryPlayground/CppBeforeMetricHandler.h diff --git a/integration-test/Integration.Android.Tests.ps1 b/integration-test/Integration.Android.Tests.ps1 index c9868ba8a..979e33bc0 100644 --- a/integration-test/Integration.Android.Tests.ps1 +++ b/integration-test/Integration.Android.Tests.ps1 @@ -173,7 +173,7 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa # ========================================== Write-Host "Running metric-capture test on $Platform..." -ForegroundColor Yellow - $metricIntentArgs = "-e cmdline '-metric-capture -ini:Engine:[/Script/Sentry.SentrySettings]:EnableMetrics=True'" + $metricIntentArgs = "-e cmdline '-metric-capture -ini:Engine:[/Script/Sentry.SentrySettings]:EnableMetrics=True -ini:Engine:[/Script/Sentry.SentrySettings]:BeforeMetricHandler=/Script/SentryPlayground.CppBeforeMetricHandler'" $global:AndroidMetricResult = Invoke-DeviceApp -ExecutablePath $script:ActivityName -Arguments $metricIntentArgs Write-Host "Metric test exit code: $($global:AndroidMetricResult.ExitCode)" -ForegroundColor Cyan @@ -392,7 +392,9 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa Context "Metrics Capture Tests" { BeforeAll { $script:MetricResult = $global:AndroidMetricResult - $script:CapturedMetrics = @() + $script:CapturedCounterMetrics = @() + $script:CapturedDistributionMetrics = @() + $script:CapturedGaugeMetrics = @() $script:TestId = $null # Parse test ID from output (format: METRIC_TRIGGERED: ) @@ -401,12 +403,28 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa $script:TestId = ($metricTriggeredLines[0] -split 'METRIC_TRIGGERED: ')[-1].Trim() Write-Host "Captured Test ID: $($script:TestId)" -ForegroundColor Cyan - # Fetch metrics from Sentry with automatic polling + # Fetch all three metric types from Sentry with automatic polling + $metricFields = @('handler_added', 'to_be_removed') + try { - $script:CapturedMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId + $script:CapturedCounterMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields } catch { - Write-Host "Warning: $_" -ForegroundColor Red + Write-Host "Warning (counter): $_" -ForegroundColor Red + } + + try { + $script:CapturedDistributionMetrics = Get-SentryTestMetric -MetricName 'test.integration.distribution' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields + } + catch { + Write-Host "Warning (distribution): $_" -ForegroundColor Red + } + + try { + $script:CapturedGaugeMetrics = Get-SentryTestMetric -MetricName 'test.integration.gauge' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields + } + catch { + Write-Host "Warning (gauge): $_" -ForegroundColor Red } } else { @@ -424,28 +442,67 @@ Describe 'Sentry Unreal Android Integration Tests ()' -ForEach $TestTa $testResultLine | Should -Match '"success"\s*:\s*true' } - It "Should capture metric in Sentry" { - $script:CapturedMetrics | Should -Not -BeNullOrEmpty - $script:CapturedMetrics.Count | Should -BeGreaterThan 0 + # Counter metric assertions + It "Should capture counter metric in Sentry" { + $script:CapturedCounterMetrics | Should -Not -BeNullOrEmpty } - It "Should have correct metric name" { - $metric = $script:CapturedMetrics[0] + It "Should have correct counter metric name and type" { + $metric = $script:CapturedCounterMetrics[0] $metric.'metric.name' | Should -Be 'test.integration.counter' - } - - It "Should have correct metric type" { - $metric = $script:CapturedMetrics[0] $metric.'metric.type' | Should -Be 'counter' } - It "Should have correct metric value" { - $metric = $script:CapturedMetrics[0] + It "Should have correct counter metric value" { + $metric = $script:CapturedCounterMetrics[0] $metric.value | Should -Be 1.0 } + # Distribution metric assertions + It "Should capture distribution metric in Sentry" { + $script:CapturedDistributionMetrics | Should -Not -BeNullOrEmpty + } + + It "Should have correct distribution metric name and type" { + $metric = $script:CapturedDistributionMetrics[0] + $metric.'metric.name' | Should -Be 'test.integration.distribution' + $metric.'metric.type' | Should -Be 'distribution' + } + + It "Should have correct distribution metric value" { + $metric = $script:CapturedDistributionMetrics[0] + $metric.value | Should -Be 42.5 + } + + # Gauge metric assertions + It "Should capture gauge metric in Sentry" { + $script:CapturedGaugeMetrics | Should -Not -BeNullOrEmpty + } + + It "Should have correct gauge metric name and type" { + $metric = $script:CapturedGaugeMetrics[0] + $metric.'metric.name' | Should -Be 'test.integration.gauge' + $metric.'metric.type' | Should -Be 'gauge' + } + + It "Should have correct gauge metric value" { + $metric = $script:CapturedGaugeMetrics[0] + $metric.value | Should -Be 15.0 + } + + # BeforeMetricHandler attribute assertions (verified on counter, applies to all) + It "Should have attribute added by BeforeMetricHandler" { + $metric = $script:CapturedCounterMetrics[0] + $metric.'handler_added' | Should -Be 'added_value' + } + + It "Should not have attribute removed by BeforeMetricHandler" { + $metric = $script:CapturedCounterMetrics[0] + $metric.'to_be_removed' | Should -BeNullOrEmpty + } + It "Should have test_id attribute matching captured ID" { - $metric = $script:CapturedMetrics[0] + $metric = $script:CapturedCounterMetrics[0] $metric.test_id | Should -Be $script:TestId } } diff --git a/integration-test/Integration.Desktop.Tests.ps1 b/integration-test/Integration.Desktop.Tests.ps1 index 5bed86e8b..9acab68f3 100644 --- a/integration-test/Integration.Desktop.Tests.ps1 +++ b/integration-test/Integration.Desktop.Tests.ps1 @@ -399,7 +399,9 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa Context "Metrics Capture Tests" -Skip:$IsMacOS { BeforeAll { $script:MetricResult = $null - $script:CapturedMetrics = @() + $script:CapturedCounterMetrics = @() + $script:CapturedDistributionMetrics = @() + $script:CapturedGaugeMetrics = @() $script:TestId = $null Write-Host "Running metrics capture test..." -ForegroundColor Yellow @@ -414,6 +416,7 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa # Override default project settings $appArgs += "-ini:Engine:[/Script/Sentry.SentrySettings]:Dsn=$script:DSN" $appArgs += "-ini:Engine:[/Script/Sentry.SentrySettings]:EnableMetrics=True" + $appArgs += "-ini:Engine:[/Script/Sentry.SentrySettings]:BeforeMetricHandler=/Script/SentryPlayground.CppBeforeMetricHandler" # -metric-capture triggers integration test metric scenario in the sample app $script:MetricResult = Invoke-DeviceApp -ExecutablePath $script:AppPath -Arguments ((@('-metric-capture') + $appArgs) -join ' ') @@ -426,12 +429,28 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa $script:TestId = ($metricTriggeredLines[0] -split 'METRIC_TRIGGERED: ')[-1].Trim() Write-Host "Captured Test ID: $($script:TestId)" -ForegroundColor Cyan - # Fetch metrics from Sentry with automatic polling + # Fetch all three metric types from Sentry with automatic polling + $metricFields = @('handler_added', 'to_be_removed') + try { - $script:CapturedMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId + $script:CapturedCounterMetrics = Get-SentryTestMetric -MetricName 'test.integration.counter' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields } catch { - Write-Host "Warning: $_" -ForegroundColor Red + Write-Host "Warning (counter): $_" -ForegroundColor Red + } + + try { + $script:CapturedDistributionMetrics = Get-SentryTestMetric -MetricName 'test.integration.distribution' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields + } + catch { + Write-Host "Warning (distribution): $_" -ForegroundColor Red + } + + try { + $script:CapturedGaugeMetrics = Get-SentryTestMetric -MetricName 'test.integration.gauge' -AttributeName 'test_id' -AttributeValue $script:TestId -Fields $metricFields + } + catch { + Write-Host "Warning (gauge): $_" -ForegroundColor Red } } else { @@ -453,28 +472,67 @@ Describe "Sentry Unreal Desktop Integration Tests ()" -ForEach $TestTa $testResultLine | Should -Match '"success"\s*:\s*true' } - It "Should capture metric in Sentry" { - $script:CapturedMetrics | Should -Not -BeNullOrEmpty - $script:CapturedMetrics.Count | Should -BeGreaterThan 0 + # Counter metric assertions + It "Should capture counter metric in Sentry" { + $script:CapturedCounterMetrics | Should -Not -BeNullOrEmpty } - It "Should have correct metric name" { - $metric = $script:CapturedMetrics[0] + It "Should have correct counter metric name and type" { + $metric = $script:CapturedCounterMetrics[0] $metric.'metric.name' | Should -Be 'test.integration.counter' - } - - It "Should have correct metric type" { - $metric = $script:CapturedMetrics[0] $metric.'metric.type' | Should -Be 'counter' } - It "Should have correct metric value" { - $metric = $script:CapturedMetrics[0] + It "Should have correct counter metric value" { + $metric = $script:CapturedCounterMetrics[0] $metric.value | Should -Be 1.0 } + # Distribution metric assertions + It "Should capture distribution metric in Sentry" { + $script:CapturedDistributionMetrics | Should -Not -BeNullOrEmpty + } + + It "Should have correct distribution metric name and type" { + $metric = $script:CapturedDistributionMetrics[0] + $metric.'metric.name' | Should -Be 'test.integration.distribution' + $metric.'metric.type' | Should -Be 'distribution' + } + + It "Should have correct distribution metric value" { + $metric = $script:CapturedDistributionMetrics[0] + $metric.value | Should -Be 42.5 + } + + # Gauge metric assertions + It "Should capture gauge metric in Sentry" { + $script:CapturedGaugeMetrics | Should -Not -BeNullOrEmpty + } + + It "Should have correct gauge metric name and type" { + $metric = $script:CapturedGaugeMetrics[0] + $metric.'metric.name' | Should -Be 'test.integration.gauge' + $metric.'metric.type' | Should -Be 'gauge' + } + + It "Should have correct gauge metric value" { + $metric = $script:CapturedGaugeMetrics[0] + $metric.value | Should -Be 15.0 + } + + # BeforeMetricHandler attribute assertions (verified on counter, applies to all) + It "Should have attribute added by BeforeMetricHandler" { + $metric = $script:CapturedCounterMetrics[0] + $metric.'handler_added' | Should -Be 'added_value' + } + + It "Should not have attribute removed by BeforeMetricHandler" { + $metric = $script:CapturedCounterMetrics[0] + $metric.'to_be_removed' | Should -BeNullOrEmpty + } + It "Should have test_id attribute matching captured ID" { - $metric = $script:CapturedMetrics[0] + $metric = $script:CapturedCounterMetrics[0] $metric.test_id | Should -Be $script:TestId } } diff --git a/sample/Source/SentryPlayground/CppBeforeMetricHandler.cpp b/sample/Source/SentryPlayground/CppBeforeMetricHandler.cpp new file mode 100644 index 000000000..c795af3dd --- /dev/null +++ b/sample/Source/SentryPlayground/CppBeforeMetricHandler.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#include "CppBeforeMetricHandler.h" + +#include "SentryMetric.h" + +USentryMetric* UCppBeforeMetricHandler::HandleBeforeMetric_Implementation(USentryMetric* Metric) +{ + Metric->SetAttribute(TEXT("handler_added"), FSentryVariant(TEXT("added_value"))); + Metric->RemoveAttribute(TEXT("to_be_removed")); + + return Super::HandleBeforeMetric_Implementation(Metric); +} diff --git a/sample/Source/SentryPlayground/CppBeforeMetricHandler.h b/sample/Source/SentryPlayground/CppBeforeMetricHandler.h new file mode 100644 index 000000000..be9d61f6a --- /dev/null +++ b/sample/Source/SentryPlayground/CppBeforeMetricHandler.h @@ -0,0 +1,18 @@ +// Copyright (c) 2025 Sentry. All Rights Reserved. + +#pragma once + +#include "CoreMinimal.h" + +#include "SentryBeforeMetricHandler.h" + +#include "CppBeforeMetricHandler.generated.h" + +UCLASS() +class SENTRYPLAYGROUND_API UCppBeforeMetricHandler : public USentryBeforeMetricHandler +{ + GENERATED_BODY() + +public: + virtual USentryMetric* HandleBeforeMetric_Implementation(USentryMetric* Metric) override; +}; diff --git a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp index 92112a60e..da3a95990 100644 --- a/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp +++ b/sample/Source/SentryPlayground/SentryPlaygroundGameInstance.cpp @@ -7,6 +7,7 @@ #include "SentrySettings.h" #include "SentryPlaygroundUtils.h" #include "SentryUser.h" +#include "SentryUnit.h" #include "CoreGlobals.h" #include "HAL/Platform.h" @@ -168,10 +169,21 @@ void USentryPlaygroundGameInstance::RunMetricTest() FString TestId = FGuid::NewGuid().ToString(EGuidFormats::DigitsWithHyphens); - TMap Attributes; - Attributes.Add(TEXT("test_id"), FSentryVariant(TestId)); + TMap CounterAttributes; + CounterAttributes.Add(TEXT("test_id"), FSentryVariant(TestId)); + CounterAttributes.Add(TEXT("to_be_removed"), FSentryVariant(TEXT("original_value"))); + + TMap DistributionAttributes; + DistributionAttributes.Add(TEXT("test_id"), FSentryVariant(TestId)); + DistributionAttributes.Add(TEXT("to_be_removed"), FSentryVariant(TEXT("original_value"))); + + TMap GaugeAttributes; + GaugeAttributes.Add(TEXT("test_id"), FSentryVariant(TestId)); + GaugeAttributes.Add(TEXT("to_be_removed"), FSentryVariant(TEXT("original_value"))); - SentrySubsystem->AddCountWithAttributes(TEXT("test.integration.counter"), 1, Attributes); + SentrySubsystem->AddCountWithAttributes(TEXT("test.integration.counter"), 1, CounterAttributes); + SentrySubsystem->AddDistributionWithAttributes(TEXT("test.integration.distribution"), 42.5f, FSentryUnit(ESentryUnit::Millisecond), DistributionAttributes); + SentrySubsystem->AddGaugeWithAttributes(TEXT("test.integration.gauge"), 15.0f, FSentryUnit(ESentryUnit::Byte), GaugeAttributes); UE_LOG(LogSentrySample, Display, TEXT("METRIC_TRIGGERED: %s\n"), *TestId); From 5bd9d0533ddf377f60d94c9e98c1dd01b332c156 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 11 Feb 2026 20:53:18 +0200 Subject: [PATCH 4/6] Bump app-runner --- integration-test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-test/CMakeLists.txt b/integration-test/CMakeLists.txt index 15c7b43d0..8f0924982 100644 --- a/integration-test/CMakeLists.txt +++ b/integration-test/CMakeLists.txt @@ -7,7 +7,7 @@ include(FetchContent) FetchContent_Declare( app-runner GIT_REPOSITORY https://github.com/getsentry/app-runner.git - GIT_TAG cbda853bdddb956a076a4e5dc8394ee5db23c105 + GIT_TAG c2863719c1a32e0b401f6ed745b666f34cb3d675 ) FetchContent_MakeAvailable(app-runner) From f66e73b251796824be4822b0bdf73d6dc4d790f8 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Wed, 11 Feb 2026 21:52:23 +0200 Subject: [PATCH 5/6] Clean up unit tests --- .../Tests/SentryBeforeMetricHandler.spec.cpp | 57 ------------------- .../Tests/SentryTestBeforeMetricHandler.h | 22 ------- scripts/packaging/package.snapshot | 2 - 3 files changed, 81 deletions(-) delete mode 100644 plugin-dev/Source/Sentry/Private/Tests/SentryBeforeMetricHandler.spec.cpp delete mode 100644 plugin-dev/Source/Sentry/Private/Tests/SentryTestBeforeMetricHandler.h diff --git a/plugin-dev/Source/Sentry/Private/Tests/SentryBeforeMetricHandler.spec.cpp b/plugin-dev/Source/Sentry/Private/Tests/SentryBeforeMetricHandler.spec.cpp deleted file mode 100644 index 3b51ae8be..000000000 --- a/plugin-dev/Source/Sentry/Private/Tests/SentryBeforeMetricHandler.spec.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#include "SentryBeforeMetricHandler.h" -#include "Engine/Engine.h" -#include "SentryMetric.h" -#include "SentrySettings.h" -#include "SentrySubsystem.h" -#include "SentryTestBeforeMetricHandler.h" -#include "SentryTests.h" - -#include "Misc/AutomationTest.h" - -#include "HAL/PlatformSentryMetric.h" - -TDelegate UTestBeforeMetricHandler::OnTestBeforeMetricHandler; - -#if WITH_AUTOMATION_TESTS && (PLATFORM_ANDROID || USE_SENTRY_NATIVE) - -BEGIN_DEFINE_SPEC(SentryBeforeMetricHandlerSpec, "Sentry.SentryBeforeMetricHandler", EAutomationTestFlags::ProductFilter | SentryApplicationContextMask) -END_DEFINE_SPEC(SentryBeforeMetricHandlerSpec) - -void SentryBeforeMetricHandlerSpec::Define() -{ - Describe("BeforeMetricHandler functionality", [this]() - { - It("should be called when metric is emitted", [this]() - { - USentrySubsystem* SentrySubsystem = GEngine->GetEngineSubsystem(); - - bool bHandlerCalled = false; - const FString TestKey = TEXT("test.counter"); - const int32 TestValue = 5; - - SentrySubsystem->InitializeWithSettings(FConfigureSettingsNativeDelegate::CreateLambda([=](USentrySettings* Settings) - { - Settings->EnableMetrics = true; - Settings->BeforeMetricHandler = UTestBeforeMetricHandler::StaticClass(); - })); - - UTestBeforeMetricHandler::OnTestBeforeMetricHandler.BindLambda([this, &bHandlerCalled, TestKey](USentryMetric* MetricData) - { - bHandlerCalled = true; - TestEqual("Handler received correct name", MetricData->GetName(), TestKey); - TestEqual("Handler received correct type", MetricData->GetType(), ESentryMetricType::Counter); - }); - - SentrySubsystem->AddCount(TestKey, TestValue); - - TestTrue("BeforeMetricHandler should be called", bHandlerCalled); - - UTestBeforeMetricHandler::OnTestBeforeMetricHandler.Unbind(); - SentrySubsystem->Close(); - }); - }); -} - -#endif diff --git a/plugin-dev/Source/Sentry/Private/Tests/SentryTestBeforeMetricHandler.h b/plugin-dev/Source/Sentry/Private/Tests/SentryTestBeforeMetricHandler.h deleted file mode 100644 index 8de3a8e15..000000000 --- a/plugin-dev/Source/Sentry/Private/Tests/SentryTestBeforeMetricHandler.h +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2025 Sentry. All Rights Reserved. - -#pragma once - -#include "SentryBeforeMetricHandler.h" -#include "SentryMetric.h" - -#include "SentryTestBeforeMetricHandler.generated.h" - -UCLASS() -class UTestBeforeMetricHandler : public USentryBeforeMetricHandler -{ - GENERATED_BODY() -public: - virtual USentryMetric* HandleBeforeMetric_Implementation(USentryMetric* Metric) override - { - OnTestBeforeMetricHandler.ExecuteIfBound(Metric); - return Super::HandleBeforeMetric_Implementation(Metric); - } - - static TDelegate OnTestBeforeMetricHandler; -}; diff --git a/scripts/packaging/package.snapshot b/scripts/packaging/package.snapshot index d34ade762..de0c63ce5 100644 --- a/scripts/packaging/package.snapshot +++ b/scripts/packaging/package.snapshot @@ -209,7 +209,6 @@ Source/Sentry/Private/SentryUnit.cpp Source/Sentry/Private/SentryUser.cpp Source/Sentry/Private/SentryVariant.cpp Source/Sentry/Private/Tests/SentryBeforeLogHandler.spec.cpp -Source/Sentry/Private/Tests/SentryBeforeMetricHandler.spec.cpp Source/Sentry/Private/Tests/SentryBreadcrumb.spec.cpp Source/Sentry/Private/Tests/SentryEvent.spec.cpp Source/Sentry/Private/Tests/SentryFeedback.spec.cpp @@ -220,7 +219,6 @@ Source/Sentry/Private/Tests/SentryScope.spec.cpp Source/Sentry/Private/Tests/SentryScopeBeforeSendHandler.h Source/Sentry/Private/Tests/SentrySubsystem.spec.cpp Source/Sentry/Private/Tests/SentryTestBeforeLogHandler.h -Source/Sentry/Private/Tests/SentryTestBeforeMetricHandler.h Source/Sentry/Private/Tests/SentryTests.h Source/Sentry/Private/Tests/SentryTraceSampling.spec.cpp Source/Sentry/Private/Tests/SentryTraceSamplingHandler.h From aa78570a3c599190eb8170c4c7082ac2de481f77 Mon Sep 17 00:00:00 2001 From: Ivan Tustanivskyi Date: Thu, 12 Feb 2026 06:54:49 +0200 Subject: [PATCH 6/6] Bump app-runner --- integration-test/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration-test/CMakeLists.txt b/integration-test/CMakeLists.txt index 8f0924982..ec5764cf8 100644 --- a/integration-test/CMakeLists.txt +++ b/integration-test/CMakeLists.txt @@ -7,7 +7,7 @@ include(FetchContent) FetchContent_Declare( app-runner GIT_REPOSITORY https://github.com/getsentry/app-runner.git - GIT_TAG c2863719c1a32e0b401f6ed745b666f34cb3d675 + GIT_TAG 0f3a63a67aeead62b004e1d1548ffe47dc630fb6 ) FetchContent_MakeAvailable(app-runner)