diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a5e6f2a5..77161b52d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -355,11 +355,13 @@ jobs: with: unity-version: ${{ matrix.unity-version }} init-type: ${{ matrix.init-type }} + secrets: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} test-run-ios: name: Run iOS ${{ matrix.unity-version }} Integration Test if: ${{ !startsWith(github.ref, 'refs/heads/release/') }} - needs: [test-compile-ios, create-unity-matrix] + needs: [test-build-ios, create-unity-matrix] uses: ./.github/workflows/test-run-ios.yml with: unity-version: ${{ matrix.unity-version }} diff --git a/.github/workflows/test-build-android.yml b/.github/workflows/test-build-android.yml index 03641763d..69a8dd371 100644 --- a/.github/workflows/test-build-android.yml +++ b/.github/workflows/test-build-android.yml @@ -108,6 +108,8 @@ jobs: - name: Assert symbols and sources were uploaded (Runtime) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath samples/IntegrationTest/Logs/sentry-symbols-upload.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Compare build sizes (Runtime) run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform "Android" -UnityVersion "$env:UNITY_VERSION" @@ -147,6 +149,8 @@ jobs: - name: Assert symbols and sources were uploaded (Build-Time) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath samples/IntegrationTest/Logs/sentry-symbols-upload.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Bundle build & symbol-upload logs with the APK (Build-Time) run: | diff --git a/.github/workflows/test-build-linux.yml b/.github/workflows/test-build-linux.yml index 3d1040e10..42062757b 100644 --- a/.github/workflows/test-build-linux.yml +++ b/.github/workflows/test-build-linux.yml @@ -120,6 +120,8 @@ jobs: - name: Assert symbols and sources were uploaded (Breakpad backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Compare build sizes run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform Linux -UnityVersion "$env:UNITY_VERSION" @@ -158,6 +160,8 @@ jobs: - name: Assert symbols and sources were uploaded (Native backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Create archive (Native backend) run: | diff --git a/.github/workflows/test-build-macos.yml b/.github/workflows/test-build-macos.yml index f00510685..c5b76fd72 100644 --- a/.github/workflows/test-build-macos.yml +++ b/.github/workflows/test-build-macos.yml @@ -100,6 +100,8 @@ jobs: - name: Assert symbols and sources were uploaded (Cocoa backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Compare build sizes run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform MacOS -UnityVersion "$env:UNITY_VERSION" @@ -140,6 +142,8 @@ jobs: - name: Assert symbols and sources were uploaded (Native backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Create archive (Native backend) run: | diff --git a/.github/workflows/test-build-windows-mono.yml b/.github/workflows/test-build-windows-mono.yml index ad14e51fb..de670000f 100644 --- a/.github/workflows/test-build-windows-mono.yml +++ b/.github/workflows/test-build-windows-mono.yml @@ -80,6 +80,8 @@ jobs: - name: Assert symbols and sources were uploaded run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} # We create tar explicitly because upload-artifact is slow for many files. - name: Create archive diff --git a/.github/workflows/test-build-windows-x86.yml b/.github/workflows/test-build-windows-x86.yml index e34266b3e..3751a6d0f 100644 --- a/.github/workflows/test-build-windows-x86.yml +++ b/.github/workflows/test-build-windows-x86.yml @@ -92,6 +92,8 @@ jobs: - name: Assert symbols and sources were uploaded run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} # We create tar explicitly because upload-artifact is slow for many files. - name: Create archive diff --git a/.github/workflows/test-build-windows.yml b/.github/workflows/test-build-windows.yml index bc761b8ad..672911ca2 100644 --- a/.github/workflows/test-build-windows.yml +++ b/.github/workflows/test-build-windows.yml @@ -100,6 +100,8 @@ jobs: - name: Assert symbols and sources were uploaded (Crashpad backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Compare build sizes run: ./test/Scripts.Integration.Test/measure-build-size.ps1 -Path1 "samples/IntegrationTest/Build-NoSentry" -Path2 "samples/IntegrationTest/Build" -Platform Windows -UnityVersion "$env:UNITY_VERSION" @@ -140,6 +142,8 @@ jobs: - name: Assert symbols and sources were uploaded (Native backend) run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath unity.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Create archive (Native backend) run: | diff --git a/.github/workflows/test-compile-ios.yml b/.github/workflows/test-compile-ios.yml index dbb1883f9..7eb41122a 100644 --- a/.github/workflows/test-compile-ios.yml +++ b/.github/workflows/test-compile-ios.yml @@ -8,6 +8,9 @@ on: init-type: required: true type: string + secrets: + SENTRY_AUTH_TOKEN: + required: true defaults: run: @@ -42,6 +45,8 @@ jobs: - name: Assert symbols and sources were uploaded run: ./test/Scripts.Integration.Test/assert-symbol-upload.ps1 -LogPath samples/IntegrationTest/Build/sentry-symbols-upload.log + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} - name: Upload integration-test project on failure if: ${{ failure() }} diff --git a/.github/workflows/test-run-ios.yml b/.github/workflows/test-run-ios.yml index 4e7e4d294..2867868b8 100644 --- a/.github/workflows/test-run-ios.yml +++ b/.github/workflows/test-run-ios.yml @@ -28,7 +28,7 @@ defaults: jobs: run: - timeout-minutes: 25 + timeout-minutes: 45 name: ${{ inputs.unity-version }} ${{ inputs.ios-version }} ${{ inputs.init-type }} runs-on: macos-15 # Pinning to get the oldest, supported version of iOS simulator # Map the job outputs to step outputs @@ -50,28 +50,41 @@ jobs: run: git submodule update --init modules/app-runner shell: bash + - name: Set Xcode for iOS version ${{ env.IOS_VERSION }} + if: ${{ env.IOS_VERSION != 'latest'}} + uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 + with: + xcode-version: '16.4' # to run iOS 18.5 we need Xcode 16.4 + + # A launch within ~2 minutes of the boot can stall past the run timeout, so boot while the app compiles. + - name: Boot simulator + timeout-minutes: 10 + run: | + . ./modules/app-runner/import-modules.ps1 + $target = if ($env:IOS_VERSION -match '^\d+\.\d+$') { "iOS $env:IOS_VERSION" } else { $env:IOS_VERSION } + Connect-Device -Platform "iOSSimulator" -Target $target | Out-Null + "SIMULATOR_BOOTED_AT=$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())" >> $env:GITHUB_ENV + - name: Download app artifact - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + timeout-minutes: 20 + uses: ./.github/actions/wait-for-artifact with: name: testapp-ios-compiled-${{ env.UNITY_VERSION }}-${{ env.INIT_TYPE }} path: samples/IntegrationTest/Build + producer: Compile iOS ${{ env.UNITY_VERSION }} Test / ${{ env.UNITY_VERSION }} ${{ env.INIT_TYPE }} - name: List downloaded files run: | Get-ChildItem -Path "samples/IntegrationTest/Build" -Recurse - - name: Set Xcode for iOS version ${{ env.IOS_VERSION }} - if: ${{ env.IOS_VERSION != 'latest'}} - uses: maxim-lobanov/setup-xcode@ed7a3b1fda3918c0306d1b724322adc0b8cc0a90 # v1.7.0 - with: - xcode-version: '16.4' # to run iOS 18.5 we need Xcode 16.4 - - name: Run iOS Integration Tests id: integration-test timeout-minutes: 20 env: SENTRY_IOS_VERSION: ${{ inputs.ios-version }} run: | + # A rerun finds the app at once; keep its first launch out of the settle window too. + Start-Sleep -Seconds ([Math]::Max(0, [int]$env:SIMULATOR_BOOTED_AT + 150 - [DateTimeOffset]::UtcNow.ToUnixTimeSeconds())) $env:SENTRY_TEST_PLATFORM = "iOS" $env:SENTRY_TEST_APP = "samples/IntegrationTest/Build/IntegrationTest.app" Invoke-Pester -Path test/IntegrationTest/Integration.Tests.ps1 -CI diff --git a/CHANGELOG.md b/CHANGELOG.md index ee836469d..f9c861040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## Unreleased + +### Fixes + +- Running `SentryEditorWindowInstrumentation.ConfigureOptions` again on a configured project no longer clears the options and CLI options configuration ([#2911](https://github.com/getsentry/sentry-unity/pull/2911)) + ## 4.11.0 ### Fixes diff --git a/scripts/unity-utils.ps1 b/scripts/unity-utils.ps1 index 1ee54531a..7740034c2 100644 --- a/scripts/unity-utils.ps1 +++ b/scripts/unity-utils.ps1 @@ -12,7 +12,7 @@ function GetUnityRetrySignature([string] $unityPath, [string[]] $arguments, [int return $null } - # Unity 6.6 on Linux intermittently gets SIGKILLed during startup. + # Unity 6.6 kills the PID in a stale Library/burst.pid at startup, which in a fresh container can be its own xvfb-run. if ($exitCode -eq 137) { return 'killed' @@ -48,7 +48,8 @@ function GetUnityRetrySignature([string] $unityPath, [string[]] $arguments, [int return 'tool-segfault' } - if (($stdout -match 'Batchmode quit successfully invoked') -and ($stdout -match 'Caught fatal signal')) + # A trap after a long shutdown stall (exit 133, SIGTRAP) can kill Unity before it logs the signal. + if (($stdout -match 'Batchmode quit successfully invoked') -and (($stdout -match 'Caught fatal signal') -or $exitCode -eq 133)) { return 'shutdown-crash' } diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/ConfigurationCreator.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/ConfigurationCreator.cs index 93ec1ba50..88e80f5cc 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/ConfigurationCreator.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/ConfigurationCreator.cs @@ -97,23 +97,39 @@ private static void OnScriptsReloaded() internal static void SetScript(string scriptNameWithoutExtension) { - var optionsConfigurationObject = ScriptableObject.CreateInstance(scriptNameWithoutExtension); + var window = EditorWindow.GetWindow(); + SetScript(scriptNameWithoutExtension, window.Options, window.CliOptions); + } - var options = EditorWindow.GetWindow().Options; - var cliOptions = EditorWindow.GetWindow().CliOptions; + internal static void SetScript(string scriptNameWithoutExtension, ScriptableSentryUnityOptions options, SentryCliOptions cliOptions) + { + var optionsConfigurationObject = ScriptableObject.CreateInstance(scriptNameWithoutExtension); switch (optionsConfigurationObject) { case SentryOptionsConfiguration configuration: - AssetDatabase.CreateAsset(optionsConfigurationObject, SentryOptionsConfiguration.GetAssetPath(scriptNameWithoutExtension)); - AssetDatabase.Refresh(); + configuration = LoadOrCreateAsset(configuration, SentryOptionsConfiguration.GetAssetPath(scriptNameWithoutExtension)); options.OptionsConfiguration ??= configuration; // Don't overwrite if already set break; case SentryCliOptionsConfiguration cliConfiguration: - AssetDatabase.CreateAsset(optionsConfigurationObject, SentryCliOptionsConfiguration.GetAssetPath(scriptNameWithoutExtension)); - AssetDatabase.Refresh(); + cliConfiguration = LoadOrCreateAsset(cliConfiguration, SentryCliOptionsConfiguration.GetAssetPath(scriptNameWithoutExtension)); cliOptions.CliOptionsConfiguration ??= cliConfiguration; // Don't overwrite if already set break; } } + + // Recreating the asset would leave the options referencing an object that is no longer it. + private static T LoadOrCreateAsset(T instance, string path) where T : ScriptableObject + { + var existing = AssetDatabase.LoadAssetAtPath(path); + if (existing != null) + { + UnityEngine.Object.DestroyImmediate(instance); + return existing; + } + + AssetDatabase.CreateAsset(instance, path); + AssetDatabase.Refresh(); + return instance; + } } diff --git a/test/IntegrationTest/Integration.Tests.ps1 b/test/IntegrationTest/Integration.Tests.ps1 index 6f2e815d3..4212e7143 100644 --- a/test/IntegrationTest/Integration.Tests.ps1 +++ b/test/IntegrationTest/Integration.Tests.ps1 @@ -270,6 +270,7 @@ BeforeAll { } $runResult = Invoke-App -Action $Action + $sendResult = $null # On Xbox, console output is not available in non-development builds. # Retrieve the log file the app writes directly to disk. @@ -324,11 +325,23 @@ BeforeAll { $runResult.Output | ForEach-Object { Write-Host $_ } Write-Host "::endgroup::" + # sentry-cocoa sends the crash report on crash-send and deletes the envelope if sentry.io answers with a 5xx. Rerun once per job. + $ingest5xx = $Action -eq "crash-capture" -and [bool]($sendResult.Output -match "Received response status code: 5\d\d") + if ($ingest5xx -and $script:Ingest5xxRetried) { + Write-Host "::warning::sentry.io answered the crash report with a 5xx again (signature: ingest-5xx). Not running $Action a third time." + } + elseif ($ingest5xx) { + $script:Ingest5xxRetried = $true + Write-Host "::warning::sentry.io answered the crash report with a 5xx and the SDK dropped it (signature: ingest-5xx). Running $Action again." + return Invoke-TestAction -Action $Action + } + return $runResult } $script:AndroidUninitialisedLaunches = 0 $script:FlushTimeoutRelaunched = $false + $script:Ingest5xxRetried = $false $script:Platform = $env:SENTRY_TEST_PLATFORM if ([string]::IsNullOrEmpty($script:Platform)) { diff --git a/test/Scripts.Integration.Test/assert-symbol-upload.ps1 b/test/Scripts.Integration.Test/assert-symbol-upload.ps1 index ae5e1682f..17e03ba28 100644 --- a/test/Scripts.Integration.Test/assert-symbol-upload.ps1 +++ b/test/Scripts.Integration.Test/assert-symbol-upload.ps1 @@ -60,13 +60,20 @@ if ($log -notmatch "--include-sources") # Confirm the upload reached a terminal success state rather than erroring out. # "Uploaded N missing debug information files" on first upload; "Nothing to upload" -# when the artifacts are already on the server (idempotent re-run / cache hit). +# when the artifacts are already on the server (idempotent re-run / cache hit); +# only "File upload/processing complete" when a rebuild's chunks are all on the server already. $uploadSucceeded = $log -match "Uploaded \d+ missing debug information file" ` - -or $log -match "Nothing to upload" + -or $log -match "Nothing to upload" ` + -or $log -match "File (upload|processing) complete" if (-not $uploadSucceeded) { throw "Symbol-upload assertion failed: sentry-cli upload did not reach a success state in '$LogPath'. " + - "Expected 'Uploaded N missing debug information files' or 'Nothing to upload'." + "Expected 'Uploaded N missing debug information files', 'Nothing to upload' or 'File upload/processing complete'." +} + +if ($log -match "some symbols did not process correctly") +{ + throw "Symbol-upload assertion failed: sentry.io failed to process a debug file in '$LogPath'." } $retries = [regex]::Matches($log, "retry number \d+ failed due to request failed with retryable status code (\d+)") @@ -77,3 +84,74 @@ if ($retries.Count -gt 0) } Write-Host "Symbol-upload assertion passed: sentry-cli uploaded debug symbols and sources ('$LogPath')." -ForegroundColor Green + +# sentry-cli returns before sentry.io has processed the upload, and a large debug companion can take +# minutes. An event that arrives earlier is symbolicated without it and never reprocessed, so wait +# until the server lists each uploaded debug companion, IL2CPP mapping and source bundle. +$pending = [regex]::Matches($log, "UPLOADED (\S+) \([^;)]+;\s*(?:\w+ )?(debug companion|il2cpp|sources)\)") | + ForEach-Object { "$($_.Groups[1].Value) $($_.Groups[2].Value)" } | Sort-Object -Unique +function Test-ProcessedFile($files, [string] $kind) +{ + foreach ($file in $files) + { + $processed = switch ($kind) + { + 'debug companion' { ($file.data | Select-Object -ExpandProperty features -ErrorAction Ignore) -contains 'debug' } + 'il2cpp' { $file.symbolType -eq 'il2cpp' } + # A portable PDB embeds its sources instead of getting a source bundle. + 'sources' { $file.symbolType -eq 'sourcebundle' -or ($file.data | Select-Object -ExpandProperty features -ErrorAction Ignore) -contains 'sources' } + } + if ($processed) { return $true } + } + return $false +} + +if ($pending -and -not $env:SENTRY_AUTH_TOKEN) +{ + Write-Host "::warning::Not waiting for sentry.io to process the debug files: SENTRY_AUTH_TOKEN is not set (signature: symbols-processing-skipped)." +} +elseif ($pending) +{ + # The project CliConfiguration.cs uploads to. + $uriBase = "https://sentry.io/api/0/projects/sentry-sdks/sentry-unity-integration-tests/files/dsyms/" + $timeoutSeconds = 300 + $headers = @{ Authorization = "Bearer $env:SENTRY_AUTH_TOKEN" } + # Deterministic builds reuse debug IDs, so only a file the server processed for this build counts. + $since = [DateTime]::UtcNow.AddMinutes(-30) + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + foreach ($debugId in ($pending | ForEach-Object { $_.Split(' ')[0] } | Sort-Object -Unique)) + { + $kinds = $pending | Where-Object { $_.StartsWith("$debugId ") } | ForEach-Object { $_.Substring($debugId.Length + 1) } + while ($true) + { + try + { + $files = Invoke-RestMethod -Uri "$uriBase`?debug_id=$debugId" -Headers $headers -TimeoutSec 30 + } + catch + { + $status = if ($_.Exception -is [Microsoft.PowerShell.Commands.HttpResponseException]) { [int]$_.Exception.Response.StatusCode } else { 0 } + if ($status -in 401, 403) + { + Write-Host "::warning::Not waiting for sentry.io to process the debug files: the token can't list them (HTTP $status, signature: symbols-processing-skipped)." + return + } + Write-Host "Querying debug files of $debugId failed: $_" + $files = @() + } + $files = @($files | Where-Object { ([DateTime]$_.dateCreated).ToUniversalTime() -ge $since }) + $missing = $kinds | Where-Object { -not (Test-ProcessedFile $files $_) } + if (-not $missing) + { + break + } + if ($stopwatch.Elapsed.TotalSeconds -gt $timeoutSeconds) + { + Write-Host "::warning::sentry.io has not processed $($missing -join ', ') of $debugId after $timeoutSeconds s (signature: symbols-processing-timeout). Events may stay unsymbolicated." + return + } + Start-Sleep -Seconds 10 + } + } + Write-Host "sentry.io processed the uploaded debug files after $([int]$stopwatch.Elapsed.TotalSeconds) s." +} diff --git a/test/Scripts.Integration.Test/build-project.ps1 b/test/Scripts.Integration.Test/build-project.ps1 index d9c4ef5df..38444aa20 100644 --- a/test/Scripts.Integration.Test/build-project.ps1 +++ b/test/Scripts.Integration.Test/build-project.ps1 @@ -22,6 +22,10 @@ Write-Log "Build method: $buildMethod" Write-Detail "Output path: $outputPath" $unityArgs = @("-batchmode", "-projectPath ", "$(GetNewProjectPath)", "-executeMethod", $buildMethod , "-buildPath", $outputPath, "-quit") +# Unity kills the process in Library/burst.pid at startup. In a project made in another container that PID names +# an unrelated process here, typically the xvfb-run wrapping the editor. +Remove-Item "$(GetNewProjectPath)/Library/*.pid" -ErrorAction SilentlyContinue + if ($Platform -eq "Switch") { $unityArgs += @("-buildTarget", "Switch") diff --git a/test/Sentry.Unity.Editor.Tests/OptionsConfigurationItemTests.cs b/test/Sentry.Unity.Editor.Tests/OptionsConfigurationItemTests.cs new file mode 100644 index 000000000..8a924c416 --- /dev/null +++ b/test/Sentry.Unity.Editor.Tests/OptionsConfigurationItemTests.cs @@ -0,0 +1,60 @@ +using NUnit.Framework; +using Sentry.Unity.Editor.ConfigurationWindow; +using UnityEditor; +using UnityEngine; + +namespace Sentry.Unity.Editor.Tests; + +public class SetScriptTestOptionsConfiguration : SentryOptionsConfiguration +{ + public override void Configure(SentryUnityOptions options) { } +} + +public class SetScriptTestCliOptionsConfiguration : SentryCliOptionsConfiguration +{ + public override void Configure(SentryCliOptions cliOptions) { } +} + +public class OptionsConfigurationItemTests +{ + private ScriptableSentryUnityOptions _options = null!; + private SentryCliOptions _cliOptions = null!; + + [SetUp] + public void Setup() + { + _options = ScriptableObject.CreateInstance(); + _cliOptions = ScriptableObject.CreateInstance(); + } + + [TearDown] + public void TearDown() + { + AssetDatabase.DeleteAsset(SentryOptionsConfiguration.GetAssetPath(nameof(SetScriptTestOptionsConfiguration))); + AssetDatabase.DeleteAsset(SentryCliOptionsConfiguration.GetAssetPath(nameof(SetScriptTestCliOptionsConfiguration))); + Object.DestroyImmediate(_options); + Object.DestroyImmediate(_cliOptions); + } + + [Test] + public void SetScript_CalledTwice_KeepsReferenceToOptionsConfigurationAsset() + { + OptionsConfigurationItem.SetScript(nameof(SetScriptTestOptionsConfiguration), _options, _cliOptions); + OptionsConfigurationItem.SetScript(nameof(SetScriptTestOptionsConfiguration), _options, _cliOptions); + + var asset = AssetDatabase.LoadAssetAtPath(SentryOptionsConfiguration.GetAssetPath(nameof(SetScriptTestOptionsConfiguration))); + Assert.IsTrue(asset != null); + Assert.AreSame(asset, _options.OptionsConfiguration); + } + + [Test] + public void SetScript_CalledTwice_KeepsReferenceToCliOptionsConfigurationAsset() + { + OptionsConfigurationItem.SetScript(nameof(SetScriptTestCliOptionsConfiguration), _options, _cliOptions); + OptionsConfigurationItem.SetScript(nameof(SetScriptTestCliOptionsConfiguration), _options, _cliOptions); + + var asset = AssetDatabase.LoadAssetAtPath(SentryCliOptionsConfiguration.GetAssetPath(nameof(SetScriptTestCliOptionsConfiguration))); + Assert.IsTrue(asset != null); + Assert.AreSame(asset, _cliOptions.CliOptionsConfiguration); + } +}