Skip to content
Draft
Changes from all commits
Commits
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
115 changes: 115 additions & 0 deletions test/Sentry.Unity.Tests/ScreenshotEventProcessorTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.IO;
using System.Text.RegularExpressions;
using NUnit.Framework;
using Sentry.Unity.Tests.Stubs;
using UnityEngine;
Expand All @@ -10,6 +11,26 @@ namespace Sentry.Unity.Tests;

public class ScreenshotEventProcessorTests
{
/// <summary>
/// Subclass that mocks screenshot capture and WaitForEndOfFrame but uses the REAL
/// CaptureAttachment implementation (via Hub.CaptureAttachment), allowing us to verify
/// that the attachment envelope actually reaches the HTTP transport.
/// </summary>
private class RealCaptureScreenshotEventProcessor : ScreenshotEventProcessor
{
public RealCaptureScreenshotEventProcessor(SentryUnityOptions options, ISentryMonoBehaviour sentryMonoBehaviour)
: base(options, sentryMonoBehaviour) { }

internal override Texture2D CreateNewScreenshotTexture2D(SentryUnityOptions options)
=> new Texture2D(1, 1);

internal override YieldInstruction WaitForEndOfFrame()
=> new YieldInstruction();

// CaptureAttachment is intentionally NOT overridden — the base implementation
// calls Hub.CaptureAttachment which sends a standalone attachment envelope.
}

private class TestScreenshotEventProcessor : ScreenshotEventProcessor
{
public Func<SentryUnityOptions, Texture2D> CreateScreenshotFunc { get; set; }
Expand Down Expand Up @@ -348,6 +369,100 @@ public IEnumerator Process_BeforeCaptureScreenshotCallbackReturnsTrue_CapturesSc
Assert.AreEqual(1, screenshotCaptureCallCount);
}

[UnityTest]
public IEnumerator Process_EventCapturedSuccessfully_ScreenshotAttachmentIsSent()
{
// Positive control: when the event IS captured, the screenshot coroutine should send
// the attachment. This validates the test infrastructure so the negative test below
// is meaningful — if this test passes but the next one doesn't, the WasCaptured flag
// is doing its job.

var httpHandler = new TestHttpClientHandler("ScreenshotSuccessTest");
var sentryMonoBehaviour = GetTestMonoBehaviour();

var options = new SentryUnityOptions(application: new TestApplication())
{
Dsn = SentryTests.TestDsn,
CreateHttpMessageHandler = () => httpHandler
};

// Register test screenshot processor as an event processor — it will be called
// during DoSendEvent → ProcessEvent, just like the real ScreenshotEventProcessor.
options.AddEventProcessor(new RealCaptureScreenshotEventProcessor(options, sentryMonoBehaviour));

SentrySdk.Init(options);

try
{
// Event goes through the full DoSendEvent pipeline and is captured successfully.
// DoSendEvent sets @event.WasCaptured = true after CaptureEnvelope succeeds.
var capturedId = SentrySdk.CaptureMessage("test message");
Assert.AreNotEqual(SentryId.Empty, capturedId, "Sanity check: event should be captured");

// Wait for the screenshot coroutine to complete
yield return null;
yield return null;

// Screenshot envelope should reach the transport
var screenshotRequest = httpHandler.GetEvent("screenshot.jpg", TimeSpan.FromSeconds(2));
Assert.IsNotEmpty(screenshotRequest,
"Screenshot attachment should be sent when the event is captured successfully");
}
finally
{
SentrySdk.Close();
}
}

[UnityTest]
public IEnumerator Process_EventDroppedByBeforeSend_ScreenshotAttachmentIsNotSent()
{
// Full pipeline test: the event goes through DoSendEvent where before_send drops it.
// The screenshot coroutine (queued during ProcessEvent, before the drop decision)
// must check WasCaptured and skip — no orphaned attachment envelope.

var httpHandler = new TestHttpClientHandler("ScreenshotBeforeSendTest");
var sentryMonoBehaviour = GetTestMonoBehaviour();

var options = new SentryUnityOptions(application: new TestApplication())
{
Dsn = SentryTests.TestDsn,
CreateHttpMessageHandler = () => httpHandler
};

// Register test screenshot processor — called during DoSendEvent → ProcessEvent
options.AddEventProcessor(new RealCaptureScreenshotEventProcessor(options, sentryMonoBehaviour));

// Drop all events via before_send
options.SetBeforeSend((_, _) => null);

SentrySdk.Init(options);

try
{
// CaptureMessage goes through the full DoSendEvent pipeline:
// ProcessEvent → screenshot processor queues coroutine with @event in closure
// DoBeforeSend → returns null → event dropped, WasCaptured stays false
var capturedId = SentrySdk.CaptureMessage("test message");
Assert.AreEqual(SentryId.Empty, capturedId, "Sanity check: before_send should drop events");

// Wait for the screenshot coroutine to complete
yield return null;
yield return null;

// No screenshot envelope should reach the transport.
// GetEvent logs Debug.LogError on timeout — tell the test runner this is expected.
LogAssert.Expect(LogType.Error, new Regex("timed out"));
var screenshotRequest = httpHandler.GetEvent("screenshot.jpg", TimeSpan.FromSeconds(2));
Assert.IsEmpty(screenshotRequest,
"Screenshot attachment should not be sent when before_send drops the event");
}
finally
{
SentrySdk.Close();
}
}

private static TestSentryMonoBehaviour GetTestMonoBehaviour()
{
var gameObject = new GameObject("ScreenshotProcessorTest");
Expand Down
Loading