Skip to content

fix: Screenshot Capture #2240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
55 changes: 8 additions & 47 deletions src/Sentry.Unity/ScreenshotEventProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,61 +4,22 @@

namespace Sentry.Unity;

public class ScreenshotEventProcessor : ISentryEventProcessorWithHint
public class ScreenshotEventProcessor : ISentryEventProcessor
{
private readonly SentryUnityOptions _options;
private readonly IApplication _application;
public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) : this(sentryOptions, null) { }
private readonly SentryMonoBehaviour _sentryMonoBehaviour;

internal ScreenshotEventProcessor(SentryUnityOptions sentryOptions, IApplication? application)
{
_options = sentryOptions;
_application = application ?? ApplicationAdapter.Instance;
}
public ScreenshotEventProcessor(SentryUnityOptions sentryOptions) : this(sentryOptions, SentryMonoBehaviour.Instance) { }

public SentryEvent? Process(SentryEvent @event)
internal ScreenshotEventProcessor(SentryUnityOptions sentryOptions, SentryMonoBehaviour? sentryMonoBehaviour)
{
return @event;
_options = sentryOptions;
_sentryMonoBehaviour = sentryMonoBehaviour ?? SentryMonoBehaviour.Instance;
Copy link
Member

Choose a reason for hiding this comment

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

We have a public ctor that passes SentryMonoBehaviour.Instance already, having also ?? seems redundant.

Since this is an internal ctor, how about:

Suggested change
_sentryMonoBehaviour = sentryMonoBehaviour ?? SentryMonoBehaviour.Instance;
_sentryMonoBehaviour = sentryMonoBehaviour ?? throw new ArgumentNullException("sentryMonoBehaviour");

}

public SentryEvent? Process(SentryEvent @event, SentryHint hint)
public SentryEvent Process(SentryEvent @event)
{
// save event id
// wait for end of frame
// check if last id is event it
// send screenshot

// add workitem: screentshot for ID xxx
// sdk integration checking for work: if ID got sent, follow up with screenshot

if (!MainThreadData.IsMainThread())
{
_options.DiagnosticLogger?.LogDebug("Screenshot capture skipped. Can't capture screenshots on other than the main thread.");
return @event;
}

if (_options.BeforeCaptureScreenshotInternal?.Invoke() is not false)
{
if (_application.IsEditor)
{
_options.DiagnosticLogger?.LogInfo("Screenshot capture skipped. Capturing screenshots it not supported in the Editor");
return @event;
}

if (Screen.width == 0 || Screen.height == 0)
{
_options.DiagnosticLogger?.LogWarning("Can't capture screenshots on a screen with a resolution of '{0}x{1}'.", Screen.width, Screen.height);
}
else
{
hint.AddAttachment(SentryScreenshot.Capture(_options), "screenshot.jpg", contentType: "image/jpeg");
}
}
else
{
_options.DiagnosticLogger?.LogInfo("Screenshot capture skipped by BeforeAttachScreenshot callback.");
}

_sentryMonoBehaviour.CaptureScreenshotForEvent(_options, @event.EventId);
return @event;
}
}
24 changes: 24 additions & 0 deletions src/Sentry.Unity/SentryMonoBehaviour.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections;
using Sentry.Unity.Integrations;
using UnityEngine;

Expand Down Expand Up @@ -112,3 +113,26 @@ private void Awake()
DontDestroyOnLoad(gameObject);
}
}

/// <summary>
/// A MonoBehaviour that captures screenshots
/// </summary>
public partial class SentryMonoBehaviour
{
// Todo: keep track of event ID to not capture double/more than once per frame

public void CaptureScreenshotForEvent(SentryUnityOptions options, SentryId eventId)
{
StartCoroutine(CaptureScreenshot(options, eventId));
Copy link
Member

Choose a reason for hiding this comment

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

This method returns before the coroutine starts/ends, right? how do we know the screenshot will be taken before the event is created?

}

private IEnumerator CaptureScreenshot(SentryUnityOptions options, SentryId eventId)
{
yield return new WaitForEndOfFrame();

SentryScreenshot.Capture(options);

// Todo: figure out how to capture an event with a screenshot attachment from out here
var sentryEvent = new SentryEvent(eventId: eventId);
}
}
65 changes: 2 additions & 63 deletions test/Sentry.Unity.Tests/ScreenshotEventProcessorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ private class Fixture
public SentryUnityOptions Options = new() { AttachScreenshot = true };
public TestApplication TestApplication = new();

public ScreenshotEventProcessor GetSut() => new(Options, TestApplication);
public ScreenshotEventProcessor GetSut() => new(Options);
}

private Fixture _fixture = null!;
Expand All @@ -28,66 +28,5 @@ public void TearDown()
}
}

[Test]
public void Process_IsMainThread_AddsScreenshotToHint()
{
_fixture.TestApplication.IsEditor = false;
var sut = _fixture.GetSut();
var sentryEvent = new SentryEvent();
var hint = new SentryHint();

sut.Process(sentryEvent, hint);

Assert.AreEqual(1, hint.Attachments.Count);
}

[Test]
public void Process_IsNonMainThread_DoesNotAddScreenshotToHint()
{
var sut = _fixture.GetSut();
var sentryEvent = new SentryEvent();
var hint = new SentryHint();

new Thread(() =>
{
Thread.CurrentThread.IsBackground = true;
var stream = sut.Process(sentryEvent, hint);

Assert.AreEqual(0, hint.Attachments.Count);
}).Start();
}

[Test]
[TestCase(true)]
[TestCase(false)]
public void Process_BeforeCaptureScreenshotCallbackProvided_RespectsScreenshotCaptureDecision(bool captureScreenshot)
{
_fixture.TestApplication.IsEditor = false;
_fixture.Options.SetBeforeCaptureScreenshot(() => captureScreenshot);
var sut = _fixture.GetSut();
var sentryEvent = new SentryEvent();
var hint = new SentryHint();

sut.Process(sentryEvent, hint);

Assert.AreEqual(captureScreenshot ? 1 : 0, hint.Attachments.Count);
}

[Test]
[TestCase(true, 0)]
[TestCase(false, 1)]
public void Process_InEditorEnvironment_DoesNotCaptureScreenshot(bool isEditor, int expectedAttachmentCount)
{
// Arrange
_fixture.TestApplication.IsEditor = isEditor;
var sut = _fixture.GetSut();
var sentryEvent = new SentryEvent();
var hint = new SentryHint();

// Act
sut.Process(sentryEvent, hint);

// Assert
Assert.AreEqual(expectedAttachmentCount, hint.Attachments.Count);
}
// Todo: Add tests that verify passing the capture on to the MonoBehaviour
}