From b88060c508b0aa593babf6a0bbadca93fb46bb4f Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 2 Jul 2024 13:44:42 +0200 Subject: [PATCH 01/14] created fallback for user id --- src/Sentry.Unity.Android/SentryNativeAndroid.cs | 17 +++++++++++++++++ src/Sentry.Unity.iOS/SentryNativeCocoa.cs | 16 ++++++++++++++++ src/Sentry.Unity/SentryMonoBehaviour.cs | 4 ++++ 3 files changed, 37 insertions(+) diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 355e0deb8..0fc2b4ce8 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -1,6 +1,7 @@ using System; using Sentry.Extensibility; using UnityEngine; +using UnityEngine.Analytics; namespace Sentry.Unity.Android { @@ -64,7 +65,23 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry } options.NativeSupportCloseCallback = () => Close(options.DiagnosticLogger); + options.DefaultUserId = SentryJava.GetInstallationId(JniExecutor); + if (string.IsNullOrEmpty(options.DefaultUserId)) + { + // In case we can't get an installation ID we create one and sync that down to the native layer + options.DiagnosticLogger?.LogDebug("Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); + + options.DefaultUserId = AnalyticsSessionInfo.userId; + if (options.DefaultUserId is not null) + { + options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); + } + else + { + options.DiagnosticLogger?.LogDebug("Failed to create new 'Default User ID'."); + } + } } /// diff --git a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs index 297b6da95..78ecbb7c0 100644 --- a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs +++ b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs @@ -3,6 +3,7 @@ using Sentry.PlatformAbstractions; using Sentry.Unity.Integrations; using UnityEngine; +using UnityEngine.Analytics; namespace Sentry.Unity.iOS { @@ -58,6 +59,21 @@ internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sent if (sentryUnityInfo.IL2CPP) { options.DefaultUserId = SentryCocoaBridgeProxy.GetInstallationId(); + if (string.IsNullOrEmpty(options.DefaultUserId)) + { + // In case we can't get an installation ID we create one and sync that down to the native layer + options.DiagnosticLogger?.LogDebug("Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); + + options.DefaultUserId = AnalyticsSessionInfo.userId; + if (options.DefaultUserId is not null) + { + options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); + } + else + { + options.DiagnosticLogger?.LogDebug("Failed to create new 'Default User ID'."); + } + } } } diff --git a/src/Sentry.Unity/SentryMonoBehaviour.cs b/src/Sentry.Unity/SentryMonoBehaviour.cs index c94836015..e8dfdd99f 100644 --- a/src/Sentry.Unity/SentryMonoBehaviour.cs +++ b/src/Sentry.Unity/SentryMonoBehaviour.cs @@ -130,7 +130,11 @@ internal void CollectData() // delay on the UI and we're safe to do it on the main thread. MainThreadData.MainThreadId = SentrySystemInfo.MainThreadId; MainThreadData.ProcessorCount = SentrySystemInfo.ProcessorCount; + MainThreadData.OperatingSystem = SentrySystemInfo.OperatingSystem; + Debug.Log("HUEHUE"); + Debug.Log(MainThreadData.OperatingSystem); + MainThreadData.CpuDescription = SentrySystemInfo.CpuDescription; MainThreadData.SupportsVibration = SentrySystemInfo.SupportsVibration; MainThreadData.DeviceName = SentrySystemInfo.DeviceName; From f4cca630054d2713d9405f54959d74feb4858782 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 2 Jul 2024 14:37:59 +0200 Subject: [PATCH 02/14] no comment --- src/Sentry.Unity/SentryMonoBehaviour.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Sentry.Unity/SentryMonoBehaviour.cs b/src/Sentry.Unity/SentryMonoBehaviour.cs index e8dfdd99f..c94836015 100644 --- a/src/Sentry.Unity/SentryMonoBehaviour.cs +++ b/src/Sentry.Unity/SentryMonoBehaviour.cs @@ -130,11 +130,7 @@ internal void CollectData() // delay on the UI and we're safe to do it on the main thread. MainThreadData.MainThreadId = SentrySystemInfo.MainThreadId; MainThreadData.ProcessorCount = SentrySystemInfo.ProcessorCount; - MainThreadData.OperatingSystem = SentrySystemInfo.OperatingSystem; - Debug.Log("HUEHUE"); - Debug.Log(MainThreadData.OperatingSystem); - MainThreadData.CpuDescription = SentrySystemInfo.CpuDescription; MainThreadData.SupportsVibration = SentrySystemInfo.SupportsVibration; MainThreadData.DeviceName = SentrySystemInfo.DeviceName; From 7e7ed038e686b87eb32df26a669a957a92e22834 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 2 Jul 2024 14:46:35 +0200 Subject: [PATCH 03/14] Updated CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05e05168e..930e7415f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixes + +- Added a fallback for user.id on Android and iOS in case none could be extracted from the native layer ([#1710](https://github.com/getsentry/sentry-unity/pull/1710)) + ### Dependencies - Bump Cocoa SDK from v8.29.1 to v8.30.0 ([#1702](https://github.com/getsentry/sentry-unity/pull/1702)) From 44e9861a56af3af5e240719269fa97b579ecc4ff Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 3 Jul 2024 19:31:25 +0200 Subject: [PATCH 04/14] test/ --- src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs | 4 ++-- src/Sentry.Unity.Android/IJniExecutor.cs | 10 ++++++++++ src/Sentry.Unity.Android/JniExecutor.cs | 2 +- src/Sentry.Unity.Android/NativeContextWriter.cs | 4 ++-- src/Sentry.Unity.Android/SentryJava.cs | 8 ++++---- src/Sentry.Unity.Android/SentryNativeAndroid.cs | 4 ++-- 6 files changed, 21 insertions(+), 11 deletions(-) create mode 100644 src/Sentry.Unity.Android/IJniExecutor.cs diff --git a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs index 013c80833..d960c593c 100644 --- a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs +++ b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs @@ -8,9 +8,9 @@ namespace Sentry.Unity.Android /// public class AndroidJavaScopeObserver : ScopeObserver { - private readonly JniExecutor _jniExecutor; + private readonly IJniExecutor _jniExecutor; - public AndroidJavaScopeObserver(SentryOptions options, JniExecutor jniExecutor) : base("Android", options) + public AndroidJavaScopeObserver(SentryOptions options, IJniExecutor jniExecutor) : base("Android", options) { _jniExecutor = jniExecutor; } diff --git a/src/Sentry.Unity.Android/IJniExecutor.cs b/src/Sentry.Unity.Android/IJniExecutor.cs new file mode 100644 index 000000000..4167eefe2 --- /dev/null +++ b/src/Sentry.Unity.Android/IJniExecutor.cs @@ -0,0 +1,10 @@ +using System; + +namespace Sentry.Unity.Android +{ + public interface IJniExecutor : IDisposable + { + public TResult? Run(Func jniOperation); + public void Run(Action jniOperation); + } +} diff --git a/src/Sentry.Unity.Android/JniExecutor.cs b/src/Sentry.Unity.Android/JniExecutor.cs index 5d0aed79e..1e93f8049 100644 --- a/src/Sentry.Unity.Android/JniExecutor.cs +++ b/src/Sentry.Unity.Android/JniExecutor.cs @@ -5,7 +5,7 @@ namespace Sentry.Unity.Android { - public class JniExecutor + public class JniExecutor : IJniExecutor { private readonly CancellationTokenSource _shutdownSource; private readonly AutoResetEvent _taskEvent; diff --git a/src/Sentry.Unity.Android/NativeContextWriter.cs b/src/Sentry.Unity.Android/NativeContextWriter.cs index a65628aee..882df78af 100644 --- a/src/Sentry.Unity.Android/NativeContextWriter.cs +++ b/src/Sentry.Unity.Android/NativeContextWriter.cs @@ -4,9 +4,9 @@ namespace Sentry.Unity.Android { internal class NativeContextWriter : ContextWriter { - private readonly JniExecutor _jniExecutor; + private readonly IJniExecutor _jniExecutor; - public NativeContextWriter(JniExecutor jniExecutor) + public NativeContextWriter(IJniExecutor jniExecutor) { _jniExecutor = jniExecutor; } diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index e922ae60e..6aae6cb59 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -14,7 +14,7 @@ namespace Sentry.Unity.Android /// internal static class SentryJava { - internal static string? GetInstallationId(JniExecutor jniExecutor) + internal static string? GetInstallationId(IJniExecutor jniExecutor) { return jniExecutor.Run(() => { @@ -35,7 +35,7 @@ internal static class SentryJava /// True if the last run terminated in a crash. No otherwise. /// If the SDK wasn't able to find this information, null is returned. /// - public static bool? CrashedLastRun(JniExecutor jniExecutor) + public static bool? CrashedLastRun(IJniExecutor jniExecutor) { return jniExecutor.Run(() => { @@ -45,7 +45,7 @@ internal static class SentryJava }); } - public static void Close(JniExecutor jniExecutor) + public static void Close(IJniExecutor jniExecutor) { jniExecutor.Run(() => { @@ -57,7 +57,7 @@ public static void Close(JniExecutor jniExecutor) private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); public static void WriteScope( - JniExecutor jniExecutor, + IJniExecutor jniExecutor, int? GpuId, string? GpuName, string? GpuVendorName, diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 0fc2b4ce8..55be50b97 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -10,7 +10,7 @@ namespace Sentry.Unity.Android /// public static class SentryNativeAndroid { - private static JniExecutor? JniExecutor; + internal static IJniExecutor? JniExecutor; /// /// Configures the native Android support. @@ -26,7 +26,7 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry return; } - JniExecutor = new JniExecutor(); + JniExecutor ??= new JniExecutor(); options.NativeContextWriter = new NativeContextWriter(JniExecutor); options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor); From 6d9841c992f8c6e528657470a1e9fc6c81eb5fc0 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 3 Jul 2024 19:32:20 +0200 Subject: [PATCH 05/14] test --- .../SentryNativeAndroidTests.cs | 2 ++ .../TestJniExecutor.cs | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+) create mode 100644 test/Sentry.Unity.Android.Tests/TestJniExecutor.cs diff --git a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs index fb9b63415..82fffb68d 100644 --- a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs +++ b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs @@ -24,6 +24,8 @@ public void SetUp() _fakeReinstallSentryNativeBackendStrategy); _reinstallCalled = false; _sentryUnityInfo = new TestUnityInfo { IL2CPP = false }; + + SentryNativeAndroid.JniExecutor = new TestJniExecutor(); } [TearDown] diff --git a/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs b/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs new file mode 100644 index 000000000..744a9b50c --- /dev/null +++ b/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs @@ -0,0 +1,21 @@ +using System; + +namespace Sentry.Unity.Android.Tests +{ + public class TestJniExecutor : IJniExecutor + { + public TResult? Run(Func jniOperation) + { + return default; + } + + public void Run(Action jniOperation) + { + } + + public void Dispose() + { + // TODO release managed resources here + } + } +} From ce80bd488c7ea39a579ea0226edc1236cccebf17 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 3 Jul 2024 20:06:08 +0200 Subject: [PATCH 06/14] more tests --- .../AndroidJavaScopeObserver.cs | 2 +- src/Sentry.Unity.Android/IJniExecutor.cs | 2 +- src/Sentry.Unity.Android/JniExecutor.cs | 2 +- .../NativeContextWriter.cs | 6 +- src/Sentry.Unity.Android/SentryJava.cs | 81 ++++++++++++------- .../SentryNativeAndroid.cs | 6 +- .../SentryNativeAndroidTests.cs | 15 +++- .../TestSentryJava.cs | 38 +++++++++ 8 files changed, 116 insertions(+), 36 deletions(-) create mode 100644 test/Sentry.Unity.Android.Tests/TestSentryJava.cs diff --git a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs index d960c593c..c56428761 100644 --- a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs +++ b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs @@ -6,7 +6,7 @@ namespace Sentry.Unity.Android /// Scope Observer for Android through Java (JNI). /// /// - public class AndroidJavaScopeObserver : ScopeObserver + internal class AndroidJavaScopeObserver : ScopeObserver { private readonly IJniExecutor _jniExecutor; diff --git a/src/Sentry.Unity.Android/IJniExecutor.cs b/src/Sentry.Unity.Android/IJniExecutor.cs index 4167eefe2..4eb8c8f9d 100644 --- a/src/Sentry.Unity.Android/IJniExecutor.cs +++ b/src/Sentry.Unity.Android/IJniExecutor.cs @@ -2,7 +2,7 @@ namespace Sentry.Unity.Android { - public interface IJniExecutor : IDisposable + internal interface IJniExecutor : IDisposable { public TResult? Run(Func jniOperation); public void Run(Action jniOperation); diff --git a/src/Sentry.Unity.Android/JniExecutor.cs b/src/Sentry.Unity.Android/JniExecutor.cs index 1e93f8049..7bd97bcbc 100644 --- a/src/Sentry.Unity.Android/JniExecutor.cs +++ b/src/Sentry.Unity.Android/JniExecutor.cs @@ -5,7 +5,7 @@ namespace Sentry.Unity.Android { - public class JniExecutor : IJniExecutor + internal class JniExecutor : IJniExecutor { private readonly CancellationTokenSource _shutdownSource; private readonly AutoResetEvent _taskEvent; diff --git a/src/Sentry.Unity.Android/NativeContextWriter.cs b/src/Sentry.Unity.Android/NativeContextWriter.cs index 882df78af..3b77bb175 100644 --- a/src/Sentry.Unity.Android/NativeContextWriter.cs +++ b/src/Sentry.Unity.Android/NativeContextWriter.cs @@ -5,10 +5,12 @@ namespace Sentry.Unity.Android internal class NativeContextWriter : ContextWriter { private readonly IJniExecutor _jniExecutor; + private readonly ISentryJava _sentryJava; - public NativeContextWriter(IJniExecutor jniExecutor) + public NativeContextWriter(IJniExecutor jniExecutor, ISentryJava sentryJava) { _jniExecutor = jniExecutor; + _sentryJava = sentryJava; } protected override void WriteScope( @@ -50,7 +52,7 @@ protected override void WriteScope( // We're only setting the missing contexts, the rest is configured by sentry-java. We could also sync // the "unity" context, but it doesn't seem so useful and the effort to do is larger because there's no // class for it in Java - not sure how we could add a generic context object in Java... - SentryJava.WriteScope( + _sentryJava.WriteScope( _jniExecutor, GpuId, GpuName, diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index 6aae6cb59..628c45e3d 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -4,6 +4,30 @@ namespace Sentry.Unity.Android { + internal interface ISentryJava + { + public string? GetInstallationId(IJniExecutor jniExecutor); + public bool? CrashedLastRun(IJniExecutor jniExecutor); + public void Close(IJniExecutor jniExecutor); + public void WriteScope( + IJniExecutor jniExecutor, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel); + } + /// /// JNI access to `sentry-java` methods. /// @@ -12,9 +36,9 @@ namespace Sentry.Unity.Android /// and `sentry-java` maven packages. /// /// - internal static class SentryJava + internal class SentryJava : ISentryJava { - internal static string? GetInstallationId(IJniExecutor jniExecutor) + public string? GetInstallationId(IJniExecutor jniExecutor) { return jniExecutor.Run(() => { @@ -35,7 +59,7 @@ internal static class SentryJava /// True if the last run terminated in a crash. No otherwise. /// If the SDK wasn't able to find this information, null is returned. /// - public static bool? CrashedLastRun(IJniExecutor jniExecutor) + public bool? CrashedLastRun(IJniExecutor jniExecutor) { return jniExecutor.Run(() => { @@ -45,7 +69,7 @@ internal static class SentryJava }); } - public static void Close(IJniExecutor jniExecutor) + public void Close(IJniExecutor jniExecutor) { jniExecutor.Run(() => { @@ -56,7 +80,7 @@ public static void Close(IJniExecutor jniExecutor) private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); - public static void WriteScope( + public void WriteScope( IJniExecutor jniExecutor, int? GpuId, string? GpuName, @@ -101,28 +125,6 @@ public static void WriteScope( }); } - private static void SetIfNotNull(this AndroidJavaObject javaObject, string property, T? value, string? valueClass = null) - { - if (value is not null) - { - if (valueClass is null) - { - javaObject.Set(property, value!); - } - else - { - using var valueObject = new AndroidJavaObject(valueClass, value!); - javaObject.Set(property, valueObject); - } - } - } - - private static void SetIfNotNull(this AndroidJavaObject javaObject, string property, int? value) => - SetIfNotNull(javaObject, property, value, "java.lang.Integer"); - - private static void SetIfNotNull(this AndroidJavaObject javaObject, string property, bool? value) => - SetIfNotNull(javaObject, property, value, "java.lang.Boolean"); - // Implements the io.sentry.ScopeCallback interface. internal class ScopeCallback : AndroidJavaProxy { @@ -156,4 +158,29 @@ public ScopeCallback(Action callback) : base("io.sentry.Scope } } } + + internal static class AndroidJavaObjectExtension + { + public static void SetIfNotNull(this AndroidJavaObject javaObject, string property, T? value, string? valueClass = null) + { + if (value is not null) + { + if (valueClass is null) + { + javaObject.Set(property, value!); + } + else + { + using var valueObject = new AndroidJavaObject(valueClass, value!); + javaObject.Set(property, valueObject); + } + } + } + + public static void SetIfNotNull(this AndroidJavaObject javaObject, string property, int? value) => + SetIfNotNull(javaObject, property, value, "java.lang.Integer"); + + public static void SetIfNotNull(this AndroidJavaObject javaObject, string property, bool? value) => + SetIfNotNull(javaObject, property, value, "java.lang.Boolean"); + } } diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 55be50b97..791446518 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -11,6 +11,7 @@ namespace Sentry.Unity.Android public static class SentryNativeAndroid { internal static IJniExecutor? JniExecutor; + internal static ISentryJava? SentryJava; /// /// Configures the native Android support. @@ -27,8 +28,9 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry } JniExecutor ??= new JniExecutor(); + SentryJava ??= new SentryJava(); - options.NativeContextWriter = new NativeContextWriter(JniExecutor); + options.NativeContextWriter = new NativeContextWriter(JniExecutor, SentryJava); options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor); options.EnableScopeSync = true; options.CrashedLastRun = () => @@ -95,7 +97,7 @@ public static void Close(IDiagnosticLogger? logger = null) // This is an edge-case where the Android SDK has been enabled and setup during build-time but is being // shut down at runtime. In this case Configure() has not been called and there is no JniExecutor yet JniExecutor ??= new JniExecutor(); - SentryJava.Close(JniExecutor); + SentryJava?.Close(JniExecutor); JniExecutor.Dispose(); } } diff --git a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs index 82fffb68d..a8c7061fb 100644 --- a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs +++ b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs @@ -1,8 +1,6 @@ using System; using System.Threading; using NUnit.Framework; -using Sentry.Unity; -using UnityEngine; namespace Sentry.Unity.Android.Tests { @@ -26,6 +24,7 @@ public void SetUp() _sentryUnityInfo = new TestUnityInfo { IL2CPP = false }; SentryNativeAndroid.JniExecutor = new TestJniExecutor(); + SentryNativeAndroid.SentryJava = new TestSentryJava(); } [TearDown] @@ -94,5 +93,17 @@ public void Configure_NativeAndroidSupportDisabled_DoesNotReInitializeNativeBack SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.False(_reinstallCalled); } + + [Test] + public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() + { + var options = new SentryUnityOptions(); + var sentryJava = SentryNativeAndroid.SentryJava as TestSentryJava; + Assert.NotNull(sentryJava); + sentryJava!.InstallationId = string.Empty; + + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.False(string.IsNullOrEmpty(options.DefaultUserId)); + } } } diff --git a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs new file mode 100644 index 000000000..0fe1da140 --- /dev/null +++ b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs @@ -0,0 +1,38 @@ +namespace Sentry.Unity.Android.Tests +{ + internal class TestSentryJava : ISentryJava + { + public string? InstallationId { get; set; } + public bool? IsCrashedLastRun { get; set; } + + public string? GetInstallationId(IJniExecutor jniExecutor) + { + return InstallationId; + } + + public bool? CrashedLastRun(IJniExecutor jniExecutor) + { + return IsCrashedLastRun; + } + + public void Close(IJniExecutor jniExecutor) { } + + public void WriteScope( + IJniExecutor jniExecutor, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel) { } + } +} From ffd942d211c4d254f4805ae21c4c92de51fd2b0f Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Thu, 4 Jul 2024 12:46:12 +0000 Subject: [PATCH 07/14] Format code --- test/Sentry.Unity.Android.Tests/TestSentryJava.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs index 0fe1da140..a51b522bf 100644 --- a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs +++ b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs @@ -33,6 +33,7 @@ public void WriteScope( bool? GpuSupportsGeometryShaders, string? GpuVendorId, bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel) { } + string? GpuGraphicsShaderLevel) + { } } } From 898234561c98eb2fb738109a0511f9d70c039c81 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 5 Jul 2024 13:06:56 +0200 Subject: [PATCH 08/14] added early bail check --- src/Sentry.Unity.Android/SentryJava.cs | 20 ++++++++++++++++-- .../SentryNativeAndroid.cs | 21 +++++++++++++++---- .../TestSentryJava.cs | 2 ++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index 628c45e3d..904a77474 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -26,6 +26,8 @@ public void WriteScope( string? GpuVendorId, bool? GpuMultiThreadedRendering, string? GpuGraphicsShaderLevel); + + public bool IsSentryJavaPresent(); } /// @@ -38,6 +40,8 @@ public void WriteScope( /// internal class SentryJava : ISentryJava { + private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); + public string? GetInstallationId(IJniExecutor jniExecutor) { return jniExecutor.Run(() => @@ -78,8 +82,6 @@ public void Close(IJniExecutor jniExecutor) }); } - private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); - public void WriteScope( IJniExecutor jniExecutor, int? GpuId, @@ -125,6 +127,20 @@ public void WriteScope( }); } + public bool IsSentryJavaPresent() + { + try + { + _ = GetSentryJava(); + } + catch (AndroidJavaException) + { + return false; + } + + return true; + } + // Implements the io.sentry.ScopeCallback interface. internal class ScopeCallback : AndroidJavaProxy { diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index 791446518..007ff99ff 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -1,6 +1,5 @@ using System; using Sentry.Extensibility; -using UnityEngine; using UnityEngine.Analytics; namespace Sentry.Unity.Android @@ -11,7 +10,7 @@ namespace Sentry.Unity.Android public static class SentryNativeAndroid { internal static IJniExecutor? JniExecutor; - internal static ISentryJava? SentryJava; + internal static ISentryJava SentryJava = new SentryJava(); /// /// Configures the native Android support. @@ -27,8 +26,16 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry return; } + if (!SentryJava.IsSentryJavaPresent()) + { + options.DiagnosticLogger?.LogError("Android Native Support has been enabled but the " + + "Sentry Java SDK is missing. This could have been caused by a mismatching" + + "build time / runtime configuration. Please make sure you have " + + "Android Native Support enabled during build time."); + return; + } + JniExecutor ??= new JniExecutor(); - SentryJava ??= new SentryJava(); options.NativeContextWriter = new NativeContextWriter(JniExecutor, SentryJava); options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor); @@ -75,6 +82,7 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry options.DiagnosticLogger?.LogDebug("Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); options.DefaultUserId = AnalyticsSessionInfo.userId; + if (options.DefaultUserId is not null) { options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); @@ -91,13 +99,18 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry /// public static void Close(IDiagnosticLogger? logger = null) { + if (!SentryJava.IsSentryJavaPresent()) + { + return; + } + // Sentry Native is initialized and closed by the Java SDK, no need to call into it directly logger?.LogDebug("Closing the sentry-java SDK"); // This is an edge-case where the Android SDK has been enabled and setup during build-time but is being // shut down at runtime. In this case Configure() has not been called and there is no JniExecutor yet JniExecutor ??= new JniExecutor(); - SentryJava?.Close(JniExecutor); + SentryJava.Close(JniExecutor); JniExecutor.Dispose(); } } diff --git a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs index 0fe1da140..3fd8a01db 100644 --- a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs +++ b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs @@ -34,5 +34,7 @@ public void WriteScope( string? GpuVendorId, bool? GpuMultiThreadedRendering, string? GpuGraphicsShaderLevel) { } + + public bool IsSentryJavaPresent() => true; } } From 4090b6a81f9a73686bd3ebf952e888c1330abc5f Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Fri, 5 Jul 2024 11:10:52 +0000 Subject: [PATCH 09/14] Format code --- test/Sentry.Unity.Android.Tests/TestSentryJava.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs index 3fd8a01db..7d07a09e6 100644 --- a/test/Sentry.Unity.Android.Tests/TestSentryJava.cs +++ b/test/Sentry.Unity.Android.Tests/TestSentryJava.cs @@ -33,7 +33,8 @@ public void WriteScope( bool? GpuSupportsGeometryShaders, string? GpuVendorId, bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel) { } + string? GpuGraphicsShaderLevel) + { } public bool IsSentryJavaPresent() => true; } From 004b43440cc850cf48e9f065939afabda5f5d47a Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Fri, 5 Jul 2024 14:06:23 +0200 Subject: [PATCH 10/14] Updated CHANGELOG.md --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8074443c4..e8c6242c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- The SDK now checks whether the Android SDK is available before attempting to initialize it. This prevents `AndroidJavaException: java.lang.ClassNotFoundException: io.sentry.Sentry` from being thrown ([#1714](https://github.com/getsentry/sentry-unity/pull/1714)) - Added a fallback for user.id on Android and iOS in case none could be extracted from the native layer ([#1710](https://github.com/getsentry/sentry-unity/pull/1710)) ### Dependencies From 918843b3ca0777055bf233e2e3f8a1292d1f2b14 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Tue, 9 Jul 2024 14:13:26 +0200 Subject: [PATCH 11/14] moved getsentry --- src/Sentry.Unity.Android/SentryJava.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index ba5532e53..c23d4251a 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -81,8 +81,6 @@ public void Close(IJniExecutor jniExecutor) }); } - private static AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); - public void WriteScope( IJniExecutor jniExecutor, int? GpuId, From db20403a780f61320caab67bc93a125bbd95fb9b Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 10 Jul 2024 14:26:38 +0200 Subject: [PATCH 12/14] fucked up the merge --- Directory.Build.props | 2 +- .../AndroidJavaScopeObserver.cs | 147 ++- .../BreadcrumbExtensions.cs | 41 +- src/Sentry.Unity.Android/IJniExecutor.cs | 13 +- src/Sentry.Unity.Android/JniExecutor.cs | 179 ++- .../NativeContextWriter.cs | 179 ++- src/Sentry.Unity.Android/SentryNative.cs | 53 +- .../BuildPostProcess.cs | 277 +++-- src/Sentry.Unity.Editor.iOS/NativeMain.cs | 73 +- src/Sentry.Unity.Editor.iOS/NativeOptions.cs | 66 +- .../SentryXcodeProject.cs | 303 +++-- .../Android/AndroidManifestConfiguration.cs | 706 ++++++------ .../Android/AndroidUtils.cs | 49 +- .../Android/DebugSymbolUpload.cs | 489 ++++---- .../Android/GradleSetup.cs | 97 +- .../Android/PostBuildCheck.cs | 221 ++-- .../Android/ProguardSetup.cs | 155 ++- .../SentryPerformanceAutoInstrumentation.cs | 155 ++- .../SentryPlayerReaderWriter.cs | 245 ++-- .../ConfigurationWindow/AdvancedTab.cs | 337 +++--- .../ConfigurationWindow/CoreTab.cs | 197 ++-- .../ConfigurationWindow/DebugSymbolsTab.cs | 91 +- .../ConfigurationWindow/EnrichmentTab.cs | 286 +++-- .../OptionsConfigurationTab.cs | 301 +++-- .../SentryEditorWindowInstrumentation.cs | 103 +- .../ConfigurationWindow/SentryTestWindow.cs | 29 +- .../ConfigurationWindow/SentryWindow.cs | 370 +++--- .../ConfigurationWindow/TransportTab.cs | 113 +- .../ConfigurationWindow/Wizard.cs | 519 +++++---- .../ConfigurationWindow/WizardApi.cs | 96 +- .../Il2CppBuildPreProcess.cs | 76 +- .../Native/BuildPostProcess.cs | 417 ++++--- .../ScriptableSentryUnityOptionsEditor.cs | 207 ++-- src/Sentry.Unity.Editor/SentryCli.cs | 180 ++- .../SentryCliOptionsEditor.cs | 33 +- src/Sentry.Unity.Editor/SentryFileUtil.cs | 41 +- src/Sentry.Unity.Editor/SentryPackageInfo.cs | 39 +- .../SentryScriptableObject.cs | 67 +- src/Sentry.Unity.Editor/SentryUnityVersion.cs | 27 +- .../UnityCommandLineArguments.cs | 35 +- .../NativeContextWriter.cs | 151 ++- .../NativeScopeObserver.cs | 87 +- src/Sentry.Unity.Native/SentryNative.cs | 124 +- src/Sentry.Unity.Native/SentryNativeBridge.cs | 419 ++++--- src/Sentry.Unity.iOS/NativeContextWriter.cs | 227 ++-- src/Sentry.Unity.iOS/NativeScopeObserver.cs | 67 +- .../SentryCocoaBridgeProxy.cs | 151 ++- src/Sentry.Unity.iOS/SentryNativeCocoa.cs | 131 ++- src/Sentry.Unity/ContextWriter.cs | 175 ++- src/Sentry.Unity/EventCapture.cs | 11 +- src/Sentry.Unity/Extensions/JsonExtensions.cs | 57 +- src/Sentry.Unity/ISentryUnityInfo.cs | 65 +- src/Sentry.Unity/Il2CppEventProcessor.cs | 546 +++++---- .../Integrations/AnrIntegration.cs | 291 +++-- src/Sentry.Unity/Integrations/IApplication.cs | 81 +- .../Integrations/ISceneManager.cs | 65 +- .../Integrations/SceneManagerIntegration.cs | 89 +- .../Integrations/SessionIntegration.cs | 49 +- .../UnityBadGatewayExceptionFilter.cs | 17 +- .../UnityBeforeSceneLoadIntegration.cs | 27 +- .../UnityLogHandlerIntegration.cs | 283 +++-- .../Integrations/UnityScopeIntegration.cs | 279 +++-- .../UnitySocketExceptionFilter.cs | 17 +- .../Integrations/UnityWebExceptionFilter.cs | 17 +- src/Sentry.Unity/Json/SafeSerializer.cs | 47 +- src/Sentry.Unity/MainThreadData.cs | 75 +- src/Sentry.Unity/NativeUtils/CFunctions.cs | 287 +++-- src/Sentry.Unity/NativeUtils/ContextWriter.cs | 181 ++- src/Sentry.Unity/Protocol/Unity.cs | 195 ++-- src/Sentry.Unity/ScopeObserver.cs | 129 ++- src/Sentry.Unity/ScreenshotAttachment.cs | 181 ++- .../ScriptableSentryUnityOptions.cs | 505 +++++---- .../SentryBuildTimeOptionsConfiguration.cs | 23 +- src/Sentry.Unity/SentryCliOptions.cs | 113 +- src/Sentry.Unity/SentryMonoBehaviour.cs | 289 +++-- .../SentryRuntimeOptionsConfiguration.cs | 23 +- src/Sentry.Unity/SentryUnity.cs | 73 +- src/Sentry.Unity/SentryUnityOptions.cs | 611 +++++----- .../SentryUnityOptionsExtensions.cs | 171 ++- src/Sentry.Unity/SentryUnitySDK.cs | 161 ++- src/Sentry.Unity/SystemInfoAdapter.cs | 196 ++-- src/Sentry.Unity/TimeDebounceBase.cs | 83 +- src/Sentry.Unity/UnityEventProcessor.cs | 139 ++- src/Sentry.Unity/UnityLogger.cs | 65 +- .../UnityViewHierarchyAttachmentContent.cs | 149 ++- src/Sentry.Unity/UnityViewHierarchyNode.cs | 77 +- src/Sentry.Unity/UnityWebRequestTransport.cs | 168 ++- src/Sentry.Unity/WebGL/SentryWebGL.cs | 75 +- .../SentryNativeAndroidTests.cs | 89 +- .../TestJniExecutor.cs | 27 +- .../AndroidManifestConfigurationTests.cs | 668 ++++++----- .../Android/DebugSymbolUploadTests.cs | 393 ++++--- .../Android/GradleSetupTests.cs | 157 ++- .../Android/ProguardSetupTests.cs | 189 ++-- .../EditorModeTests.cs | 101 +- .../EmbeddedResourcesTests.cs | 21 +- .../Il2CppBuildPreProcess.cs | 126 +-- .../ScriptableSentryUnityOptionsTests.cs | 93 +- .../SentryCliTests.cs | 215 ++-- .../SentryScriptableObjectTests.cs | 107 +- .../SentryUnityVersionTests.cs | 53 +- test/Sentry.Unity.Editor.Tests/WizardTests.cs | 95 +- .../BuildPostProcessorTests.cs | 377 ++++--- .../NativeMainTests.cs | 111 +- .../NativeOptionsTests.cs | 169 ++- .../SentryXcodeProjectTests.cs | 241 ++-- test/Sentry.Unity.Tests/AnrDetectionTests.cs | 211 ++-- test/Sentry.Unity.Tests/ContextWriterTests.cs | 422 ++++--- test/Sentry.Unity.Tests/DebouncerTests.cs | 79 +- test/Sentry.Unity.Tests/IntegrationTests.cs | 499 ++++---- .../Json/SafeSerializerTests.cs | 99 +- .../Sentry.Unity.Tests/Protocol/UnityTests.cs | 115 +- .../SceneManagerIntegrationTests.cs | 243 ++-- .../ScreenshotAttachmentContentTests.cs | 138 ++- .../ScriptableSentryUnityOptionsTests.cs | 297 +++-- .../SentryMonoBehaviourTests.cs | 109 +- test/Sentry.Unity.Tests/SentryTests.cs | 35 +- .../SentryUnityOptionsExtensionsTests.cs | 197 ++-- .../SentryUnityOptionsTests.cs | 122 +- test/Sentry.Unity.Tests/SentryUnityTests.cs | 162 ++- .../SessionIntegrationTests.cs | 55 +- test/Sentry.Unity.Tests/Stubs/TestHub.cs | 331 +++--- .../TestBehaviours/TestMonoBehaviour.cs | 43 +- .../TestHttpClientHandler.cs | 91 +- .../UnityBadGatewayExceptionFilterTests.cs | 73 +- .../UnityBeforeSceneLoadIntegrationTests.cs | 63 +- .../UnityEventScopeTests.cs | 1002 ++++++++--------- ...UnityIl2CppEventExceptionProcessorTests.cs | 29 +- .../UnityLogHandlerIntegrationTests.cs | 383 ++++--- test/Sentry.Unity.Tests/UnityLoggerTests.cs | 94 +- .../UnitySocketExceptionFilterTests.cs | 41 +- .../UnityViewHierarchyAttachmentTests.cs | 257 +++-- .../UnityWebExceptionFilterTests.cs | 41 +- .../NativeScopeObserverTests.cs | 43 +- .../SentryNativeIosTests.cs | 85 +- test/SharedClasses/TestApplication.cs | 67 +- test/SharedClasses/TestLogger.cs | 35 +- test/SharedClasses/UnityTestLogger.cs | 171 ++- 138 files changed, 11567 insertions(+), 11753 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cfcabcdbf..027b9c5cf 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ 2.1.0 - 9 + 12 enable true false diff --git a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs index c56428761..94ca19eae 100644 --- a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs +++ b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs @@ -1,92 +1,91 @@ using UnityEngine; -namespace Sentry.Unity.Android +namespace Sentry.Unity.Android; + +/// +/// Scope Observer for Android through Java (JNI). +/// +/// +internal class AndroidJavaScopeObserver : ScopeObserver { - /// - /// Scope Observer for Android through Java (JNI). - /// - /// - internal class AndroidJavaScopeObserver : ScopeObserver - { - private readonly IJniExecutor _jniExecutor; + private readonly IJniExecutor _jniExecutor; - public AndroidJavaScopeObserver(SentryOptions options, IJniExecutor jniExecutor) : base("Android", options) - { - _jniExecutor = jniExecutor; - } + public AndroidJavaScopeObserver(SentryOptions options, IJniExecutor jniExecutor) : base("Android", options) + { + _jniExecutor = jniExecutor; + } - private AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); + private AndroidJavaObject GetSentryJava() => new AndroidJavaClass("io.sentry.Sentry"); - public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) + public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) + { + _jniExecutor.Run(() => { - _jniExecutor.Run(() => - { - using var sentry = GetSentryJava(); - using var javaBreadcrumb = new AndroidJavaObject("io.sentry.Breadcrumb"); - javaBreadcrumb.Set("message", breadcrumb.Message); - javaBreadcrumb.Set("type", breadcrumb.Type); - javaBreadcrumb.Set("category", breadcrumb.Category); - using var javaLevel = breadcrumb.Level.ToJavaSentryLevel(); - javaBreadcrumb.Set("level", javaLevel); - sentry.CallStatic("addBreadcrumb", javaBreadcrumb, null); - }); - } + using var sentry = GetSentryJava(); + using var javaBreadcrumb = new AndroidJavaObject("io.sentry.Breadcrumb"); + javaBreadcrumb.Set("message", breadcrumb.Message); + javaBreadcrumb.Set("type", breadcrumb.Type); + javaBreadcrumb.Set("category", breadcrumb.Category); + using var javaLevel = breadcrumb.Level.ToJavaSentryLevel(); + javaBreadcrumb.Set("level", javaLevel); + sentry.CallStatic("addBreadcrumb", javaBreadcrumb, null); + }); + } - public override void SetExtraImpl(string key, string? value) + public override void SetExtraImpl(string key, string? value) + { + _jniExecutor.Run(() => { - _jniExecutor.Run(() => - { - using var sentry = GetSentryJava(); - sentry.CallStatic("setExtra", key, value); - }); - } - public override void SetTagImpl(string key, string value) + using var sentry = GetSentryJava(); + sentry.CallStatic("setExtra", key, value); + }); + } + public override void SetTagImpl(string key, string value) + { + _jniExecutor.Run(() => { - _jniExecutor.Run(() => - { - using var sentry = GetSentryJava(); - sentry.CallStatic("setTag", key, value); - }); - } + using var sentry = GetSentryJava(); + sentry.CallStatic("setTag", key, value); + }); + } - public override void UnsetTagImpl(string key) + public override void UnsetTagImpl(string key) + { + _jniExecutor.Run(() => { - _jniExecutor.Run(() => - { - using var sentry = GetSentryJava(); - sentry.CallStatic("removeTag", key); - }); - } + using var sentry = GetSentryJava(); + sentry.CallStatic("removeTag", key); + }); + } - public override void SetUserImpl(SentryUser user) + public override void SetUserImpl(SentryUser user) + { + _jniExecutor.Run(() => { - _jniExecutor.Run(() => + AndroidJavaObject? javaUser = null; + try + { + javaUser = new AndroidJavaObject("io.sentry.protocol.User"); + javaUser.Set("email", user.Email); + javaUser.Set("id", user.Id); + javaUser.Set("username", user.Username); + javaUser.Set("ipAddress", user.IpAddress); + using var sentry = GetSentryJava(); + sentry.CallStatic("setUser", javaUser); + } + finally { - AndroidJavaObject? javaUser = null; - try - { - javaUser = new AndroidJavaObject("io.sentry.protocol.User"); - javaUser.Set("email", user.Email); - javaUser.Set("id", user.Id); - javaUser.Set("username", user.Username); - javaUser.Set("ipAddress", user.IpAddress); - using var sentry = GetSentryJava(); - sentry.CallStatic("setUser", javaUser); - } - finally - { - javaUser?.Dispose(); - } - }); - } + javaUser?.Dispose(); + } + }); + } - public override void UnsetUserImpl() + public override void UnsetUserImpl() + { + _jniExecutor.Run(() => { - _jniExecutor.Run(() => - { - using var sentry = GetSentryJava(); - sentry.CallStatic("setUser", null); - }); - } + using var sentry = GetSentryJava(); + sentry.CallStatic("setUser", null); + }); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Android/BreadcrumbExtensions.cs b/src/Sentry.Unity.Android/BreadcrumbExtensions.cs index b57bfcf44..38ce84bbd 100644 --- a/src/Sentry.Unity.Android/BreadcrumbExtensions.cs +++ b/src/Sentry.Unity.Android/BreadcrumbExtensions.cs @@ -1,29 +1,28 @@ using UnityEngine; -namespace Sentry.Unity.Android +namespace Sentry.Unity.Android; + +/// +/// Extension methods to Breadcrumb. +/// +public static class BreadcrumbExtensions { /// - /// Extension methods to Breadcrumb. + /// To Java SentryLevel. /// - public static class BreadcrumbExtensions + /// The Breadcrumb level to convert to Java level. + /// An Android Java object representing the SentryLevel. + public static AndroidJavaObject ToJavaSentryLevel(this BreadcrumbLevel level) { - /// - /// To Java SentryLevel. - /// - /// The Breadcrumb level to convert to Java level. - /// An Android Java object representing the SentryLevel. - public static AndroidJavaObject ToJavaSentryLevel(this BreadcrumbLevel level) + using var javaSentryLevel = new AndroidJavaClass("io.sentry.SentryLevel"); + return level switch { - using var javaSentryLevel = new AndroidJavaClass("io.sentry.SentryLevel"); - return level switch - { - BreadcrumbLevel.Critical => javaSentryLevel.GetStatic("FATAL"), - BreadcrumbLevel.Error => javaSentryLevel.GetStatic("ERROR"), - BreadcrumbLevel.Warning => javaSentryLevel.GetStatic("WARNING"), - BreadcrumbLevel.Debug => javaSentryLevel.GetStatic("DEBUG"), - // BreadcrumbLevel.Info or unknown: - _ => javaSentryLevel.GetStatic("INFO") - }; - } + BreadcrumbLevel.Critical => javaSentryLevel.GetStatic("FATAL"), + BreadcrumbLevel.Error => javaSentryLevel.GetStatic("ERROR"), + BreadcrumbLevel.Warning => javaSentryLevel.GetStatic("WARNING"), + BreadcrumbLevel.Debug => javaSentryLevel.GetStatic("DEBUG"), + // BreadcrumbLevel.Info or unknown: + _ => javaSentryLevel.GetStatic("INFO") + }; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Android/IJniExecutor.cs b/src/Sentry.Unity.Android/IJniExecutor.cs index 4eb8c8f9d..02076b552 100644 --- a/src/Sentry.Unity.Android/IJniExecutor.cs +++ b/src/Sentry.Unity.Android/IJniExecutor.cs @@ -1,10 +1,9 @@ using System; -namespace Sentry.Unity.Android +namespace Sentry.Unity.Android; + +internal interface IJniExecutor : IDisposable { - internal interface IJniExecutor : IDisposable - { - public TResult? Run(Func jniOperation); - public void Run(Action jniOperation); - } -} + public TResult? Run(Func jniOperation); + public void Run(Action jniOperation); +} \ No newline at end of file diff --git a/src/Sentry.Unity.Android/JniExecutor.cs b/src/Sentry.Unity.Android/JniExecutor.cs index 7bd97bcbc..c02f4d914 100644 --- a/src/Sentry.Unity.Android/JniExecutor.cs +++ b/src/Sentry.Unity.Android/JniExecutor.cs @@ -3,122 +3,121 @@ using System.Threading.Tasks; using UnityEngine; -namespace Sentry.Unity.Android +namespace Sentry.Unity.Android; + +internal class JniExecutor : IJniExecutor { - internal class JniExecutor : IJniExecutor - { - private readonly CancellationTokenSource _shutdownSource; - private readonly AutoResetEvent _taskEvent; - private Delegate _currentTask = null!; // The current task will always be set together with the task event + private readonly CancellationTokenSource _shutdownSource; + private readonly AutoResetEvent _taskEvent; + private Delegate _currentTask = null!; // The current task will always be set together with the task event - private TaskCompletionSource? _taskCompletionSource; + private TaskCompletionSource? _taskCompletionSource; - private readonly object _lock = new object(); + private readonly object _lock = new object(); - public JniExecutor() - { - _taskEvent = new AutoResetEvent(false); - _shutdownSource = new CancellationTokenSource(); + public JniExecutor() + { + _taskEvent = new AutoResetEvent(false); + _shutdownSource = new CancellationTokenSource(); - new Thread(DoWork) { IsBackground = true, Name = "SentryJniExecutorThread" }.Start(); - } + new Thread(DoWork) { IsBackground = true, Name = "SentryJniExecutorThread" }.Start(); + } - private void DoWork() - { - AndroidJNI.AttachCurrentThread(); + private void DoWork() + { + AndroidJNI.AttachCurrentThread(); - var waitHandles = new[] { _taskEvent, _shutdownSource.Token.WaitHandle }; + var waitHandles = new[] { _taskEvent, _shutdownSource.Token.WaitHandle }; - while (true) + while (true) + { + var index = WaitHandle.WaitAny(waitHandles); + if (index > 0) { - var index = WaitHandle.WaitAny(waitHandles); - if (index > 0) - { - // We only care about the _taskEvent - break; - } + // We only care about the _taskEvent + break; + } - try + try + { + // Matching the type of the `_currentTask` exactly. The result gets cast to the expected type + // when returning from the blocking call. + switch (_currentTask) { - // Matching the type of the `_currentTask` exactly. The result gets cast to the expected type - // when returning from the blocking call. - switch (_currentTask) + case Action action: { - case Action action: - { - action.Invoke(); - _taskCompletionSource?.SetResult(null); - break; - } - case Func func1: - { - var result = func1.Invoke(); - _taskCompletionSource?.SetResult(result); - break; - } - case Func func2: - { - var result = func2.Invoke(); - _taskCompletionSource?.SetResult(result); - break; - } - default: - throw new ArgumentException("Invalid type for _currentTask."); + action.Invoke(); + _taskCompletionSource?.SetResult(null); + break; } - } - catch (Exception e) - { - Debug.unityLogger.Log(LogType.Exception, UnityLogger.LogTag, $"Error during JNI execution: {e}"); + case Func func1: + { + var result = func1.Invoke(); + _taskCompletionSource?.SetResult(result); + break; + } + case Func func2: + { + var result = func2.Invoke(); + _taskCompletionSource?.SetResult(result); + break; + } + default: + throw new ArgumentException("Invalid type for _currentTask."); } } - - AndroidJNI.DetachCurrentThread(); + catch (Exception e) + { + Debug.unityLogger.Log(LogType.Exception, UnityLogger.LogTag, $"Error during JNI execution: {e}"); + } } - public TResult? Run(Func jniOperation) - { - lock (_lock) - { - _taskCompletionSource = new TaskCompletionSource(); - _currentTask = jniOperation; - _taskEvent.Set(); + AndroidJNI.DetachCurrentThread(); + } - try - { - return (TResult?)_taskCompletionSource.Task.GetAwaiter().GetResult(); - } - catch (Exception e) - { - Debug.unityLogger.Log(LogType.Exception, UnityLogger.LogTag, $"Error during JNI execution: {e}"); - } + public TResult? Run(Func jniOperation) + { + lock (_lock) + { + _taskCompletionSource = new TaskCompletionSource(); + _currentTask = jniOperation; + _taskEvent.Set(); - return default; + try + { + return (TResult?)_taskCompletionSource.Task.GetAwaiter().GetResult(); + } + catch (Exception e) + { + Debug.unityLogger.Log(LogType.Exception, UnityLogger.LogTag, $"Error during JNI execution: {e}"); } + + return default; } + } - public void Run(Action jniOperation) + public void Run(Action jniOperation) + { + lock (_lock) { - lock (_lock) - { - _taskCompletionSource = new TaskCompletionSource(); - _currentTask = jniOperation; - _taskEvent.Set(); + _taskCompletionSource = new TaskCompletionSource(); + _currentTask = jniOperation; + _taskEvent.Set(); - try - { - _taskCompletionSource.Task.Wait(); - } - catch (Exception e) - { - Debug.unityLogger.Log(LogType.Exception, UnityLogger.LogTag, $"Error during JNI execution: {e}"); - } + try + { + _taskCompletionSource.Task.Wait(); + } + catch (Exception e) + { + Debug.unityLogger.Log(LogType.Exception, UnityLogger.LogTag, $"Error during JNI execution: {e}"); } } + } - public void Dispose() - { - _shutdownSource.Cancel(); - _taskEvent.Dispose(); - } + public void Dispose() + { + _shutdownSource.Cancel(); + _taskEvent.Dispose(); } } diff --git a/src/Sentry.Unity.Android/NativeContextWriter.cs b/src/Sentry.Unity.Android/NativeContextWriter.cs index 3b77bb175..e5fca4959 100644 --- a/src/Sentry.Unity.Android/NativeContextWriter.cs +++ b/src/Sentry.Unity.Android/NativeContextWriter.cs @@ -1,98 +1,97 @@ using CWUtil = Sentry.Unity.NativeUtils.ContextWriter; -namespace Sentry.Unity.Android +namespace Sentry.Unity.Android; + +internal class NativeContextWriter : ContextWriter { - internal class NativeContextWriter : ContextWriter - { - private readonly IJniExecutor _jniExecutor; - private readonly ISentryJava _sentryJava; + private readonly IJniExecutor _jniExecutor; + private readonly ISentryJava _sentryJava; - public NativeContextWriter(IJniExecutor jniExecutor, ISentryJava sentryJava) - { - _jniExecutor = jniExecutor; - _sentryJava = sentryJava; - } + public NativeContextWriter(IJniExecutor jniExecutor, ISentryJava sentryJava) + { + _jniExecutor = jniExecutor; + _sentryJava = sentryJava; + } - protected override void WriteScope( - string? AppStartTime, - string? AppBuildType, - string? OperatingSystemRawDescription, - int? DeviceProcessorCount, - string? DeviceCpuDescription, - string? DeviceTimezone, - bool? DeviceSupportsVibration, - string? DeviceName, - bool? DeviceSimulator, - string? DeviceDeviceUniqueIdentifier, - string? DeviceDeviceType, - string? DeviceModel, - long? DeviceMemorySize, - int? GpuId, - string? GpuName, - string? GpuVendorName, - int? GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int? GpuMaxTextureSize, - bool? GpuSupportsDrawCallInstancing, - bool? GpuSupportsRayTracing, - bool? GpuSupportsComputeShaders, - bool? GpuSupportsGeometryShaders, - string? GpuVendorId, - bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel, - string? EditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode - ) - { - // We're only setting the missing contexts, the rest is configured by sentry-java. We could also sync - // the "unity" context, but it doesn't seem so useful and the effort to do is larger because there's no - // class for it in Java - not sure how we could add a generic context object in Java... - _sentryJava.WriteScope( - _jniExecutor, - GpuId, - GpuName, - GpuVendorName, - GpuMemorySize, - GpuNpotSupport, - GpuVersion, - GpuApiType, - GpuMaxTextureSize, - GpuSupportsDrawCallInstancing, - GpuSupportsRayTracing, - GpuSupportsComputeShaders, - GpuSupportsGeometryShaders, - GpuVendorId, - GpuMultiThreadedRendering, - GpuGraphicsShaderLevel); + protected override void WriteScope( + string? AppStartTime, + string? AppBuildType, + string? OperatingSystemRawDescription, + int? DeviceProcessorCount, + string? DeviceCpuDescription, + string? DeviceTimezone, + bool? DeviceSupportsVibration, + string? DeviceName, + bool? DeviceSimulator, + string? DeviceDeviceUniqueIdentifier, + string? DeviceDeviceType, + string? DeviceModel, + long? DeviceMemorySize, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel, + string? EditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode + ) + { + // We're only setting the missing contexts, the rest is configured by sentry-java. We could also sync + // the "unity" context, but it doesn't seem so useful and the effort to do is larger because there's no + // class for it in Java - not sure how we could add a generic context object in Java... + _sentryJava.WriteScope( + _jniExecutor, + GpuId, + GpuName, + GpuVendorName, + GpuMemorySize, + GpuNpotSupport, + GpuVersion, + GpuApiType, + GpuMaxTextureSize, + GpuSupportsDrawCallInstancing, + GpuSupportsRayTracing, + GpuSupportsComputeShaders, + GpuSupportsGeometryShaders, + GpuVendorId, + GpuMultiThreadedRendering, + GpuGraphicsShaderLevel); - CWUtil.WriteGpu( - GpuId, - GpuName, - GpuVendorName, - GpuMemorySize, - GpuNpotSupport, - GpuVersion, - GpuApiType, - GpuMaxTextureSize, - GpuSupportsDrawCallInstancing, - GpuSupportsRayTracing, - GpuSupportsComputeShaders, - GpuSupportsGeometryShaders, - GpuVendorId, - GpuMultiThreadedRendering, - GpuGraphicsShaderLevel); + CWUtil.WriteGpu( + GpuId, + GpuName, + GpuVendorName, + GpuMemorySize, + GpuNpotSupport, + GpuVersion, + GpuApiType, + GpuMaxTextureSize, + GpuSupportsDrawCallInstancing, + GpuSupportsRayTracing, + GpuSupportsComputeShaders, + GpuSupportsGeometryShaders, + GpuVendorId, + GpuMultiThreadedRendering, + GpuGraphicsShaderLevel); - CWUtil.WriteUnity( - EditorVersion, - UnityInstallMode, - UnityTargetFrameRate, - UnityCopyTextureSupport, - UnityRenderingThreadingMode); - } + CWUtil.WriteUnity( + EditorVersion, + UnityInstallMode, + UnityTargetFrameRate, + UnityCopyTextureSupport, + UnityRenderingThreadingMode); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Android/SentryNative.cs b/src/Sentry.Unity.Android/SentryNative.cs index 507fd4a06..ee1d96d02 100644 --- a/src/Sentry.Unity.Android/SentryNative.cs +++ b/src/Sentry.Unity.Android/SentryNative.cs @@ -1,36 +1,35 @@ using System; using System.Runtime.InteropServices; -namespace Sentry.Unity.Android +namespace Sentry.Unity.Android; + +/// +/// P/Invoke to `sentry-native` functions. +/// +/// +/// The `sentry-native` SDK on Android is brought in through the `sentry-android-ndk` +/// maven package. +/// +/// +/// +public static class SentryNative { /// - /// P/Invoke to `sentry-native` functions. + /// Re-installs the sentry-native backend essentially retaking the signal handlers. + /// + /// + /// Sentry's Android SDK initializes before Unity. But once Unity initializes, it takes the signal handler + /// and does not forward the call to the original handler (sentry) before shutting down. + /// This results in a crash captured by the Sentry Android SDK (Java/ART) layer captured crash report + /// containing a tombstone, without any of the scope data such as tags set through + /// Sentry SDKs C# -> Java -> C /// - /// - /// The `sentry-native` SDK on Android is brought in through the `sentry-android-ndk` - /// maven package. - /// - /// - /// - public static class SentryNative - { - /// - /// Re-installs the sentry-native backend essentially retaking the signal handlers. - /// - /// - /// Sentry's Android SDK initializes before Unity. But once Unity initializes, it takes the signal handler - /// and does not forward the call to the original handler (sentry) before shutting down. - /// This results in a crash captured by the Sentry Android SDK (Java/ART) layer captured crash report - /// containing a tombstone, without any of the scope data such as tags set through - /// Sentry SDKs C# -> Java -> C - /// - public static void ReinstallBackend() => ReinstallSentryNativeBackendStrategy(); + public static void ReinstallBackend() => ReinstallSentryNativeBackendStrategy(); - // libsentry.io - [DllImport("sentry")] - private static extern void sentry_reinstall_backend(); + // libsentry.io + [DllImport("sentry")] + private static extern void sentry_reinstall_backend(); - // Testing - internal static Action ReinstallSentryNativeBackendStrategy = sentry_reinstall_backend; - } + // Testing + internal static Action ReinstallSentryNativeBackendStrategy = sentry_reinstall_backend; } diff --git a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs index 6a9a5b31d..fe19e1893 100644 --- a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs @@ -7,191 +7,190 @@ using UnityEditor.Build; using UnityEditor.Callbacks; -namespace Sentry.Unity.Editor.iOS +namespace Sentry.Unity.Editor.iOS; + +public static class BuildPostProcess { - public static class BuildPostProcess + [PostProcessBuild(1)] + public static void OnPostProcessBuild(BuildTarget target, string pathToProject) { - [PostProcessBuild(1)] - public static void OnPostProcessBuild(BuildTarget target, string pathToProject) + if (target != BuildTarget.iOS) { - if (target != BuildTarget.iOS) - { - return; - } + return; + } - var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); - var logger = options?.DiagnosticLogger ?? new UnityLogger(new SentryUnityOptions()); + var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); + var logger = options?.DiagnosticLogger ?? new UnityLogger(new SentryUnityOptions()); - AddSentryToXcodeProject(options, cliOptions, logger, pathToProject); - } + AddSentryToXcodeProject(options, cliOptions, logger, pathToProject); + } - internal static bool IsNativeSupportEnabled(SentryUnityOptions options, IDiagnosticLogger logger) + internal static bool IsNativeSupportEnabled(SentryUnityOptions options, IDiagnosticLogger logger) + { + if (!options.IsValid()) { - if (!options.IsValid()) - { - logger.LogWarning("Sentry SDK has been disabled. There will be no iOS native support."); - return false; - } - - if (!options.IosNativeSupportEnabled) - { - logger.LogInfo("The iOS native support has been disabled through the options."); - return false; - } + logger.LogWarning("Sentry SDK has been disabled. There will be no iOS native support."); + return false; + } - return true; + if (!options.IosNativeSupportEnabled) + { + logger.LogInfo("The iOS native support has been disabled through the options."); + return false; } - internal static void AddSentryToXcodeProject(SentryUnityOptions? options, - SentryCliOptions? cliOptions, - IDiagnosticLogger logger, - string pathToProject) + return true; + } + + internal static void AddSentryToXcodeProject(SentryUnityOptions? options, + SentryCliOptions? cliOptions, + IDiagnosticLogger logger, + string pathToProject) + { + if (options is null) { - if (options is null) - { - logger.LogWarning("iOS native support disabled because Sentry has not been configured. " + - "You can do that through the editor: {0}", SentryWindow.EditorMenuPath); + logger.LogWarning("iOS native support disabled because Sentry has not been configured. " + + "You can do that through the editor: {0}", SentryWindow.EditorMenuPath); - SetupNoOpBridge(logger, pathToProject); - return; - } + SetupNoOpBridge(logger, pathToProject); + return; + } - if (!IsNativeSupportEnabled(options, logger)) + if (!IsNativeSupportEnabled(options, logger)) + { + var mainPath = Path.Combine(pathToProject, SentryXcodeProject.MainPath); + if (File.Exists(mainPath)) { - var mainPath = Path.Combine(pathToProject, SentryXcodeProject.MainPath); - if (File.Exists(mainPath)) + var main = File.ReadAllText(mainPath); + if (NativeMain.ContainsSentry(main, logger)) { - var main = File.ReadAllText(mainPath); - if (NativeMain.ContainsSentry(main, logger)) - { - throw new BuildFailedException( - "The iOS native support has been disabled but the exported project has been modified " + - "during a previous build. Select 'Replace' when exporting the project to create a clean project."); - } + throw new BuildFailedException( + "The iOS native support has been disabled but the exported project has been modified " + + "during a previous build. Select 'Replace' when exporting the project to create a clean project."); } - - SetupNoOpBridge(logger, pathToProject); - return; } - SetupSentry(options, cliOptions, logger, pathToProject); + SetupNoOpBridge(logger, pathToProject); + return; } - internal static void SetupNoOpBridge(IDiagnosticLogger logger, string pathToProject) + SetupSentry(options, cliOptions, logger, pathToProject); + } + + internal static void SetupNoOpBridge(IDiagnosticLogger logger, string pathToProject) + { + try { - try - { - logger.LogDebug("Copying the NoOp bride to the output project."); + logger.LogDebug("Copying the NoOp bride to the output project."); - // The Unity SDK expects the bridge to be there. The Xcode build will break during linking otherwise. - var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.NoOpBridgeName)); - CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger); + // The Unity SDK expects the bridge to be there. The Xcode build will break during linking otherwise. + var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.NoOpBridgeName)); + CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger); - using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject); - sentryXcodeProject.AddSentryNativeBridge(); - } - catch (Exception e) - { - logger.LogError(e, "Failed to add the Sentry NoOp bridge to the output project."); - } + using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject); + sentryXcodeProject.AddSentryNativeBridge(); } - - internal static void SetupSentry(SentryUnityOptions options, - SentryCliOptions? cliOptions, - IDiagnosticLogger logger, - string pathToProject) + catch (Exception e) { - logger.LogInfo("Attempting to add Sentry to the Xcode project."); + logger.LogError(e, "Failed to add the Sentry NoOp bridge to the output project."); + } + } - try - { - // The Sentry.xcframework ends in '~' to hide it from Unity. Otherwise, Unity tries to export it with the XCode build. - var frameworkPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.FrameworkName + "~")); - CopyFramework(frameworkPath, Path.Combine(pathToProject, "Frameworks", SentryXcodeProject.FrameworkName), logger); + internal static void SetupSentry(SentryUnityOptions options, + SentryCliOptions? cliOptions, + IDiagnosticLogger logger, + string pathToProject) + { + logger.LogInfo("Attempting to add Sentry to the Xcode project."); - var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.BridgeName)); - CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger); + try + { + // The Sentry.xcframework ends in '~' to hide it from Unity. Otherwise, Unity tries to export it with the XCode build. + var frameworkPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.FrameworkName + "~")); + CopyFramework(frameworkPath, Path.Combine(pathToProject, "Frameworks", SentryXcodeProject.FrameworkName), logger); - using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject, logger); - sentryXcodeProject.AddSentryFramework(); - sentryXcodeProject.AddSentryNativeBridge(); - sentryXcodeProject.AddNativeOptions(options, NativeOptions.CreateFile); - sentryXcodeProject.AddSentryToMain(options); + var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.BridgeName)); + CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger); - if (cliOptions != null && cliOptions.IsValid(logger, EditorUserBuildSettings.development)) - { - logger.LogInfo("Automatic symbol upload enabled. Adding script to build phase."); + using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject, logger); + sentryXcodeProject.AddSentryFramework(); + sentryXcodeProject.AddSentryNativeBridge(); + sentryXcodeProject.AddNativeOptions(options, NativeOptions.CreateFile); + sentryXcodeProject.AddSentryToMain(options); - SentryCli.CreateSentryProperties(pathToProject, cliOptions, options); - SentryCli.SetupSentryCli(pathToProject, RuntimePlatform.OSXEditor); - sentryXcodeProject.AddBuildPhaseSymbolUpload(cliOptions); - } - else if (options.Il2CppLineNumberSupportEnabled) - { - logger.LogWarning("The IL2CPP line number support requires the debug symbol upload to be enabled."); - } + if (cliOptions != null && cliOptions.IsValid(logger, EditorUserBuildSettings.development)) + { + logger.LogInfo("Automatic symbol upload enabled. Adding script to build phase."); + + SentryCli.CreateSentryProperties(pathToProject, cliOptions, options); + SentryCli.SetupSentryCli(pathToProject, RuntimePlatform.OSXEditor); + sentryXcodeProject.AddBuildPhaseSymbolUpload(cliOptions); } - catch (Exception e) + else if (options.Il2CppLineNumberSupportEnabled) { - logger.LogError(e, "Failed to add the Sentry framework to the generated Xcode project"); - return; + logger.LogWarning("The IL2CPP line number support requires the debug symbol upload to be enabled."); } - - logger.LogInfo("Successfully added Sentry to the Xcode project."); + } + catch (Exception e) + { + logger.LogError(e, "Failed to add the Sentry framework to the generated Xcode project"); + return; } - internal static void CopyFramework(string sourcePath, string targetPath, IDiagnosticLogger? logger) + logger.LogInfo("Successfully added Sentry to the Xcode project."); + } + + internal static void CopyFramework(string sourcePath, string targetPath, IDiagnosticLogger? logger) + { + if (!Directory.Exists(sourcePath)) { - if (!Directory.Exists(sourcePath)) - { - throw new DirectoryNotFoundException($"Failed to find '{sourcePath}'"); - } + throw new DirectoryNotFoundException($"Failed to find '{sourcePath}'"); + } - if (Directory.Exists(targetPath)) - { - logger?.LogDebug("'{0}' has already been copied to '{1}'", Path.GetFileName(targetPath), targetPath); - return; - } + if (Directory.Exists(targetPath)) + { + logger?.LogDebug("'{0}' has already been copied to '{1}'", Path.GetFileName(targetPath), targetPath); + return; + } - if (Directory.Exists(sourcePath)) - { - logger?.LogDebug("Copying from: '{0}' to '{1}'", sourcePath, targetPath); + if (Directory.Exists(sourcePath)) + { + logger?.LogDebug("Copying from: '{0}' to '{1}'", sourcePath, targetPath); - Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(targetPath))); - FileUtil.CopyFileOrDirectory(sourcePath, targetPath); - } + Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(targetPath))); + FileUtil.CopyFileOrDirectory(sourcePath, targetPath); + } - if (!Directory.Exists(targetPath)) - { - throw new DirectoryNotFoundException($"Failed to copy '{sourcePath}' to '{targetPath}'"); - } + if (!Directory.Exists(targetPath)) + { + throw new DirectoryNotFoundException($"Failed to copy '{sourcePath}' to '{targetPath}'"); } + } - internal static void CopyFile(string sourcePath, string targetPath, IDiagnosticLogger? logger) + internal static void CopyFile(string sourcePath, string targetPath, IDiagnosticLogger? logger) + { + if (!File.Exists(sourcePath)) { - if (!File.Exists(sourcePath)) - { - throw new FileNotFoundException($"Failed to find '{sourcePath}'"); - } + throw new FileNotFoundException($"Failed to find '{sourcePath}'"); + } - if (File.Exists(targetPath)) - { - logger?.LogDebug("'{0}' has already been copied to '{1}'", Path.GetFileName(targetPath), targetPath); - return; - } + if (File.Exists(targetPath)) + { + logger?.LogDebug("'{0}' has already been copied to '{1}'", Path.GetFileName(targetPath), targetPath); + return; + } - if (File.Exists(sourcePath)) - { - logger?.LogDebug("Copying from: '{0}' to '{1}'", sourcePath, targetPath); + if (File.Exists(sourcePath)) + { + logger?.LogDebug("Copying from: '{0}' to '{1}'", sourcePath, targetPath); - Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(targetPath))); - FileUtil.CopyFileOrDirectory(sourcePath, targetPath); - } + Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(targetPath))); + FileUtil.CopyFileOrDirectory(sourcePath, targetPath); + } - if (!File.Exists(targetPath)) - { - throw new FileNotFoundException($"Failed to copy '{sourcePath}' to '{targetPath}'"); - } + if (!File.Exists(targetPath)) + { + throw new FileNotFoundException($"Failed to copy '{sourcePath}' to '{targetPath}'"); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor.iOS/NativeMain.cs b/src/Sentry.Unity.Editor.iOS/NativeMain.cs index b3f990f34..28ece36be 100644 --- a/src/Sentry.Unity.Editor.iOS/NativeMain.cs +++ b/src/Sentry.Unity.Editor.iOS/NativeMain.cs @@ -3,14 +3,14 @@ using System.Text.RegularExpressions; using Sentry.Extensibility; -namespace Sentry.Unity.Editor.iOS +namespace Sentry.Unity.Editor.iOS; + +internal static class NativeMain { - internal static class NativeMain - { - public const string Include = @"#include + public const string Include = @"#include #include ""SentryOptions.m"" "; - private const string Init = @" + private const string Init = @" SentryOptions* options = getSentryOptions(); if(options != nil) { @@ -18,46 +18,45 @@ internal static class NativeMain } "; - public static void AddSentry(string pathToMain, IDiagnosticLogger? logger) + public static void AddSentry(string pathToMain, IDiagnosticLogger? logger) + { + if (!File.Exists(pathToMain)) { - if (!File.Exists(pathToMain)) - { - throw new FileNotFoundException("Could not find main.", pathToMain); - } - - var main = File.ReadAllText(pathToMain); - if (ContainsSentry(main, logger)) - { - return; - } - - var sentryMain = AddSentryToMain(main); - File.WriteAllText(pathToMain, sentryMain); + throw new FileNotFoundException("Could not find main.", pathToMain); } - internal static bool ContainsSentry(string main, IDiagnosticLogger? logger) + var main = File.ReadAllText(pathToMain); + if (ContainsSentry(main, logger)) { - if (main.Contains(Include)) - { - logger?.LogInfo("'main.mm' already contains Sentry."); - return true; - } - - return false; + return; } - internal static string AddSentryToMain(string main) + var sentryMain = AddSentryToMain(main); + File.WriteAllText(pathToMain, sentryMain); + } + + internal static bool ContainsSentry(string main, IDiagnosticLogger? logger) + { + if (main.Contains(Include)) { - main = main.Insert(0, Include); + logger?.LogInfo("'main.mm' already contains Sentry."); + return true; + } + + return false; + } - var initRegex = new Regex(@"int main\(int argc, char\* argv\[\]\)\s+{\s+@autoreleasepool\s+{"); - var match = initRegex.Match(main); - if (match.Success) - { - return main.Insert(match.Index + match.Length, Init); - } + internal static string AddSentryToMain(string main) + { + main = main.Insert(0, Include); - throw new ArgumentException($"Failed to add Sentry to main.\n{main}", nameof(main)); + var initRegex = new Regex(@"int main\(int argc, char\* argv\[\]\)\s+{\s+@autoreleasepool\s+{"); + var match = initRegex.Match(main); + if (match.Success) + { + return main.Insert(match.Index + match.Length, Init); } + + throw new ArgumentException($"Failed to add Sentry to main.\n{main}", nameof(main)); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs index 31245fdd4..74a9a16af 100644 --- a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs +++ b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs @@ -1,17 +1,16 @@ using System.Collections.Generic; using System.IO; -using System.Linq; -namespace Sentry.Unity.Editor.iOS +namespace Sentry.Unity.Editor.iOS; + +internal static class NativeOptions { - internal static class NativeOptions - { - public static void CreateFile(string path, SentryUnityOptions options) => File.WriteAllText(path, Generate(options)); + public static void CreateFile(string path, SentryUnityOptions options) => File.WriteAllText(path, Generate(options)); - internal static string Generate(SentryUnityOptions options) - { - var failedRequestStatusCodesArray = GetFailedRequestStatusCodesArray(options.FailedRequestStatusCodes); - var nativeOptions = $@"#import + internal static string Generate(SentryUnityOptions options) + { + var failedRequestStatusCodesArray = GetFailedRequestStatusCodesArray(options.FailedRequestStatusCodes); + var nativeOptions = $@"#import #import #import @@ -57,37 +56,36 @@ internal static string Generate(SentryUnityOptions options) return options; }}"; - return nativeOptions; - } + return nativeOptions; + } - private static string ToObjCString(bool b) => b ? "YES" : "NO"; + private static string ToObjCString(bool b) => b ? "YES" : "NO"; - private static string ToNativeDiagnosticLevel(SentryLevel sentryLevel) + private static string ToNativeDiagnosticLevel(SentryLevel sentryLevel) + { + return sentryLevel switch { - return sentryLevel switch - { - SentryLevel.Debug => "debug", - SentryLevel.Info => "info", - SentryLevel.Warning => "warning", - SentryLevel.Error => "error", - SentryLevel.Fatal => "fatal", - _ => "none" - }; - } + SentryLevel.Debug => "debug", + SentryLevel.Info => "info", + SentryLevel.Warning => "warning", + SentryLevel.Error => "error", + SentryLevel.Fatal => "fatal", + _ => "none" + }; + } - private static string GetFailedRequestStatusCodesArray(IList httpStatusCodeRanges) + private static string GetFailedRequestStatusCodesArray(IList httpStatusCodeRanges) + { + var codeRanges = string.Empty; + for (var i = 0; i < httpStatusCodeRanges.Count; i++) { - var codeRanges = string.Empty; - for (var i = 0; i < httpStatusCodeRanges.Count; i++) + codeRanges += $"[[SentryHttpStatusCodeRange alloc] initWithMin:{httpStatusCodeRanges[i].Start} max:{httpStatusCodeRanges[i].End}]"; + if (i < httpStatusCodeRanges.Count - 1) { - codeRanges += $"[[SentryHttpStatusCodeRange alloc] initWithMin:{httpStatusCodeRanges[i].Start} max:{httpStatusCodeRanges[i].End}]"; - if (i < httpStatusCodeRanges.Count - 1) - { - codeRanges += ", "; - } + codeRanges += ", "; } - - return codeRanges; } + + return codeRanges; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs b/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs index a98242c7d..8a686bd24 100644 --- a/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs +++ b/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs @@ -10,206 +10,205 @@ // using UnityEditor.iOS.Xcode; // using UnityEditor.iOS.Xcode.Extensions; -namespace Sentry.Unity.Editor.iOS +namespace Sentry.Unity.Editor.iOS; + +internal class SentryXcodeProject : IDisposable { - internal class SentryXcodeProject : IDisposable - { - internal const string FrameworkName = "Sentry.xcframework"; - internal const string BridgeName = "SentryNativeBridge.m"; - internal const string NoOpBridgeName = "SentryNativeBridgeNoOp.m"; - internal const string OptionsName = "SentryOptions.m"; - internal const string SymbolUploadPhaseName = "SymbolUpload"; - - internal static readonly string MainPath = Path.Combine("MainApp", "main.mm"); - private readonly string _optionsPath = Path.Combine("MainApp", OptionsName); - private readonly string _uploadScript = @" + internal const string FrameworkName = "Sentry.xcframework"; + internal const string BridgeName = "SentryNativeBridge.m"; + internal const string NoOpBridgeName = "SentryNativeBridgeNoOp.m"; + internal const string OptionsName = "SentryOptions.m"; + internal const string SymbolUploadPhaseName = "SymbolUpload"; + + internal static readonly string MainPath = Path.Combine("MainApp", "main.mm"); + private readonly string _optionsPath = Path.Combine("MainApp", OptionsName); + private readonly string _uploadScript = @" export SENTRY_PROPERTIES=sentry.properties echo ""Uploading debug symbols and bcsymbolmaps."" ./{0} debug-files upload --force-foreground {1} $BUILT_PRODUCTS_DIR 2>&1 | tee ./sentry-symbols-upload.log "; - private readonly IDiagnosticLogger? _logger = null; - private readonly Type _pbxProjectType = null!; // Set in constructor or throws - private readonly Type _pbxProjectExtensionsType = null!; // Set in constructor or throws - private readonly object _project = null!; // Set in constructor or throws + private readonly IDiagnosticLogger? _logger = null; + private readonly Type _pbxProjectType = null!; // Set in constructor or throws + private readonly Type _pbxProjectExtensionsType = null!; // Set in constructor or throws + private readonly object _project = null!; // Set in constructor or throws - private readonly string _projectRoot; - private readonly string _projectPath; + private readonly string _projectRoot; + private readonly string _projectPath; - private string _mainTargetGuid = null!; // Set when opening the project - private string _unityFrameworkTargetGuid = null!; // Set when opening the project + private string _mainTargetGuid = null!; // Set when opening the project + private string _unityFrameworkTargetGuid = null!; // Set when opening the project - internal SentryXcodeProject(string projectRoot, IDiagnosticLogger? logger = null) - { - _logger = logger; - var xcodeAssembly = Assembly.Load("UnityEditor.iOS.Extensions.Xcode"); - _pbxProjectType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.PBXProject"); - _pbxProjectExtensionsType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.Extensions.PBXProjectExtensions"); + internal SentryXcodeProject(string projectRoot, IDiagnosticLogger? logger = null) + { + _logger = logger; + var xcodeAssembly = Assembly.Load("UnityEditor.iOS.Extensions.Xcode"); + _pbxProjectType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.PBXProject"); + _pbxProjectExtensionsType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.Extensions.PBXProjectExtensions"); - _project = Activator.CreateInstance(_pbxProjectType); + _project = Activator.CreateInstance(_pbxProjectType); - _projectRoot = projectRoot; - _projectPath = (string)_pbxProjectType.GetMethod("GetPBXProjectPath", BindingFlags.Public | BindingFlags.Static) - .Invoke(null, new[] { _projectRoot }); - } + _projectRoot = projectRoot; + _projectPath = (string)_pbxProjectType.GetMethod("GetPBXProjectPath", BindingFlags.Public | BindingFlags.Static) + .Invoke(null, new[] { _projectRoot }); + } - public static SentryXcodeProject Open(string path, IDiagnosticLogger? logger = null) - { - var xcodeProject = new SentryXcodeProject(path, logger); - xcodeProject.ReadFromProjectFile(); + public static SentryXcodeProject Open(string path, IDiagnosticLogger? logger = null) + { + var xcodeProject = new SentryXcodeProject(path, logger); + xcodeProject.ReadFromProjectFile(); - return xcodeProject; - } + return xcodeProject; + } - internal void ReadFromProjectFile() - { - _logger?.LogInfo("Reading the Xcode project file."); + internal void ReadFromProjectFile() + { + _logger?.LogInfo("Reading the Xcode project file."); - if (!File.Exists(_projectPath)) - { - throw new FileNotFoundException("Could not locate generated Xcode project at", _projectPath); - } + if (!File.Exists(_projectPath)) + { + throw new FileNotFoundException("Could not locate generated Xcode project at", _projectPath); + } - _pbxProjectType.GetMethod("ReadFromString", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new[] { File.ReadAllText(_projectPath) }); - _mainTargetGuid = (string)_pbxProjectType.GetMethod("GetUnityMainTargetGuid", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, null); - _unityFrameworkTargetGuid = (string)_pbxProjectType.GetMethod("GetUnityFrameworkTargetGuid", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, null); + _pbxProjectType.GetMethod("ReadFromString", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new[] { File.ReadAllText(_projectPath) }); + _mainTargetGuid = (string)_pbxProjectType.GetMethod("GetUnityMainTargetGuid", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, null); + _unityFrameworkTargetGuid = (string)_pbxProjectType.GetMethod("GetUnityFrameworkTargetGuid", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, null); - _logger?.LogDebug("Successfully read the Xcode project file."); - } + _logger?.LogDebug("Successfully read the Xcode project file."); + } - public void AddSentryFramework() - { - _logger?.LogInfo("Adding the build properties and flags."); - AddBuildProperties(); + public void AddSentryFramework() + { + _logger?.LogInfo("Adding the build properties and flags."); + AddBuildProperties(); - var relativeFrameworkPath = Path.Combine("Frameworks", FrameworkName); + var relativeFrameworkPath = Path.Combine("Frameworks", FrameworkName); - var hasFileMethod = _pbxProjectType.GetMethod("ContainsFileByProjectPath", new[] { typeof(string) }); - if (hasFileMethod != null) + var hasFileMethod = _pbxProjectType.GetMethod("ContainsFileByProjectPath", new[] { typeof(string) }); + if (hasFileMethod != null) + { + var hasAddedFramework = (bool)hasFileMethod.Invoke(_project, new object[] { relativeFrameworkPath }); + if (hasAddedFramework) { - var hasAddedFramework = (bool)hasFileMethod.Invoke(_project, new object[] { relativeFrameworkPath }); - if (hasAddedFramework) - { - _logger?.LogDebug("Skipping. The Sentry framework has already been added to the project."); - return; - } + _logger?.LogDebug("Skipping. The Sentry framework has already been added to the project."); + return; } + } - _logger?.LogInfo("Adding the Sentry framework."); + _logger?.LogInfo("Adding the Sentry framework."); - var frameworkGuid = (string)_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new object[] { relativeFrameworkPath, relativeFrameworkPath, 1 }); // 1 is PBXSourceTree.Source + var frameworkGuid = (string)_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new object[] { relativeFrameworkPath, relativeFrameworkPath, 1 }); // 1 is PBXSourceTree.Source - // Embedding the framework because it's dynamic and needed at runtime - _pbxProjectExtensionsType.GetMethod("AddFileToEmbedFrameworks", BindingFlags.Public | BindingFlags.Static) - .Invoke(null, new object?[] { _project, _mainTargetGuid, frameworkGuid, null }); + // Embedding the framework because it's dynamic and needed at runtime + _pbxProjectExtensionsType.GetMethod("AddFileToEmbedFrameworks", BindingFlags.Public | BindingFlags.Static) + .Invoke(null, new object?[] { _project, _mainTargetGuid, frameworkGuid, null }); - // Getting the Link With Binary phase - var getBuildPhaseMethod = _pbxProjectType.GetMethod("GetFrameworksBuildPhaseByTarget", new[] { typeof(string) }); - var mainBuildPhaseGuid = (string)getBuildPhaseMethod.Invoke(_project, new object[] { _mainTargetGuid }); - var unityFrameworkBuildPhaseGuid = (string)getBuildPhaseMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid }); + // Getting the Link With Binary phase + var getBuildPhaseMethod = _pbxProjectType.GetMethod("GetFrameworksBuildPhaseByTarget", new[] { typeof(string) }); + var mainBuildPhaseGuid = (string)getBuildPhaseMethod.Invoke(_project, new object[] { _mainTargetGuid }); + var unityFrameworkBuildPhaseGuid = (string)getBuildPhaseMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid }); - // Linking With Binary - var addFileToBuildSectionMethod = _pbxProjectType.GetMethod("AddFileToBuildSection", new[] { typeof(string), typeof(string), typeof(string) }); - addFileToBuildSectionMethod.Invoke(_project, new object[] { _mainTargetGuid, mainBuildPhaseGuid, frameworkGuid }); - addFileToBuildSectionMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid, unityFrameworkBuildPhaseGuid, frameworkGuid }); + // Linking With Binary + var addFileToBuildSectionMethod = _pbxProjectType.GetMethod("AddFileToBuildSection", new[] { typeof(string), typeof(string), typeof(string) }); + addFileToBuildSectionMethod.Invoke(_project, new object[] { _mainTargetGuid, mainBuildPhaseGuid, frameworkGuid }); + addFileToBuildSectionMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid, unityFrameworkBuildPhaseGuid, frameworkGuid }); - _logger?.LogDebug("Successfully added the Sentry framework."); - } + _logger?.LogDebug("Successfully added the Sentry framework."); + } - private void AddBuildProperties() - { - SetSearchPathBuildProperty("$(inherited)"); - SetSearchPathBuildProperty("$(PROJECT_DIR)/Frameworks/"); + private void AddBuildProperties() + { + SetSearchPathBuildProperty("$(inherited)"); + SetSearchPathBuildProperty("$(PROJECT_DIR)/Frameworks/"); - var setBuildPropertyMethod = _pbxProjectType.GetMethod("SetBuildProperty", new[] { typeof(string), typeof(string), typeof(string) }); - setBuildPropertyMethod.Invoke(_project, new object[] { _mainTargetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" }); - setBuildPropertyMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" }); + var setBuildPropertyMethod = _pbxProjectType.GetMethod("SetBuildProperty", new[] { typeof(string), typeof(string), typeof(string) }); + setBuildPropertyMethod.Invoke(_project, new object[] { _mainTargetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" }); + setBuildPropertyMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym" }); - _pbxProjectType.GetMethod("AddBuildProperty", new[] { typeof(string), typeof(string), typeof(string) }) - .Invoke(_project, new object[] { _mainTargetGuid, "OTHER_LDFLAGS", "-ObjC" }); - } + _pbxProjectType.GetMethod("AddBuildProperty", new[] { typeof(string), typeof(string), typeof(string) }) + .Invoke(_project, new object[] { _mainTargetGuid, "OTHER_LDFLAGS", "-ObjC" }); + } - public void AddSentryNativeBridge() - { - _logger?.LogInfo("Adding the Sentry Native Bridge."); + public void AddSentryNativeBridge() + { + _logger?.LogInfo("Adding the Sentry Native Bridge."); - var relativeBridgePath = Path.Combine("Libraries", SentryPackageInfo.GetName(), BridgeName); - var bridgeGuid = (string)_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new object[] { relativeBridgePath, relativeBridgePath, 1 }); // 1 is PBXSourceTree.Source + var relativeBridgePath = Path.Combine("Libraries", SentryPackageInfo.GetName(), BridgeName); + var bridgeGuid = (string)_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new object[] { relativeBridgePath, relativeBridgePath, 1 }); // 1 is PBXSourceTree.Source - _pbxProjectType.GetMethod("AddFileToBuild", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new[] { _unityFrameworkTargetGuid, bridgeGuid }); + _pbxProjectType.GetMethod("AddFileToBuild", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new[] { _unityFrameworkTargetGuid, bridgeGuid }); - _logger?.LogDebug("Successfully added the Sentry Native Bridge."); - } + _logger?.LogDebug("Successfully added the Sentry Native Bridge."); + } + + // Used for testing + internal void SetSearchPathBuildProperty(string path) + { + _pbxProjectType.GetMethod("AddBuildProperty", new[] { typeof(string[]), typeof(string), typeof(string) }) + .Invoke(_project, new object[] { new[] { _mainTargetGuid, _unityFrameworkTargetGuid }, "FRAMEWORK_SEARCH_PATHS", path }); + } - // Used for testing - internal void SetSearchPathBuildProperty(string path) + public void AddBuildPhaseSymbolUpload(SentryCliOptions sentryCliOptions) + { + _logger?.LogInfo("Adding automated debug symbol upload script to build phase."); + + if (MainTargetContainsSymbolUploadBuildPhase()) { - _pbxProjectType.GetMethod("AddBuildProperty", new[] { typeof(string[]), typeof(string), typeof(string) }) - .Invoke(_project, new object[] { new[] { _mainTargetGuid, _unityFrameworkTargetGuid }, "FRAMEWORK_SEARCH_PATHS", path }); + _logger?.LogDebug("Build phase '{0}' already added.", SymbolUploadPhaseName); + return; } - public void AddBuildPhaseSymbolUpload(SentryCliOptions sentryCliOptions) + var uploadDifArguments = "--il2cpp-mapping"; + if (sentryCliOptions.UploadSources) { - _logger?.LogInfo("Adding automated debug symbol upload script to build phase."); - - if (MainTargetContainsSymbolUploadBuildPhase()) - { - _logger?.LogDebug("Build phase '{0}' already added.", SymbolUploadPhaseName); - return; - } + uploadDifArguments += " --include-sources"; + } + var uploadScript = string.Format(_uploadScript, SentryCli.SentryCliMacOS, uploadDifArguments); - var uploadDifArguments = "--il2cpp-mapping"; - if (sentryCliOptions.UploadSources) - { - uploadDifArguments += " --include-sources"; - } - var uploadScript = string.Format(_uploadScript, SentryCli.SentryCliMacOS, uploadDifArguments); + _pbxProjectType.GetMethod("AddShellScriptBuildPhase", new[] { typeof(string), typeof(string), typeof(string), typeof(string) }) + .Invoke(_project, new object[] { _mainTargetGuid, SymbolUploadPhaseName, "/bin/sh", uploadScript }); - _pbxProjectType.GetMethod("AddShellScriptBuildPhase", new[] { typeof(string), typeof(string), typeof(string), typeof(string) }) - .Invoke(_project, new object[] { _mainTargetGuid, SymbolUploadPhaseName, "/bin/sh", uploadScript }); + _logger?.LogDebug("Successfully added automated debug symbol upload script to build phase."); + } - _logger?.LogDebug("Successfully added automated debug symbol upload script to build phase."); - } + public void AddNativeOptions(SentryUnityOptions options, Action nativeOptionFileCreation) + { + _logger?.LogInfo("Adding native options."); - public void AddNativeOptions(SentryUnityOptions options, Action nativeOptionFileCreation) - { - _logger?.LogInfo("Adding native options."); + nativeOptionFileCreation.Invoke(Path.Combine(_projectRoot, _optionsPath), options); + _pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new object[] { _optionsPath, _optionsPath, 1 }); // 1 is PBXSourceTree.Source - nativeOptionFileCreation.Invoke(Path.Combine(_projectRoot, _optionsPath), options); - _pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new object[] { _optionsPath, _optionsPath, 1 }); // 1 is PBXSourceTree.Source + _logger?.LogDebug("Successfully added native options."); + } - _logger?.LogDebug("Successfully added native options."); - } + public void AddSentryToMain(SentryUnityOptions options) => + NativeMain.AddSentry(Path.Combine(_projectRoot, MainPath), options.DiagnosticLogger); - public void AddSentryToMain(SentryUnityOptions options) => - NativeMain.AddSentry(Path.Combine(_projectRoot, MainPath), options.DiagnosticLogger); + internal bool MainTargetContainsSymbolUploadBuildPhase() + { + var allBuildPhases = (string[])_pbxProjectType.GetMethod("GetAllBuildPhasesForTarget", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new object[] { _mainTargetGuid }); + var getBuildPhaseNameMethod = _pbxProjectType.GetMethod("GetBuildPhaseName", BindingFlags.Public | BindingFlags.Instance); - internal bool MainTargetContainsSymbolUploadBuildPhase() + return allBuildPhases.Any(buildPhase => { - var allBuildPhases = (string[])_pbxProjectType.GetMethod("GetAllBuildPhasesForTarget", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new object[] { _mainTargetGuid }); - var getBuildPhaseNameMethod = _pbxProjectType.GetMethod("GetBuildPhaseName", BindingFlags.Public | BindingFlags.Instance); - - return allBuildPhases.Any(buildPhase => - { - var buildPhaseName = (string)getBuildPhaseNameMethod.Invoke(_project, new[] { buildPhase }); - return buildPhaseName == SymbolUploadPhaseName; - }); - } + var buildPhaseName = (string)getBuildPhaseNameMethod.Invoke(_project, new[] { buildPhase }); + return buildPhaseName == SymbolUploadPhaseName; + }); + } - internal string ProjectToString() => - (string)_pbxProjectType.GetMethod("WriteToString", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, null); + internal string ProjectToString() => + (string)_pbxProjectType.GetMethod("WriteToString", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, null); - public void Dispose() => - _pbxProjectType.GetMethod("WriteToFile", BindingFlags.Public | BindingFlags.Instance) - .Invoke(_project, new[] { _projectPath }); - } -} + public void Dispose() => + _pbxProjectType.GetMethod("WriteToFile", BindingFlags.Public | BindingFlags.Instance) + .Invoke(_project, new[] { _projectPath }); +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs index f67ae26ec..9a4a3e848 100644 --- a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs +++ b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Reflection; using System.Text; using System.Xml; using Sentry.Extensibility; @@ -10,466 +9,465 @@ using UnityEditor.Android; using UnityEngine; -namespace Sentry.Unity.Editor.Android +namespace Sentry.Unity.Editor.Android; + +// https://github.com/getsentry/sentry-java/blob/d3764bfc97eed22564a1e23ba96fa73ad2685498/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java#L83-L217 +public class PostGenerateGradleAndroidProject : IPostGenerateGradleAndroidProject { - // https://github.com/getsentry/sentry-java/blob/d3764bfc97eed22564a1e23ba96fa73ad2685498/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java#L83-L217 - public class PostGenerateGradleAndroidProject : IPostGenerateGradleAndroidProject + public int callbackOrder { - public int callbackOrder + get { - get - { - var result = 1; - - var options = SentryScriptableObject.LoadOptions(); - if (options != null) - { - result = options.PostGenerateGradleProjectCallbackOrder; - } + var result = 1; - return result; + var options = SentryScriptableObject.LoadOptions(); + if (options != null) + { + result = options.PostGenerateGradleProjectCallbackOrder; } - } - public void OnPostGenerateGradleAndroidProject(string basePath) - { - var androidManifestConfiguration = new AndroidManifestConfiguration(); - androidManifestConfiguration.OnPostGenerateGradleAndroidProject(basePath); + return result; } } - public class AndroidManifestConfiguration + public void OnPostGenerateGradleAndroidProject(string basePath) { - private readonly SentryUnityOptions? _options; - private readonly SentryCliOptions? _sentryCliOptions; - private readonly IDiagnosticLogger _logger; - - private readonly bool _isDevelopmentBuild; - private readonly ScriptingImplementation _scriptingImplementation; - - public AndroidManifestConfiguration() - : this( - SentryScriptableObject.ConfiguredBuildTimeOptions, - isDevelopmentBuild: EditorUserBuildSettings.development, - scriptingImplementation: PlayerSettings.GetScriptingBackend(BuildTargetGroup.Android)) - { } - - // Testing - internal AndroidManifestConfiguration( - Func<(SentryUnityOptions?, SentryCliOptions?)> getOptions, - bool isDevelopmentBuild, - ScriptingImplementation scriptingImplementation, - ILogger? logger = null) - { - (_options, _sentryCliOptions) = getOptions(); - _logger = _options?.DiagnosticLogger ?? new UnityLogger(_options ?? new SentryUnityOptions(), logger); + var androidManifestConfiguration = new AndroidManifestConfiguration(); + androidManifestConfiguration.OnPostGenerateGradleAndroidProject(basePath); + } +} - _isDevelopmentBuild = isDevelopmentBuild; - _scriptingImplementation = scriptingImplementation; - } +public class AndroidManifestConfiguration +{ + private readonly SentryUnityOptions? _options; + private readonly SentryCliOptions? _sentryCliOptions; + private readonly IDiagnosticLogger _logger; + + private readonly bool _isDevelopmentBuild; + private readonly ScriptingImplementation _scriptingImplementation; + + public AndroidManifestConfiguration() + : this( + SentryScriptableObject.ConfiguredBuildTimeOptions, + isDevelopmentBuild: EditorUserBuildSettings.development, + scriptingImplementation: PlayerSettings.GetScriptingBackend(BuildTargetGroup.Android)) + { } + + // Testing + internal AndroidManifestConfiguration( + Func<(SentryUnityOptions?, SentryCliOptions?)> getOptions, + bool isDevelopmentBuild, + ScriptingImplementation scriptingImplementation, + ILogger? logger = null) + { + (_options, _sentryCliOptions) = getOptions(); + _logger = _options?.DiagnosticLogger ?? new UnityLogger(_options ?? new SentryUnityOptions(), logger); + + _isDevelopmentBuild = isDevelopmentBuild; + _scriptingImplementation = scriptingImplementation; + } - public void OnPostGenerateGradleAndroidProject(string basePath) + public void OnPostGenerateGradleAndroidProject(string basePath) + { + if (_scriptingImplementation != ScriptingImplementation.IL2CPP) { - if (_scriptingImplementation != ScriptingImplementation.IL2CPP) + if (_options is { AndroidNativeSupportEnabled: true }) { - if (_options is { AndroidNativeSupportEnabled: true }) - { - _logger.LogWarning("Android native support requires IL2CPP scripting backend and is currently unsupported on Mono."); - } - - return; + _logger.LogWarning("Android native support requires IL2CPP scripting backend and is currently unsupported on Mono."); } - ModifyManifest(basePath); + return; + } - var unityProjectPath = Directory.GetParent(Application.dataPath).FullName; - var gradleProjectPath = Directory.GetParent(basePath).FullName; + ModifyManifest(basePath); - CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath); - AddAndroidSdkDependencies(gradleProjectPath); - SetupSymbolsUpload(unityProjectPath, gradleProjectPath); - SetupProguard(gradleProjectPath); + var unityProjectPath = Directory.GetParent(Application.dataPath).FullName; + var gradleProjectPath = Directory.GetParent(basePath).FullName; + + CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath); + AddAndroidSdkDependencies(gradleProjectPath); + SetupSymbolsUpload(unityProjectPath, gradleProjectPath); + SetupProguard(gradleProjectPath); + } + + internal void ModifyManifest(string basePath) + { + var manifestPath = GetManifestPath(basePath); + if (!File.Exists(manifestPath)) + { + throw new FileNotFoundException("Can't configure native Android SDK nor set auto-init:false.", + manifestPath); } - internal void ModifyManifest(string basePath) + var enableNativeSupport = true; + if (_options is null) { - var manifestPath = GetManifestPath(basePath); - if (!File.Exists(manifestPath)) - { - throw new FileNotFoundException("Can't configure native Android SDK nor set auto-init:false.", - manifestPath); - } + _logger.LogWarning("Android native support disabled because Sentry has not been configured. " + + "You can do that through the editor: {0}", SentryWindow.EditorMenuPath); + enableNativeSupport = false; + } + else if (!_options.IsValid()) + { + _logger.LogDebug("Android native support disabled."); + enableNativeSupport = false; + } + else if (!_options.AndroidNativeSupportEnabled) + { + _logger.LogDebug("Android native support disabled through the options."); + enableNativeSupport = false; + } - var enableNativeSupport = true; - if (_options is null) - { - _logger.LogWarning("Android native support disabled because Sentry has not been configured. " + - "You can do that through the editor: {0}", SentryWindow.EditorMenuPath); - enableNativeSupport = false; - } - else if (!_options.IsValid()) - { - _logger.LogDebug("Android native support disabled."); - enableNativeSupport = false; - } - else if (!_options.AndroidNativeSupportEnabled) - { - _logger.LogDebug("Android native support disabled through the options."); - enableNativeSupport = false; - } + var androidManifest = new AndroidManifest(manifestPath, _logger); + androidManifest.RemovePreviousConfigurations(); - var androidManifest = new AndroidManifest(manifestPath, _logger); - androidManifest.RemovePreviousConfigurations(); + if (!enableNativeSupport) + { + return; + } - if (!enableNativeSupport) - { - return; - } + androidManifest.AddDisclaimerComment(); - androidManifest.AddDisclaimerComment(); + _logger.LogDebug("Configuring Sentry options on AndroidManifest: {0}", basePath); + androidManifest.SetSDK("sentry.java.android.unity"); + _logger.LogDebug("Setting DSN: {0}", _options!.Dsn); + androidManifest.SetDsn(_options.Dsn!); - _logger.LogDebug("Configuring Sentry options on AndroidManifest: {0}", basePath); - androidManifest.SetSDK("sentry.java.android.unity"); - _logger.LogDebug("Setting DSN: {0}", _options!.Dsn); - androidManifest.SetDsn(_options.Dsn!); + if (_options.Debug) + { + _logger.LogDebug("Setting Debug: {0}", _options.Debug); + androidManifest.SetDebug(_options.Debug); + } - if (_options.Debug) - { - _logger.LogDebug("Setting Debug: {0}", _options.Debug); - androidManifest.SetDebug(_options.Debug); - } + if (_options.Release is not null) + { + _logger.LogDebug("Setting Release: {0}", _options.Release); + androidManifest.SetRelease(_options.Release); + } - if (_options.Release is not null) - { - _logger.LogDebug("Setting Release: {0}", _options.Release); - androidManifest.SetRelease(_options.Release); - } + if (_options.Environment is not null) + { + _logger.LogDebug("Setting Environment: {0}", _options.Environment); + androidManifest.SetEnvironment(_options.Environment); + } - if (_options.Environment is not null) - { - _logger.LogDebug("Setting Environment: {0}", _options.Environment); - androidManifest.SetEnvironment(_options.Environment); - } + _logger.LogDebug("Setting DiagnosticLevel: {0}", _options.DiagnosticLevel); + androidManifest.SetLevel(_options.DiagnosticLevel); - _logger.LogDebug("Setting DiagnosticLevel: {0}", _options.DiagnosticLevel); - androidManifest.SetLevel(_options.DiagnosticLevel); + if (_options.SampleRate.HasValue) + { + _logger.LogDebug("Setting SampleRate: {0}", _options.SampleRate); + androidManifest.SetSampleRate(_options.SampleRate.Value); + } - if (_options.SampleRate.HasValue) - { - _logger.LogDebug("Setting SampleRate: {0}", _options.SampleRate); - androidManifest.SetSampleRate(_options.SampleRate.Value); - } + // TODO: Missing on AndroidManifest + // _logger.LogDebug("Setting MaxBreadcrumbs: {0}", options.MaxBreadcrumbs); + // androidManifest.SetMaxBreadcrumbs(options.MaxBreadcrumbs); + // _logger.LogDebug("Setting MaxCacheItems: {0}", options.MaxCacheItems); + // androidManifest.SetMaxCacheItems(options.MaxCacheItems); + // _logger.LogDebug("Setting SendDefaultPii: {0}", options.SendDefaultPii); + // // androidManifest.SetSendDefaultPii(options.SendDefaultPii); + + // Note: doesn't work - produces a blank (white) screenshot + // _logger.LogDebug("Setting AttachScreenshot: {0}", _options.AttachScreenshot); + // androidManifest.SetAttachScreenshot(_options.AttachScreenshot); + androidManifest.SetAttachScreenshot(false); + + // Disabling the native in favor of the C# layer for now + androidManifest.SetNdkEnabled(_options.NdkIntegrationEnabled); + androidManifest.SetNdkScopeSync(_options.NdkScopeSyncEnabled); + androidManifest.SetAutoSessionTracking(false); + androidManifest.SetAutoAppLifecycleBreadcrumbs(false); + androidManifest.SetAnr(false); + androidManifest.SetPersistentScopeObserver(false); + + // TODO: All SentryOptions and create specific Android options + + _ = androidManifest.Save(); + } - // TODO: Missing on AndroidManifest - // _logger.LogDebug("Setting MaxBreadcrumbs: {0}", options.MaxBreadcrumbs); - // androidManifest.SetMaxBreadcrumbs(options.MaxBreadcrumbs); - // _logger.LogDebug("Setting MaxCacheItems: {0}", options.MaxCacheItems); - // androidManifest.SetMaxCacheItems(options.MaxCacheItems); - // _logger.LogDebug("Setting SendDefaultPii: {0}", options.SendDefaultPii); - // // androidManifest.SetSendDefaultPii(options.SendDefaultPii); - - // Note: doesn't work - produces a blank (white) screenshot - // _logger.LogDebug("Setting AttachScreenshot: {0}", _options.AttachScreenshot); - // androidManifest.SetAttachScreenshot(_options.AttachScreenshot); - androidManifest.SetAttachScreenshot(false); - - // Disabling the native in favor of the C# layer for now - androidManifest.SetNdkEnabled(_options.NdkIntegrationEnabled); - androidManifest.SetNdkScopeSync(_options.NdkScopeSyncEnabled); - androidManifest.SetAutoSessionTracking(false); - androidManifest.SetAutoAppLifecycleBreadcrumbs(false); - androidManifest.SetAnr(false); - androidManifest.SetPersistentScopeObserver(false); - - // TODO: All SentryOptions and create specific Android options - - _ = androidManifest.Save(); - } + internal void CopyAndroidSdkToGradleProject(string unityProjectPath, string gradlePath) + { + var androidSdkPath = Path.Combine(unityProjectPath, "Packages", SentryPackageInfo.GetName(), "Plugins", "Android", "Sentry~"); + var targetPath = Path.Combine(gradlePath, "unityLibrary", "libs"); - internal void CopyAndroidSdkToGradleProject(string unityProjectPath, string gradlePath) + if (_options is { Enabled: true, AndroidNativeSupportEnabled: true }) { - var androidSdkPath = Path.Combine(unityProjectPath, "Packages", SentryPackageInfo.GetName(), "Plugins", "Android", "Sentry~"); - var targetPath = Path.Combine(gradlePath, "unityLibrary", "libs"); - - if (_options is { Enabled: true, AndroidNativeSupportEnabled: true }) + if (!Directory.Exists(androidSdkPath)) { - if (!Directory.Exists(androidSdkPath)) - { - throw new DirectoryNotFoundException($"Failed to find the Android SDK at '{androidSdkPath}'."); - } - - _logger.LogInfo("Copying the Android SDK to '{0}'.", gradlePath); - foreach (var file in Directory.GetFiles(androidSdkPath)) - { - var destinationFile = Path.Combine(targetPath, Path.GetFileName(file)); - if (!File.Exists(destinationFile)) - { - File.Copy(file, destinationFile); - } - } + throw new DirectoryNotFoundException($"Failed to find the Android SDK at '{androidSdkPath}'."); } - else + + _logger.LogInfo("Copying the Android SDK to '{0}'.", gradlePath); + foreach (var file in Directory.GetFiles(androidSdkPath)) { - _logger.LogInfo("Removing the Android SDK from the output project."); - foreach (var file in Directory.GetFiles(androidSdkPath)) + var destinationFile = Path.Combine(targetPath, Path.GetFileName(file)); + if (!File.Exists(destinationFile)) { - var fileToDelete = Path.Combine(targetPath, Path.GetFileName(file)); - if (File.Exists(fileToDelete)) - { - File.Delete(fileToDelete); - } + File.Copy(file, destinationFile); } } } - - internal void AddAndroidSdkDependencies(string gradleProjectPath) + else { - var tool = new GradleSetup(_logger, gradleProjectPath); - var nativeSupportEnabled = _options is { Enabled: true, AndroidNativeSupportEnabled: true }; - - try + _logger.LogInfo("Removing the Android SDK from the output project."); + foreach (var file in Directory.GetFiles(androidSdkPath)) { - if (nativeSupportEnabled) - { - tool.UpdateGradleProject(); - } - else + var fileToDelete = Path.Combine(targetPath, Path.GetFileName(file)); + if (File.Exists(fileToDelete)) { - tool.ClearGradleProject(); + File.Delete(fileToDelete); } } - catch (Exception e) - { - _logger.LogError(e, $"Failed to {(nativeSupportEnabled ? "add" : "remove")} Android Dependencies in the gradle project"); - } } + } - internal void SetupSymbolsUpload(string unityProjectPath, string gradleProjectPath) - { - var disableSymbolsUpload = false; - var symbolsUpload = new DebugSymbolUpload(_logger, _sentryCliOptions, unityProjectPath, gradleProjectPath, EditorUserBuildSettings.exportAsGoogleAndroidProject, AndroidUtils.ShouldUploadMapping()); - - if (_options is not { Enabled: true, AndroidNativeSupportEnabled: true }) - { - disableSymbolsUpload = true; - } + internal void AddAndroidSdkDependencies(string gradleProjectPath) + { + var tool = new GradleSetup(_logger, gradleProjectPath); + var nativeSupportEnabled = _options is { Enabled: true, AndroidNativeSupportEnabled: true }; - if (_sentryCliOptions is null) + try + { + if (nativeSupportEnabled) { - _logger.LogWarning("Failed to load sentry-cli options."); - disableSymbolsUpload = true; + tool.UpdateGradleProject(); } - else if (!_sentryCliOptions.IsValid(_logger, _isDevelopmentBuild)) + else { - disableSymbolsUpload = true; + tool.ClearGradleProject(); } + } + catch (Exception e) + { + _logger.LogError(e, $"Failed to {(nativeSupportEnabled ? "add" : "remove")} Android Dependencies in the gradle project"); + } + } - if (disableSymbolsUpload) - { - symbolsUpload.RemoveUploadFromGradleFile(); - - if (_options is { Il2CppLineNumberSupportEnabled: true }) - { - _logger.LogWarning("The IL2CPP line number support requires the debug symbol upload to be enabled."); - } + internal void SetupSymbolsUpload(string unityProjectPath, string gradleProjectPath) + { + var disableSymbolsUpload = false; + var symbolsUpload = new DebugSymbolUpload(_logger, _sentryCliOptions, unityProjectPath, gradleProjectPath, EditorUserBuildSettings.exportAsGoogleAndroidProject, AndroidUtils.ShouldUploadMapping()); - return; - } + if (_options is not { Enabled: true, AndroidNativeSupportEnabled: true }) + { + disableSymbolsUpload = true; + } - try - { - _logger.LogInfo("Adding automated debug symbols upload to the gradle project."); - // TODO this currently copies the CLI for the current platform, thus making the exported project only - // build properly on the same platform as it was exported from (Linux->Linux, Windows->Windows, etc.). - // In practice, users should be able to build the project on any platform, regardless of where Unity - // ran. In that case, we would either need to include all CLI binaries and switch in Gradle, or let - // gradle download CLI on demand (relevant code could be taken from sentry-java repo?). - var launcherDirectory = Path.Combine(gradleProjectPath, "launcher"); - var sentryCliPath = SentryCli.SetupSentryCli( - EditorUserBuildSettings.exportAsGoogleAndroidProject ? launcherDirectory : null); - SentryCli.CreateSentryProperties(launcherDirectory, _sentryCliOptions!, _options!); - symbolsUpload.TryCopySymbolsToGradleProject(); - - symbolsUpload.AppendUploadToGradleFile(sentryCliPath); - } - catch (Exception e) - { - _logger.LogError(e, "Failed to add the automatic symbols upload to the gradle project"); - } + if (_sentryCliOptions is null) + { + _logger.LogWarning("Failed to load sentry-cli options."); + disableSymbolsUpload = true; + } + else if (!_sentryCliOptions.IsValid(_logger, _isDevelopmentBuild)) + { + disableSymbolsUpload = true; } - private void SetupProguard(string gradleProjectPath) + if (disableSymbolsUpload) { - var tool = new ProguardSetup(_logger, gradleProjectPath); - var nativeSupportEnabled = _options is { Enabled: true, AndroidNativeSupportEnabled: true }; + symbolsUpload.RemoveUploadFromGradleFile(); - try - { - if (nativeSupportEnabled) - { - tool.AddToGradleProject(); - } - else - { - tool.RemoveFromGradleProject(); - } - } - catch (Exception e) + if (_options is { Il2CppLineNumberSupportEnabled: true }) { - _logger.LogError(e, $"Failed to {(nativeSupportEnabled ? "add" : "remove")} Proguard rules in the gradle project"); + _logger.LogWarning("The IL2CPP line number support requires the debug symbol upload to be enabled."); } - } - - internal static string GetManifestPath(string basePath) => - new StringBuilder(basePath) - .Append(Path.DirectorySeparatorChar) - .Append("src") - .Append(Path.DirectorySeparatorChar) - .Append("main") - .Append(Path.DirectorySeparatorChar) - .Append("AndroidManifest.xml") - .ToString(); + return; + } + try + { + _logger.LogInfo("Adding automated debug symbols upload to the gradle project."); + // TODO this currently copies the CLI for the current platform, thus making the exported project only + // build properly on the same platform as it was exported from (Linux->Linux, Windows->Windows, etc.). + // In practice, users should be able to build the project on any platform, regardless of where Unity + // ran. In that case, we would either need to include all CLI binaries and switch in Gradle, or let + // gradle download CLI on demand (relevant code could be taken from sentry-java repo?). + var launcherDirectory = Path.Combine(gradleProjectPath, "launcher"); + var sentryCliPath = SentryCli.SetupSentryCli( + EditorUserBuildSettings.exportAsGoogleAndroidProject ? launcherDirectory : null); + SentryCli.CreateSentryProperties(launcherDirectory, _sentryCliOptions!, _options!); + symbolsUpload.TryCopySymbolsToGradleProject(); + + symbolsUpload.AppendUploadToGradleFile(sentryCliPath); + } + catch (Exception e) + { + _logger.LogError(e, "Failed to add the automatic symbols upload to the gradle project"); + } } - internal class AndroidXmlDocument : XmlDocument + private void SetupProguard(string gradleProjectPath) { - private readonly string _path; - protected const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; - protected const string AndroidNsPrefix = "android"; + var tool = new ProguardSetup(_logger, gradleProjectPath); + var nativeSupportEnabled = _options is { Enabled: true, AndroidNativeSupportEnabled: true }; - protected AndroidXmlDocument(string path) + try { - _path = path; - using (var reader = new XmlTextReader(_path)) + if (nativeSupportEnabled) { - _ = reader.Read(); - Load(reader); + tool.AddToGradleProject(); } - - var nsManager = new XmlNamespaceManager(NameTable); - nsManager.AddNamespace("android", AndroidXmlNamespace); + else + { + tool.RemoveFromGradleProject(); + } + } + catch (Exception e) + { + _logger.LogError(e, $"Failed to {(nativeSupportEnabled ? "add" : "remove")} Proguard rules in the gradle project"); } + } + + internal static string GetManifestPath(string basePath) => + new StringBuilder(basePath) + .Append(Path.DirectorySeparatorChar) + .Append("src") + .Append(Path.DirectorySeparatorChar) + .Append("main") + .Append(Path.DirectorySeparatorChar) + .Append("AndroidManifest.xml") + .ToString(); - public string Save() => SaveAs(_path); - private string SaveAs(string path) +} + +internal class AndroidXmlDocument : XmlDocument +{ + private readonly string _path; + protected const string AndroidXmlNamespace = "http://schemas.android.com/apk/res/android"; + protected const string AndroidNsPrefix = "android"; + + protected AndroidXmlDocument(string path) + { + _path = path; + using (var reader = new XmlTextReader(_path)) { - using var writer = new XmlTextWriter(path, new UTF8Encoding(false)) { Formatting = Formatting.Indented }; - Save(writer); - return path; + _ = reader.Read(); + Load(reader); } + + var nsManager = new XmlNamespaceManager(NameTable); + nsManager.AddNamespace("android", AndroidXmlNamespace); } - internal class AndroidManifest : AndroidXmlDocument + public string Save() => SaveAs(_path); + + private string SaveAs(string path) { - private const string SentryPrefix = "io.sentry"; - private const string Disclaimer = "GENERATED BY SENTRY. Changes to the Sentry options will be lost!"; + using var writer = new XmlTextWriter(path, new UTF8Encoding(false)) { Formatting = Formatting.Indented }; + Save(writer); + return path; + } +} - private readonly XmlElement _applicationElement; - private readonly IDiagnosticLogger _logger; +internal class AndroidManifest : AndroidXmlDocument +{ + private const string SentryPrefix = "io.sentry"; + private const string Disclaimer = "GENERATED BY SENTRY. Changes to the Sentry options will be lost!"; - public AndroidManifest(string path, IDiagnosticLogger logger) : base(path) - { - _applicationElement = (XmlElement)SelectSingleNode("/manifest/application"); - _logger = logger; - } + private readonly XmlElement _applicationElement; + private readonly IDiagnosticLogger _logger; - internal void RemovePreviousConfigurations() - { - var nodesToRemove = new List(); + public AndroidManifest(string path, IDiagnosticLogger logger) : base(path) + { + _applicationElement = (XmlElement)SelectSingleNode("/manifest/application"); + _logger = logger; + } - foreach (XmlNode node in _applicationElement.ChildNodes) + internal void RemovePreviousConfigurations() + { + var nodesToRemove = new List(); + + foreach (XmlNode node in _applicationElement.ChildNodes) + { + if (node.NodeType == XmlNodeType.Comment && node.Value.Equals(Disclaimer)) { - if (node.NodeType == XmlNodeType.Comment && node.Value.Equals(Disclaimer)) - { - nodesToRemove.Add(node); - continue; - } + nodesToRemove.Add(node); + continue; + } - if (node.Name.Equals("meta-data")) + if (node.Name.Equals("meta-data")) + { + foreach (XmlAttribute attr in node.Attributes) { - foreach (XmlAttribute attr in node.Attributes) + if (attr.Prefix.Equals(AndroidNsPrefix) && attr.LocalName.Equals("name") && + attr.Value.StartsWith(SentryPrefix)) { - if (attr.Prefix.Equals(AndroidNsPrefix) && attr.LocalName.Equals("name") && - attr.Value.StartsWith(SentryPrefix)) - { - _logger.LogDebug("Removing AndroidManifest meta-data '{0}'", attr.Value); - nodesToRemove.Add(node); - break; - } + _logger.LogDebug("Removing AndroidManifest meta-data '{0}'", attr.Value); + nodesToRemove.Add(node); + break; } } } + } - foreach (var node in nodesToRemove) - { - _ = node.ParentNode.RemoveChild(node); - } + foreach (var node in nodesToRemove) + { + _ = node.ParentNode.RemoveChild(node); } + } - public void AddDisclaimerComment() => - _applicationElement.AppendChild(_applicationElement.OwnerDocument.CreateComment(Disclaimer)); + public void AddDisclaimerComment() => + _applicationElement.AppendChild(_applicationElement.OwnerDocument.CreateComment(Disclaimer)); - internal void SetDsn(string dsn) => SetMetaData($"{SentryPrefix}.dsn", dsn); + internal void SetDsn(string dsn) => SetMetaData($"{SentryPrefix}.dsn", dsn); - internal void SetSampleRate(float sampleRate) => - SetMetaData($"{SentryPrefix}.sample-rate", sampleRate.ToString()); + internal void SetSampleRate(float sampleRate) => + SetMetaData($"{SentryPrefix}.sample-rate", sampleRate.ToString()); - internal void SetRelease(string release) => SetMetaData($"{SentryPrefix}.release", release); + internal void SetRelease(string release) => SetMetaData($"{SentryPrefix}.release", release); - internal void SetAttachScreenshot(bool value) => SetMetaData($"{SentryPrefix}.attach-screenshot", value.ToString()); + internal void SetAttachScreenshot(bool value) => SetMetaData($"{SentryPrefix}.attach-screenshot", value.ToString()); - internal void SetEnvironment(string environment) => SetMetaData($"{SentryPrefix}.environment", environment); + internal void SetEnvironment(string environment) => SetMetaData($"{SentryPrefix}.environment", environment); - internal void SetSDK(string name) => SetMetaData($"{SentryPrefix}.sdk.name", name); + internal void SetSDK(string name) => SetMetaData($"{SentryPrefix}.sdk.name", name); - internal void SetAutoSessionTracking(bool enableAutoSessionTracking) - => SetMetaData($"{SentryPrefix}.auto-session-tracking.enable", enableAutoSessionTracking.ToString()); + internal void SetAutoSessionTracking(bool enableAutoSessionTracking) + => SetMetaData($"{SentryPrefix}.auto-session-tracking.enable", enableAutoSessionTracking.ToString()); - public void SetAutoAppLifecycleBreadcrumbs(bool enableAutoAppLifeCycleBreadcrumbs) - => SetMetaData($"{SentryPrefix}.breadcrumbs.app-lifecycle", enableAutoAppLifeCycleBreadcrumbs.ToString()); + public void SetAutoAppLifecycleBreadcrumbs(bool enableAutoAppLifeCycleBreadcrumbs) + => SetMetaData($"{SentryPrefix}.breadcrumbs.app-lifecycle", enableAutoAppLifeCycleBreadcrumbs.ToString()); - internal void SetAnr(bool enableAnr) - => SetMetaData($"{SentryPrefix}.anr.enable", enableAnr.ToString()); + internal void SetAnr(bool enableAnr) + => SetMetaData($"{SentryPrefix}.anr.enable", enableAnr.ToString()); - internal void SetPersistentScopeObserver(bool enableScopePersistence) - => SetMetaData($"{SentryPrefix}.enable-scope-persistence", enableScopePersistence.ToString()); + internal void SetPersistentScopeObserver(bool enableScopePersistence) + => SetMetaData($"{SentryPrefix}.enable-scope-persistence", enableScopePersistence.ToString()); - internal void SetNdkEnabled(bool enableNdk) - => SetMetaData($"{SentryPrefix}.ndk.enable", enableNdk.ToString()); + internal void SetNdkEnabled(bool enableNdk) + => SetMetaData($"{SentryPrefix}.ndk.enable", enableNdk.ToString()); - internal void SetNdkScopeSync(bool enableNdkScopeSync) - => SetMetaData($"{SentryPrefix}.ndk.scope-sync.enable", enableNdkScopeSync.ToString()); + internal void SetNdkScopeSync(bool enableNdkScopeSync) + => SetMetaData($"{SentryPrefix}.ndk.scope-sync.enable", enableNdkScopeSync.ToString()); - internal void SetDebug(bool debug) => SetMetaData($"{SentryPrefix}.debug", debug ? "true" : "false"); + internal void SetDebug(bool debug) => SetMetaData($"{SentryPrefix}.debug", debug ? "true" : "false"); - // https://github.com/getsentry/sentry-java/blob/db4dfc92f202b1cefc48d019fdabe24d487db923/sentry/src/main/java/io/sentry/SentryLevel.java#L4-L9 - internal void SetLevel(SentryLevel level) => - SetMetaData($"{SentryPrefix}.debug.level", level switch - { - SentryLevel.Debug => "debug", - SentryLevel.Error => "error", - SentryLevel.Fatal => "fatal", - SentryLevel.Info => "info", - SentryLevel.Warning => "warning", - _ => "debug" - }); - - private void SetMetaData(string key, string value) + // https://github.com/getsentry/sentry-java/blob/db4dfc92f202b1cefc48d019fdabe24d487db923/sentry/src/main/java/io/sentry/SentryLevel.java#L4-L9 + internal void SetLevel(SentryLevel level) => + SetMetaData($"{SentryPrefix}.debug.level", level switch { - var element = _applicationElement.AppendChild(_applicationElement.OwnerDocument - .CreateElement("meta-data")); - _ = element.Attributes.Append(CreateAndroidAttribute("name", key)); - _ = element.Attributes.Append(CreateAndroidAttribute("value", value)); - } + SentryLevel.Debug => "debug", + SentryLevel.Error => "error", + SentryLevel.Fatal => "fatal", + SentryLevel.Info => "info", + SentryLevel.Warning => "warning", + _ => "debug" + }); + + private void SetMetaData(string key, string value) + { + var element = _applicationElement.AppendChild(_applicationElement.OwnerDocument + .CreateElement("meta-data")); + _ = element.Attributes.Append(CreateAndroidAttribute("name", key)); + _ = element.Attributes.Append(CreateAndroidAttribute("value", value)); + } - private XmlAttribute CreateAndroidAttribute(string key, string value) - { - var attr = CreateAttribute(AndroidNsPrefix, key, AndroidXmlNamespace); - attr.Value = value; - return attr; - } + private XmlAttribute CreateAndroidAttribute(string key, string value) + { + var attr = CreateAttribute(AndroidNsPrefix, key, AndroidXmlNamespace); + attr.Value = value; + return attr; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Android/AndroidUtils.cs b/src/Sentry.Unity.Editor/Android/AndroidUtils.cs index eaf539c2b..28784249c 100644 --- a/src/Sentry.Unity.Editor/Android/AndroidUtils.cs +++ b/src/Sentry.Unity.Editor/Android/AndroidUtils.cs @@ -2,37 +2,36 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.Android +namespace Sentry.Unity.Editor.Android; + +internal static class AndroidUtils { - internal static class AndroidUtils + internal static bool ShouldUploadMapping() { - internal static bool ShouldUploadMapping() + var isDebug = EditorUserBuildSettings.development; + var majorVersion = int.Parse(Application.unityVersion.Split('.')[0]); + if (majorVersion < 2020) { - var isDebug = EditorUserBuildSettings.development; - var majorVersion = int.Parse(Application.unityVersion.Split('.')[0]); - if (majorVersion < 2020) + var buildSettingsType = typeof(EditorUserBuildSettings); + var propertyName = isDebug ? "androidDebugMinification" : "androidReleaseMinification"; + var prop = buildSettingsType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static); + if (prop != null) { - var buildSettingsType = typeof(EditorUserBuildSettings); - var propertyName = isDebug ? "androidDebugMinification" : "androidReleaseMinification"; - var prop = buildSettingsType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static); - if (prop != null) - { - var value = (int)prop.GetValue(null); - return value > 0; - } + var value = (int)prop.GetValue(null); + return value > 0; } - else + } + else + { + var type = typeof(PlayerSettings.Android); + var propertyName = isDebug ? "minifyDebug" : "minifyRelease"; + var prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static); + if (prop != null) { - var type = typeof(PlayerSettings.Android); - var propertyName = isDebug ? "minifyDebug" : "minifyRelease"; - var prop = type.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Static); - if (prop != null) - { - return (bool)prop.GetValue(null); - } + return (bool)prop.GetValue(null); } - - return false; } + + return false; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs b/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs index e68721ad7..675aa2d4b 100644 --- a/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs +++ b/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs @@ -6,306 +6,305 @@ using Sentry.Unity.Integrations; using UnityEditor; -namespace Sentry.Unity.Editor.Android +namespace Sentry.Unity.Editor.Android; + +internal class DebugSymbolUpload { - internal class DebugSymbolUpload + private readonly IDiagnosticLogger _logger; + + internal const string RelativeBuildOutputPathOld = "Temp/StagingArea/symbols"; + internal const string RelativeGradlePathOld = "Temp/gradleOut"; + internal const string RelativeBuildOutputPathNew = "Library/Bee/artifacts/Android"; + internal const string RelativeAndroidPathNew = "Library/Bee/Android"; + + internal const string SymbolUploadLogName = "sentry-symbols-upload.log"; + internal const string MappingUploadLogName = "sentry-mapping-upload.log"; + + private readonly string _unityProjectPath; + private readonly string _gradleProjectPath; + private readonly string _gradleScriptPath; + private readonly bool _isExporting; + private readonly bool _isMinifyEnabled; + private readonly bool _ignoreCliErrors; + + private readonly SentryCliOptions? _cliOptions; + private readonly List _symbolUploadPaths; + private readonly string _mappingFilePath; + + private const string SymbolUploadTaskStartComment = "// Autogenerated Sentry symbol upload task [start]"; + private const string SymbolUploadTaskEndComment = "// Autogenerated Sentry symbol upload task [end]"; + private const string SentryCliMarker = "SENTRY_CLI"; + private const string UploadArgsMarker = "UPLOAD_ARGS"; + private const string MappingPathMarker = "MAPPING_PATH"; + + private string _symbolUploadTaskFormat { - private readonly IDiagnosticLogger _logger; - - internal const string RelativeBuildOutputPathOld = "Temp/StagingArea/symbols"; - internal const string RelativeGradlePathOld = "Temp/gradleOut"; - internal const string RelativeBuildOutputPathNew = "Library/Bee/artifacts/Android"; - internal const string RelativeAndroidPathNew = "Library/Bee/Android"; - - internal const string SymbolUploadLogName = "sentry-symbols-upload.log"; - internal const string MappingUploadLogName = "sentry-mapping-upload.log"; - - private readonly string _unityProjectPath; - private readonly string _gradleProjectPath; - private readonly string _gradleScriptPath; - private readonly bool _isExporting; - private readonly bool _isMinifyEnabled; - private readonly bool _ignoreCliErrors; - - private readonly SentryCliOptions? _cliOptions; - private readonly List _symbolUploadPaths; - private readonly string _mappingFilePath; - - private const string SymbolUploadTaskStartComment = "// Autogenerated Sentry symbol upload task [start]"; - private const string SymbolUploadTaskEndComment = "// Autogenerated Sentry symbol upload task [end]"; - private const string SentryCliMarker = "SENTRY_CLI"; - private const string UploadArgsMarker = "UPLOAD_ARGS"; - private const string MappingPathMarker = "MAPPING_PATH"; - - private string _symbolUploadTaskFormat + get { - get + var stringBuilder = new StringBuilder(); + stringBuilder.AppendLine("// Credentials and project settings information are stored in the sentry.properties file"); + stringBuilder.AppendLine("afterEvaluate {"); + stringBuilder.AppendLine("task sentryUploadSymbols {"); + stringBuilder.AppendLine(" doLast {"); + if (_isExporting) { - var stringBuilder = new StringBuilder(); - stringBuilder.AppendLine("// Credentials and project settings information are stored in the sentry.properties file"); - stringBuilder.AppendLine("afterEvaluate {"); - stringBuilder.AppendLine("task sentryUploadSymbols {"); - stringBuilder.AppendLine(" doLast {"); - if (_isExporting) - { - stringBuilder.AppendLine(" println 'Uploading symbols to Sentry.'"); - } - else + stringBuilder.AppendLine(" println 'Uploading symbols to Sentry.'"); + } + else + { + var logsDir = $"{ConvertSlashes(_unityProjectPath)}/Logs"; + Directory.CreateDirectory(logsDir); + stringBuilder.AppendLine(" println 'Uploading symbols to Sentry. You can find the full log in ./Logs/sentry-symbols-upload.log (the file content may not be strictly sequential because it\\'s a merge of two streams).'"); + stringBuilder.AppendLine($" def logFilePath = '{logsDir}/{SymbolUploadLogName}'"); + stringBuilder.AppendLine(" def sentryLogFile = new FileOutputStream(logFilePath)"); + if (_ignoreCliErrors) { - var logsDir = $"{ConvertSlashes(_unityProjectPath)}/Logs"; - Directory.CreateDirectory(logsDir); - stringBuilder.AppendLine(" println 'Uploading symbols to Sentry. You can find the full log in ./Logs/sentry-symbols-upload.log (the file content may not be strictly sequential because it\\'s a merge of two streams).'"); - stringBuilder.AppendLine($" def logFilePath = '{logsDir}/{SymbolUploadLogName}'"); - stringBuilder.AppendLine(" def sentryLogFile = new FileOutputStream(logFilePath)"); - if (_ignoreCliErrors) - { - stringBuilder.AppendLine(" try {"); - } + stringBuilder.AppendLine(" try {"); } + } - stringBuilder.AppendLine(" exec {"); - stringBuilder.AppendLine(" environment 'SENTRY_PROPERTIES', './sentry.properties'"); - stringBuilder.AppendLine($" executable '{SentryCliMarker}'"); - stringBuilder.AppendLine($" args = ['debug-files', 'upload'{UploadArgsMarker}]"); - if (!_isExporting) - { - stringBuilder.AppendLine(" standardOutput sentryLogFile"); - stringBuilder.AppendLine(" errorOutput sentryLogFile"); - } + stringBuilder.AppendLine(" exec {"); + stringBuilder.AppendLine(" environment 'SENTRY_PROPERTIES', './sentry.properties'"); + stringBuilder.AppendLine($" executable '{SentryCliMarker}'"); + stringBuilder.AppendLine($" args = ['debug-files', 'upload'{UploadArgsMarker}]"); + if (!_isExporting) + { + stringBuilder.AppendLine(" standardOutput sentryLogFile"); + stringBuilder.AppendLine(" errorOutput sentryLogFile"); + } + stringBuilder.AppendLine(" }"); + if (!_isExporting && _ignoreCliErrors) + { + stringBuilder.AppendLine(" } catch (exception) {"); + stringBuilder.AppendLine(" def file = new File(logFilePath)"); + stringBuilder.AppendLine(" file.append('===ERROR===' + exception)"); stringBuilder.AppendLine(" }"); - if (!_isExporting && _ignoreCliErrors) - { - stringBuilder.AppendLine(" } catch (exception) {"); - stringBuilder.AppendLine(" def file = new File(logFilePath)"); - stringBuilder.AppendLine(" file.append('===ERROR===' + exception)"); - stringBuilder.AppendLine(" }"); - } - CheckMapping(stringBuilder); - stringBuilder.AppendLine(" }"); - stringBuilder.AppendLine("}"); - stringBuilder.AppendLine(string.Empty); - stringBuilder.AppendLine("tasks.assembleDebug.finalizedBy sentryUploadSymbols"); - stringBuilder.AppendLine("tasks.assembleRelease.finalizedBy sentryUploadSymbols"); - stringBuilder.AppendLine("tasks.bundleDebug.finalizedBy sentryUploadSymbols"); - stringBuilder.AppendLine("tasks.bundleRelease.finalizedBy sentryUploadSymbols"); - - stringBuilder.AppendLine("}"); - return stringBuilder.ToString(); } + CheckMapping(stringBuilder); + stringBuilder.AppendLine(" }"); + stringBuilder.AppendLine("}"); + stringBuilder.AppendLine(string.Empty); + stringBuilder.AppendLine("tasks.assembleDebug.finalizedBy sentryUploadSymbols"); + stringBuilder.AppendLine("tasks.assembleRelease.finalizedBy sentryUploadSymbols"); + stringBuilder.AppendLine("tasks.bundleDebug.finalizedBy sentryUploadSymbols"); + stringBuilder.AppendLine("tasks.bundleRelease.finalizedBy sentryUploadSymbols"); + + stringBuilder.AppendLine("}"); + return stringBuilder.ToString(); } + } - public DebugSymbolUpload(IDiagnosticLogger logger, - SentryCliOptions? cliOptions, - string unityProjectPath, - string gradleProjectPath, - bool isExporting = false, - bool minifyEnabled = false, - IApplication? application = null) - { - _logger = logger; - - _unityProjectPath = unityProjectPath; - _gradleProjectPath = gradleProjectPath; - _gradleScriptPath = Path.Combine(_gradleProjectPath, "launcher/build.gradle"); - _isExporting = isExporting; - _isMinifyEnabled = minifyEnabled; - _ignoreCliErrors = cliOptions != null && cliOptions.IgnoreCliErrors; - - _cliOptions = cliOptions; - _symbolUploadPaths = GetSymbolUploadPaths(application); - _mappingFilePath = GetMappingFilePath(application); - } + public DebugSymbolUpload(IDiagnosticLogger logger, + SentryCliOptions? cliOptions, + string unityProjectPath, + string gradleProjectPath, + bool isExporting = false, + bool minifyEnabled = false, + IApplication? application = null) + { + _logger = logger; + + _unityProjectPath = unityProjectPath; + _gradleProjectPath = gradleProjectPath; + _gradleScriptPath = Path.Combine(_gradleProjectPath, "launcher/build.gradle"); + _isExporting = isExporting; + _isMinifyEnabled = minifyEnabled; + _ignoreCliErrors = cliOptions != null && cliOptions.IgnoreCliErrors; + + _cliOptions = cliOptions; + _symbolUploadPaths = GetSymbolUploadPaths(application); + _mappingFilePath = GetMappingFilePath(application); + } - public void AppendUploadToGradleFile(string sentryCliPath) - { - RemoveUploadFromGradleFile(); + public void AppendUploadToGradleFile(string sentryCliPath) + { + RemoveUploadFromGradleFile(); - _logger.LogInfo("Appending debug symbols upload task to gradle file."); + _logger.LogInfo("Appending debug symbols upload task to gradle file."); - sentryCliPath = ConvertSlashes(sentryCliPath); - if (!File.Exists(sentryCliPath)) - { - throw new FileNotFoundException("Failed to find sentry-cli", sentryCliPath); - } + sentryCliPath = ConvertSlashes(sentryCliPath); + if (!File.Exists(sentryCliPath)) + { + throw new FileNotFoundException("Failed to find sentry-cli", sentryCliPath); + } - var uploadDifArguments = ", '--il2cpp-mapping'"; - if (_cliOptions != null && _cliOptions.UploadSources) - { - uploadDifArguments += ", '--include-sources'"; - } + var uploadDifArguments = ", '--il2cpp-mapping'"; + if (_cliOptions != null && _cliOptions.UploadSources) + { + uploadDifArguments += ", '--include-sources'"; + } - if (_isExporting) - { - uploadDifArguments += ", project.rootDir"; - sentryCliPath = $"./{Path.GetFileName(sentryCliPath)}"; - } - else + if (_isExporting) + { + uploadDifArguments += ", project.rootDir"; + sentryCliPath = $"./{Path.GetFileName(sentryCliPath)}"; + } + else + { + foreach (var symbolUploadPath in _symbolUploadPaths) { - foreach (var symbolUploadPath in _symbolUploadPaths) + if (Directory.Exists(symbolUploadPath)) + { + uploadDifArguments += $", '{ConvertSlashes(symbolUploadPath)}'"; + } + else { - if (Directory.Exists(symbolUploadPath)) - { - uploadDifArguments += $", '{ConvertSlashes(symbolUploadPath)}'"; - } - else - { - throw new DirectoryNotFoundException($"Failed to find the symbols directory at {symbolUploadPath}"); - } + throw new DirectoryNotFoundException($"Failed to find the symbols directory at {symbolUploadPath}"); } } + } - var symbolUploadText = _symbolUploadTaskFormat; - symbolUploadText = symbolUploadText.Trim(); - symbolUploadText = symbolUploadText.Replace(SentryCliMarker, sentryCliPath); - symbolUploadText = symbolUploadText.Replace(UploadArgsMarker, uploadDifArguments); - symbolUploadText = symbolUploadText.Replace(MappingPathMarker, _mappingFilePath); + var symbolUploadText = _symbolUploadTaskFormat; + symbolUploadText = symbolUploadText.Trim(); + symbolUploadText = symbolUploadText.Replace(SentryCliMarker, sentryCliPath); + symbolUploadText = symbolUploadText.Replace(UploadArgsMarker, uploadDifArguments); + symbolUploadText = symbolUploadText.Replace(MappingPathMarker, _mappingFilePath); - using var streamWriter = File.AppendText(_gradleScriptPath); - streamWriter.WriteLine(SymbolUploadTaskStartComment); - streamWriter.WriteLine(symbolUploadText); - streamWriter.WriteLine(SymbolUploadTaskEndComment); - } + using var streamWriter = File.AppendText(_gradleScriptPath); + streamWriter.WriteLine(SymbolUploadTaskStartComment); + streamWriter.WriteLine(symbolUploadText); + streamWriter.WriteLine(SymbolUploadTaskEndComment); + } - private string LoadGradleScript() + private string LoadGradleScript() + { + if (!File.Exists(_gradleScriptPath)) { - if (!File.Exists(_gradleScriptPath)) - { - throw new FileNotFoundException($"Failed to find the gradle config.", _gradleScriptPath); - } - - return File.ReadAllText(_gradleScriptPath); + throw new FileNotFoundException($"Failed to find the gradle config.", _gradleScriptPath); } - public void RemoveUploadFromGradleFile() + return File.ReadAllText(_gradleScriptPath); + } + + public void RemoveUploadFromGradleFile() + { + _logger.LogDebug("Removing the upload task from the gradle project."); + var gradleBuildFile = LoadGradleScript(); + if (!gradleBuildFile.Contains("sentry.properties")) { - _logger.LogDebug("Removing the upload task from the gradle project."); - var gradleBuildFile = LoadGradleScript(); - if (!gradleBuildFile.Contains("sentry.properties")) - { - _logger.LogDebug("No previous upload task found."); - return; - } + _logger.LogDebug("No previous upload task found."); + return; + } + + var regex = new Regex(Regex.Escape(SymbolUploadTaskStartComment) + ".*" + Regex.Escape(SymbolUploadTaskEndComment), RegexOptions.Singleline); + gradleBuildFile = regex.Replace(gradleBuildFile, ""); - var regex = new Regex(Regex.Escape(SymbolUploadTaskStartComment) + ".*" + Regex.Escape(SymbolUploadTaskEndComment), RegexOptions.Singleline); - gradleBuildFile = regex.Replace(gradleBuildFile, ""); + using var streamWriter = File.CreateText(_gradleScriptPath); + streamWriter.Write(gradleBuildFile); + } - using var streamWriter = File.CreateText(_gradleScriptPath); - streamWriter.Write(gradleBuildFile); + public void TryCopySymbolsToGradleProject(IApplication? application = null) + { + if (!_isExporting) + { + return; } - public void TryCopySymbolsToGradleProject(IApplication? application = null) + _logger.LogInfo("Copying debug symbols to exported gradle project."); + var targetRoot = Path.Combine(_gradleProjectPath, "symbols"); + foreach (var symbolUploadPath in _symbolUploadPaths) { - if (!_isExporting) + // Seems like not all paths exist all the time... e.g. Unity 2021.2.21 misses RelativeAndroidPathNew. + if (!Directory.Exists(symbolUploadPath)) { - return; + continue; } - _logger.LogInfo("Copying debug symbols to exported gradle project."); - var targetRoot = Path.Combine(_gradleProjectPath, "symbols"); - foreach (var symbolUploadPath in _symbolUploadPaths) + foreach (var sourcePath in Directory.GetFiles(symbolUploadPath, "*.so", SearchOption.AllDirectories)) { - // Seems like not all paths exist all the time... e.g. Unity 2021.2.21 misses RelativeAndroidPathNew. - if (!Directory.Exists(symbolUploadPath)) - { - continue; - } - - foreach (var sourcePath in Directory.GetFiles(symbolUploadPath, "*.so", SearchOption.AllDirectories)) - { - var targetPath = sourcePath.Replace(symbolUploadPath, targetRoot); - _logger.LogDebug("Copying '{0}' to '{1}'", sourcePath, targetPath); + var targetPath = sourcePath.Replace(symbolUploadPath, targetRoot); + _logger.LogDebug("Copying '{0}' to '{1}'", sourcePath, targetPath); - Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); - File.Copy(sourcePath, targetPath, true); - } + Directory.CreateDirectory(Path.GetDirectoryName(targetPath)); + File.Copy(sourcePath, targetPath, true); } } + } - internal List GetSymbolUploadPaths(IApplication? application = null) + internal List GetSymbolUploadPaths(IApplication? application = null) + { + var paths = new List(); + if (IsNewBuildingBackend(application)) { - var paths = new List(); - if (IsNewBuildingBackend(application)) - { - _logger.LogInfo("Unity version 2021.2 or newer detected. Root for symbols upload: 'Library'."); - paths.Add(Path.Combine(_unityProjectPath, RelativeBuildOutputPathNew)); - paths.Add(Path.Combine(_unityProjectPath, RelativeAndroidPathNew)); - } - else - { - _logger.LogInfo("Unity version 2021.1 or older detected. Root for symbols upload: 'Temp'."); - paths.Add(Path.Combine(_unityProjectPath, RelativeBuildOutputPathOld)); - paths.Add(Path.Combine(_unityProjectPath, RelativeGradlePathOld)); - } - - return paths; + _logger.LogInfo("Unity version 2021.2 or newer detected. Root for symbols upload: 'Library'."); + paths.Add(Path.Combine(_unityProjectPath, RelativeBuildOutputPathNew)); + paths.Add(Path.Combine(_unityProjectPath, RelativeAndroidPathNew)); + } + else + { + _logger.LogInfo("Unity version 2021.1 or older detected. Root for symbols upload: 'Temp'."); + paths.Add(Path.Combine(_unityProjectPath, RelativeBuildOutputPathOld)); + paths.Add(Path.Combine(_unityProjectPath, RelativeGradlePathOld)); } - // Starting from 2021.2 Unity caches the build output inside 'Library' instead of 'Temp' - internal static bool IsNewBuildingBackend(IApplication? application = null) => SentryUnityVersion.IsNewerOrEqualThan("2021.2", application); + return paths; + } - // Gradle doesn't support backslashes on path (Windows) so converting to forward slashes - internal static string ConvertSlashes(string path) => path.Replace(@"\", "/"); + // Starting from 2021.2 Unity caches the build output inside 'Library' instead of 'Temp' + internal static bool IsNewBuildingBackend(IApplication? application = null) => SentryUnityVersion.IsNewerOrEqualThan("2021.2", application); - private void CheckMapping(StringBuilder stringBuilder) - { - if (!_isMinifyEnabled) - return; + // Gradle doesn't support backslashes on path (Windows) so converting to forward slashes + internal static string ConvertSlashes(string path) => path.Replace(@"\", "/"); - stringBuilder.AppendLine(" println 'Uploading mapping file to Sentry.'"); - if (!_isExporting) - { - var logsDir = $"{ConvertSlashes(_unityProjectPath)}/Logs"; - Directory.CreateDirectory(logsDir); - stringBuilder.AppendLine($" def mappingLogFilePath = '{logsDir}/{MappingUploadLogName}'"); - stringBuilder.AppendLine($" def mappingLogFile = new FileOutputStream(mappingLogFilePath)"); - if (!_isExporting && _ignoreCliErrors) - { - stringBuilder.AppendLine(" try {"); - } - } - stringBuilder.AppendLine(" exec {"); - stringBuilder.AppendLine(" environment 'SENTRY_PROPERTIES', './sentry.properties'"); - stringBuilder.AppendLine($" executable '{SentryCliMarker}'"); - stringBuilder.AppendLine($" args = ['upload-proguard', {MappingPathMarker}]"); - if (!_isExporting) - { - stringBuilder.AppendLine(" standardOutput mappingLogFile"); - stringBuilder.AppendLine(" errorOutput mappingLogFile"); - } + private void CheckMapping(StringBuilder stringBuilder) + { + if (!_isMinifyEnabled) + return; - stringBuilder.AppendLine(" }"); + stringBuilder.AppendLine(" println 'Uploading mapping file to Sentry.'"); + if (!_isExporting) + { + var logsDir = $"{ConvertSlashes(_unityProjectPath)}/Logs"; + Directory.CreateDirectory(logsDir); + stringBuilder.AppendLine($" def mappingLogFilePath = '{logsDir}/{MappingUploadLogName}'"); + stringBuilder.AppendLine($" def mappingLogFile = new FileOutputStream(mappingLogFilePath)"); if (!_isExporting && _ignoreCliErrors) { - stringBuilder.AppendLine(" } catch (exception) {"); - stringBuilder.AppendLine(" def file = new File(mappingLogFilePath)"); - stringBuilder.AppendLine(" file.append('===ERROR===' + exception)"); - stringBuilder.AppendLine(" }"); + stringBuilder.AppendLine(" try {"); } } + stringBuilder.AppendLine(" exec {"); + stringBuilder.AppendLine(" environment 'SENTRY_PROPERTIES', './sentry.properties'"); + stringBuilder.AppendLine($" executable '{SentryCliMarker}'"); + stringBuilder.AppendLine($" args = ['upload-proguard', {MappingPathMarker}]"); + if (!_isExporting) + { + stringBuilder.AppendLine(" standardOutput mappingLogFile"); + stringBuilder.AppendLine(" errorOutput mappingLogFile"); + } - private string GetMappingFilePath(IApplication? application) + stringBuilder.AppendLine(" }"); + if (!_isExporting && _ignoreCliErrors) { - var gradleRelativePath = IsNewBuildingBackend(application) - ? "Library/Bee/Android/Prj/IL2CPP/Gradle" - : "Temp/gradleOut"; + stringBuilder.AppendLine(" } catch (exception) {"); + stringBuilder.AppendLine(" def file = new File(mappingLogFilePath)"); + stringBuilder.AppendLine(" file.append('===ERROR===' + exception)"); + stringBuilder.AppendLine(" }"); + } + } - string mappingPathFormat; - var buildType = EditorUserBuildSettings.development ? "debug" : "release"; - if (_isExporting) - { - mappingPathFormat = - "file(\"${rootDir}/launcher/build/outputs/mapping/{0}/mapping.txt\").absolutePath"; - } - else - { - var gradleProjectPath = Path.Combine(_unityProjectPath, gradleRelativePath); - mappingPathFormat = Path.Combine(gradleProjectPath, "launcher/build/outputs/mapping/{0}/mapping.txt"); - mappingPathFormat = $"'{mappingPathFormat}'"; - } + private string GetMappingFilePath(IApplication? application) + { + var gradleRelativePath = IsNewBuildingBackend(application) + ? "Library/Bee/Android/Prj/IL2CPP/Gradle" + : "Temp/gradleOut"; - var mappingPath = mappingPathFormat.Replace("{0}", buildType); - return mappingPath; + string mappingPathFormat; + var buildType = EditorUserBuildSettings.development ? "debug" : "release"; + if (_isExporting) + { + mappingPathFormat = + "file(\"${rootDir}/launcher/build/outputs/mapping/{0}/mapping.txt\").absolutePath"; } + else + { + var gradleProjectPath = Path.Combine(_unityProjectPath, gradleRelativePath); + mappingPathFormat = Path.Combine(gradleProjectPath, "launcher/build/outputs/mapping/{0}/mapping.txt"); + mappingPathFormat = $"'{mappingPathFormat}'"; + } + + var mappingPath = mappingPathFormat.Replace("{0}", buildType); + return mappingPath; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Android/GradleSetup.cs b/src/Sentry.Unity.Editor/Android/GradleSetup.cs index b2280a888..9efd5509e 100644 --- a/src/Sentry.Unity.Editor/Android/GradleSetup.cs +++ b/src/Sentry.Unity.Editor/Android/GradleSetup.cs @@ -2,69 +2,68 @@ using System.Text.RegularExpressions; using Sentry.Extensibility; -namespace Sentry.Unity.Editor.Android +namespace Sentry.Unity.Editor.Android; + +internal class GradleSetup { - internal class GradleSetup - { - private readonly IDiagnosticLogger _logger; + private readonly IDiagnosticLogger _logger; - private const string AndroidMarker = "android {"; - private const string SdkDependenciesFull = SdkDependencies + "\n}\n\n" + AndroidMarker; + private const string AndroidMarker = "android {"; + private const string SdkDependenciesFull = SdkDependencies + "\n}\n\n" + AndroidMarker; - public const string SdkDependencies = @"dependencies { + public const string SdkDependencies = @"dependencies { implementation(name: 'sentry-android-ndk-release', ext:'aar') implementation(name: 'sentry-android-core-release', ext:'aar')"; - public const string DependenciesAddedMessage = "The Sentry Gradle dependencies have already been added."; - private readonly string _unityLibraryGradle; - - public GradleSetup(IDiagnosticLogger logger, string gradleProjectPath) - { - _logger = logger; - _unityLibraryGradle = Path.Combine(gradleProjectPath, "unityLibrary", "build.gradle"); - } + public const string DependenciesAddedMessage = "The Sentry Gradle dependencies have already been added."; + private readonly string _unityLibraryGradle; - public void UpdateGradleProject() - { - _logger.LogInfo("Adding Sentry to the gradle project."); - var fileContent = LoadGradleScript(_unityLibraryGradle); - fileContent = AddSentryToGradle(fileContent); - File.WriteAllText(_unityLibraryGradle, fileContent); - } + public GradleSetup(IDiagnosticLogger logger, string gradleProjectPath) + { + _logger = logger; + _unityLibraryGradle = Path.Combine(gradleProjectPath, "unityLibrary", "build.gradle"); + } - public void ClearGradleProject() - { - _logger.LogInfo("Removing Sentry from the gradle project."); - var fileContent = LoadGradleScript(_unityLibraryGradle); + public void UpdateGradleProject() + { + _logger.LogInfo("Adding Sentry to the gradle project."); + var fileContent = LoadGradleScript(_unityLibraryGradle); + fileContent = AddSentryToGradle(fileContent); + File.WriteAllText(_unityLibraryGradle, fileContent); + } - if (!fileContent.Contains(SdkDependenciesFull)) - { - _logger.LogDebug("The Sentry Gradle dependencies have already been removed."); - return; - } + public void ClearGradleProject() + { + _logger.LogInfo("Removing Sentry from the gradle project."); + var fileContent = LoadGradleScript(_unityLibraryGradle); - fileContent = fileContent.Replace(SdkDependenciesFull, AndroidMarker); - File.WriteAllText(_unityLibraryGradle, fileContent); + if (!fileContent.Contains(SdkDependenciesFull)) + { + _logger.LogDebug("The Sentry Gradle dependencies have already been removed."); + return; } - public string AddSentryToGradle(string fileContent) - { - if (fileContent.Contains(SdkDependenciesFull)) - { - _logger.LogDebug(DependenciesAddedMessage); - return fileContent; - } + fileContent = fileContent.Replace(SdkDependenciesFull, AndroidMarker); + File.WriteAllText(_unityLibraryGradle, fileContent); + } - var regex = new Regex(Regex.Escape(AndroidMarker)); - return regex.Replace(fileContent, SdkDependenciesFull, 1); + public string AddSentryToGradle(string fileContent) + { + if (fileContent.Contains(SdkDependenciesFull)) + { + _logger.LogDebug(DependenciesAddedMessage); + return fileContent; } - internal static string LoadGradleScript(string path) + var regex = new Regex(Regex.Escape(AndroidMarker)); + return regex.Replace(fileContent, SdkDependenciesFull, 1); + } + + internal static string LoadGradleScript(string path) + { + if (!File.Exists(path)) { - if (!File.Exists(path)) - { - throw new FileNotFoundException("Failed to find the gradle config.", path); - } - return File.ReadAllText(path); + throw new FileNotFoundException("Failed to find the gradle config.", path); } + return File.ReadAllText(path); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs b/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs index 45b200d94..bb040ee0b 100644 --- a/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs +++ b/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs @@ -6,151 +6,150 @@ using UnityEditor.Build.Reporting; using UnityEngine; -namespace Sentry.Unity.Editor.Android +namespace Sentry.Unity.Editor.Android; + +public class PostBuildCheck : IPostprocessBuildWithReport { - public class PostBuildCheck : IPostprocessBuildWithReport - { - public int callbackOrder { get; } = 117; + public int callbackOrder { get; } = 117; - public void OnPostprocessBuild(BuildReport report) - { - var checker = new UploadTaskChecker(SentryScriptableObject.ConfiguredBuildTimeOptions); - checker.CheckUploadTaskResult(); - } + public void OnPostprocessBuild(BuildReport report) + { + var checker = new UploadTaskChecker(SentryScriptableObject.ConfiguredBuildTimeOptions); + checker.CheckUploadTaskResult(); } +} + +internal class UploadTaskChecker +{ + private readonly SentryUnityOptions? _options; + private readonly SentryCliOptions? _sentryCliOptions; + private readonly IDiagnosticLogger _logger; - internal class UploadTaskChecker + internal UploadTaskChecker(Func<(SentryUnityOptions?, SentryCliOptions?)> getOptions) { - private readonly SentryUnityOptions? _options; - private readonly SentryCliOptions? _sentryCliOptions; - private readonly IDiagnosticLogger _logger; + (_options, _sentryCliOptions) = getOptions(); + _logger = _options?.DiagnosticLogger ?? new UnityLogger(_options ?? new SentryUnityOptions()); + } - internal UploadTaskChecker(Func<(SentryUnityOptions?, SentryCliOptions?)> getOptions) + internal void CheckUploadTaskResult() + { + if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) { - (_options, _sentryCliOptions) = getOptions(); - _logger = _options?.DiagnosticLogger ?? new UnityLogger(_options ?? new SentryUnityOptions()); + _logger.LogDebug("Target platform is not Android. Will not validate the upload task."); + return; } - internal void CheckUploadTaskResult() + if (EditorUserBuildSettings.exportAsGoogleAndroidProject) { - if (EditorUserBuildSettings.activeBuildTarget != BuildTarget.Android) - { - _logger.LogDebug("Target platform is not Android. Will not validate the upload task."); - return; - } + _logger.LogDebug("The task is not executed during export. Will not validate the upload task."); + return; + } - if (EditorUserBuildSettings.exportAsGoogleAndroidProject) - { - _logger.LogDebug("The task is not executed during export. Will not validate the upload task."); - return; - } + // ReSharper disable once Unity.NoNullPropagation + if (!(_sentryCliOptions?.UploadSymbols ?? false)) + { + _logger.LogDebug("Symbol Upload is disabled. Will not validate the upload task."); + return; + } - // ReSharper disable once Unity.NoNullPropagation - if (!(_sentryCliOptions?.UploadSymbols ?? false)) - { - _logger.LogDebug("Symbol Upload is disabled. Will not validate the upload task."); - return; - } + var projectDir = Directory.GetParent(Application.dataPath); + if (projectDir == null) + { + _logger.LogDebug("Could not find Unity Project path. Will not check upload symbols task result."); + return; + } - var projectDir = Directory.GetParent(Application.dataPath); - if (projectDir == null) - { - _logger.LogDebug("Could not find Unity Project path. Will not check upload symbols task result."); - return; - } + var unityProjectPath = projectDir.FullName; + CheckUploadSymbolLogs(unityProjectPath); + CheckUploadMappingLogs(unityProjectPath); + } - var unityProjectPath = projectDir.FullName; - CheckUploadSymbolLogs(unityProjectPath); - CheckUploadMappingLogs(unityProjectPath); + private void CheckUploadSymbolLogs(string unityProjectPath) + { + var symbolUploadLogPath = Path.Combine(unityProjectPath, "Logs", DebugSymbolUpload.SymbolUploadLogName); + var hasSymbolError = HasError(symbolUploadLogPath, out var symbolError, out var symbolLog); + if (hasSymbolError) + { + _logger.LogWarning($"Symbol upload task error: {symbolError}"); } - private void CheckUploadSymbolLogs(string unityProjectPath) + LogFileContent("Symbol upload log file content:", symbolLog, hasSymbolError); + File.WriteAllText(symbolUploadLogPath, symbolLog); // Clean up the log file + } + + private void CheckUploadMappingLogs(string unityProjectPath) + { + if (!AndroidUtils.ShouldUploadMapping()) { - var symbolUploadLogPath = Path.Combine(unityProjectPath, "Logs", DebugSymbolUpload.SymbolUploadLogName); - var hasSymbolError = HasError(symbolUploadLogPath, out var symbolError, out var symbolLog); - if (hasSymbolError) - { - _logger.LogWarning($"Symbol upload task error: {symbolError}"); - } + _logger.LogDebug("Minification is disabled. Will not check upload mapping task result."); + return; + } - LogFileContent("Symbol upload log file content:", symbolLog, hasSymbolError); - File.WriteAllText(symbolUploadLogPath, symbolLog); // Clean up the log file + var mappingUploadLogPath = Path.Combine(unityProjectPath, "Logs", DebugSymbolUpload.MappingUploadLogName); + var hasMappingError = HasError(mappingUploadLogPath, out var mappingError, out var mappingLog); + if (hasMappingError) + { + _logger.LogWarning($"Mapping upload task error: {mappingError}"); } - private void CheckUploadMappingLogs(string unityProjectPath) + LogFileContent("Mapping upload log file content:", mappingLog, hasMappingError); + File.WriteAllText(mappingUploadLogPath, mappingLog); // Clean up the log file + } + + private bool HasError(string filePath, out string error, out string fileContent) + { + fileContent = error = string.Empty; + const string errorMarker = "===ERROR==="; + if (!File.Exists(filePath)) { - if (!AndroidUtils.ShouldUploadMapping()) - { - _logger.LogDebug("Minification is disabled. Will not check upload mapping task result."); - return; - } + return false; + } - var mappingUploadLogPath = Path.Combine(unityProjectPath, "Logs", DebugSymbolUpload.MappingUploadLogName); - var hasMappingError = HasError(mappingUploadLogPath, out var mappingError, out var mappingLog); - if (hasMappingError) - { - _logger.LogWarning($"Mapping upload task error: {mappingError}"); - } + var text = File.ReadAllText(filePath); + if (!text.Contains(errorMarker)) + { + fileContent = text; + return false; + } - LogFileContent("Mapping upload log file content:", mappingLog, hasMappingError); - File.WriteAllText(mappingUploadLogPath, mappingLog); // Clean up the log file + var index = text.IndexOf(errorMarker, StringComparison.InvariantCulture); + if (index < 0) + { + return false; } - private bool HasError(string filePath, out string error, out string fileContent) + fileContent = text.Substring(0, index); + error = text.Substring(index + errorMarker.Length); + return !string.IsNullOrEmpty(error); + } + + private void LogFileContent(string title, string fileContent, bool hasError) + { + var logFunction = new Action(message => { - fileContent = error = string.Empty; - const string errorMarker = "===ERROR==="; - if (!File.Exists(filePath)) + if (hasError) { - return false; + Debug.LogWarning(message); } - - var text = File.ReadAllText(filePath); - if (!text.Contains(errorMarker)) + else { - fileContent = text; - return false; + Debug.Log(message); } + }); - var index = text.IndexOf(errorMarker, StringComparison.InvariantCulture); - if (index < 0) - { - return false; - } + logFunction(title); - fileContent = text.Substring(0, index); - error = text.Substring(index + errorMarker.Length); - return !string.IsNullOrEmpty(error); + const int maxLogLength = 8192; + if (fileContent.Length < maxLogLength) + { + logFunction(fileContent); + return; } - private void LogFileContent(string title, string fileContent, bool hasError) + for (var i = 0; i < fileContent.Length; i += maxLogLength) { - var logFunction = new Action(message => - { - if (hasError) - { - Debug.LogWarning(message); - } - else - { - Debug.Log(message); - } - }); - - logFunction(title); - - const int maxLogLength = 8192; - if (fileContent.Length < maxLogLength) - { - logFunction(fileContent); - return; - } - - for (var i = 0; i < fileContent.Length; i += maxLogLength) - { - var chunkLength = maxLogLength + i > fileContent.Length ? fileContent.Length - i : maxLogLength; - logFunction(fileContent.Substring(i, chunkLength)); - } + var chunkLength = maxLogLength + i > fileContent.Length ? fileContent.Length - i : maxLogLength; + logFunction(fileContent.Substring(i, chunkLength)); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Android/ProguardSetup.cs b/src/Sentry.Unity.Editor/Android/ProguardSetup.cs index b204b130a..8bb9affd5 100644 --- a/src/Sentry.Unity.Editor/Android/ProguardSetup.cs +++ b/src/Sentry.Unity.Editor/Android/ProguardSetup.cs @@ -3,110 +3,109 @@ using System.Text.RegularExpressions; using Sentry.Extensibility; -namespace Sentry.Unity.Editor.Android +namespace Sentry.Unity.Editor.Android; + +internal class ProguardSetup { - internal class ProguardSetup + private readonly IDiagnosticLogger _logger; + private readonly string _gradleProjectPath; + private readonly string _gradleScriptPath; + public const string RuleFileName = "proguard-sentry-unity.pro"; + + public ProguardSetup(IDiagnosticLogger logger, string gradleProjectPath) { - private readonly IDiagnosticLogger _logger; - private readonly string _gradleProjectPath; - private readonly string _gradleScriptPath; - public const string RuleFileName = "proguard-sentry-unity.pro"; + _logger = logger; + _gradleProjectPath = Path.Combine(gradleProjectPath, "unityLibrary"); + _gradleScriptPath = Path.Combine(_gradleProjectPath, "build.gradle"); + } - public ProguardSetup(IDiagnosticLogger logger, string gradleProjectPath) + public void RemoveFromGradleProject() + { + _logger.LogDebug("Removing Proguard rules from the gradle project."); + var gradle = LoadGradleScript(); + if (!gradle.Contains(RuleFileName)) { - _logger = logger; - _gradleProjectPath = Path.Combine(gradleProjectPath, "unityLibrary"); - _gradleScriptPath = Path.Combine(_gradleProjectPath, "build.gradle"); + _logger.LogDebug("No reference to the Proguard ruleset {0} in {1}.", RuleFileName, _gradleScriptPath); } - - public void RemoveFromGradleProject() + else { - _logger.LogDebug("Removing Proguard rules from the gradle project."); - var gradle = LoadGradleScript(); - if (!gradle.Contains(RuleFileName)) + var pattern = string.Empty; + if (gradle.Contains("consumerProguardFiles")) { - _logger.LogDebug("No reference to the Proguard ruleset {0} in {1}.", RuleFileName, _gradleScriptPath); + _logger.LogDebug("Detected `consumerProguardFiles`. Removing Sentry rules."); + pattern = @"(\s+consumerProguardFiles .*), *'"; } - else + else if (gradle.Contains("proguardFiles")) { - var pattern = string.Empty; - if (gradle.Contains("consumerProguardFiles")) - { - _logger.LogDebug("Detected `consumerProguardFiles`. Removing Sentry rules."); - pattern = @"(\s+consumerProguardFiles .*), *'"; - } - else if (gradle.Contains("proguardFiles")) - { - _logger.LogDebug("Detected `proguardFiles`. Removing Sentry rules."); - pattern = @"(\s+proguardFiles .*), *'"; - } - - var gradleNew = Regex.Replace(gradle, pattern + RuleFileName + "'", "$1"); - if (gradle.Length == gradleNew.Length) - { - throw new Exception($"Couldn't remove Proguard rule {RuleFileName} from {_gradleScriptPath}."); - } - File.WriteAllText(_gradleScriptPath, gradleNew); + _logger.LogDebug("Detected `proguardFiles`. Removing Sentry rules."); + pattern = @"(\s+proguardFiles .*), *'"; } - var ruleFile = Path.Combine(_gradleProjectPath, RuleFileName); - if (!File.Exists(ruleFile)) + var gradleNew = Regex.Replace(gradle, pattern + RuleFileName + "'", "$1"); + if (gradle.Length == gradleNew.Length) { - _logger.LogDebug("No Proguard ruleset file at {0} - nothing to remove.", ruleFile); - } - else - { - File.Delete(ruleFile); + throw new Exception($"Couldn't remove Proguard rule {RuleFileName} from {_gradleScriptPath}."); } + File.WriteAllText(_gradleScriptPath, gradleNew); } - public void AddToGradleProject() + var ruleFile = Path.Combine(_gradleProjectPath, RuleFileName); + if (!File.Exists(ruleFile)) { - _logger.LogInfo("Adding Proguard rules to the gradle project."); - var gradle = LoadGradleScript(); + _logger.LogDebug("No Proguard ruleset file at {0} - nothing to remove.", ruleFile); + } + else + { + File.Delete(ruleFile); + } + } - var ruleFile = Path.Combine(_gradleProjectPath, RuleFileName); - _logger.LogDebug("Writing proguard rule file to {0}.", ruleFile); - File.Copy(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Android/{RuleFileName}"), ruleFile, true); + public void AddToGradleProject() + { + _logger.LogInfo("Adding Proguard rules to the gradle project."); + var gradle = LoadGradleScript(); + + var ruleFile = Path.Combine(_gradleProjectPath, RuleFileName); + _logger.LogDebug("Writing proguard rule file to {0}.", ruleFile); + File.Copy(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Android/{RuleFileName}"), ruleFile, true); - if (gradle.Contains(RuleFileName)) + if (gradle.Contains(RuleFileName)) + { + _logger.LogDebug($"Proguard rule {RuleFileName} has already been added to {_gradleScriptPath} `consumerProguardFiles` in a previous build."); + } + else + { + string pattern; + if (gradle.Contains("consumerProguardFiles")) + { + _logger.LogDebug("Detected `consumerProguardFiles`. Adding Sentry rules."); + pattern = @"(\s+consumerProguardFiles [^\r\n]*)"; + } + else if (gradle.Contains("proguardFiles")) { - _logger.LogDebug($"Proguard rule {RuleFileName} has already been added to {_gradleScriptPath} `consumerProguardFiles` in a previous build."); + _logger.LogDebug("Detected `proguardFiles`. Adding Sentry rules."); + pattern = @"(\s+proguardFiles [^\r\n]*)"; } else { - string pattern; - if (gradle.Contains("consumerProguardFiles")) - { - _logger.LogDebug("Detected `consumerProguardFiles`. Adding Sentry rules."); - pattern = @"(\s+consumerProguardFiles [^\r\n]*)"; - } - else if (gradle.Contains("proguardFiles")) - { - _logger.LogDebug("Detected `proguardFiles`. Adding Sentry rules."); - pattern = @"(\s+proguardFiles [^\r\n]*)"; - } - else - { - throw new Exception($"Failed to find 'proguard rule section' in gradle file at: {_gradleScriptPath} - no `consumerProguardFiles` or `proguardFiles` found."); - } + throw new Exception($"Failed to find 'proguard rule section' in gradle file at: {_gradleScriptPath} - no `consumerProguardFiles` or `proguardFiles` found."); + } - var gradleNew = Regex.Replace(gradle, pattern, "$1, '" + RuleFileName + "'"); - if (gradle.Length == gradleNew.Length) - { - throw new Exception($"Couldn't add Proguard rule {RuleFileName} to {_gradleScriptPath}."); - } - File.WriteAllText(_gradleScriptPath, gradleNew); + var gradleNew = Regex.Replace(gradle, pattern, "$1, '" + RuleFileName + "'"); + if (gradle.Length == gradleNew.Length) + { + throw new Exception($"Couldn't add Proguard rule {RuleFileName} to {_gradleScriptPath}."); } + File.WriteAllText(_gradleScriptPath, gradleNew); } + } - private string LoadGradleScript() + private string LoadGradleScript() + { + if (!File.Exists(_gradleScriptPath)) { - if (!File.Exists(_gradleScriptPath)) - { - throw new FileNotFoundException("Failed to find the gradle config.", _gradleScriptPath); - } - return File.ReadAllText(_gradleScriptPath); + throw new FileNotFoundException("Failed to find the gradle config.", _gradleScriptPath); } + return File.ReadAllText(_gradleScriptPath); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs index 4f6d325e6..6c6b5607b 100644 --- a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs +++ b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs @@ -8,112 +8,111 @@ using UnityEditor.Build.Reporting; using UnityEngine; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +public class SentryPerformanceAutoInstrumentation : IPostBuildPlayerScriptDLLs { - public class SentryPerformanceAutoInstrumentation : IPostBuildPlayerScriptDLLs + public int callbackOrder { get; } + public void OnPostBuildPlayerScriptDLLs(BuildReport report) { - public int callbackOrder { get; } - public void OnPostBuildPlayerScriptDLLs(BuildReport report) + var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); + if (options == null) { - var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); - if (options == null) - { - return; - } - - var logger = options.DiagnosticLogger ?? new UnityLogger(options); + return; + } - if (!options.IsValid()) - { - logger.LogDebug("Performance Auto Instrumentation disabled."); - return; - } + var logger = options.DiagnosticLogger ?? new UnityLogger(options); - if (options.TracesSampleRate <= 0.0f || !options.PerformanceAutoInstrumentationEnabled) - { - logger.LogInfo("Performance Auto Instrumentation has been disabled."); - return; - } + if (!options.IsValid()) + { + logger.LogDebug("Performance Auto Instrumentation disabled."); + return; + } - logger.LogInfo("Running Performance Auto Instrumentation in PostBuildScriptPhase."); + if (options.TracesSampleRate <= 0.0f || !options.PerformanceAutoInstrumentationEnabled) + { + logger.LogInfo("Performance Auto Instrumentation has been disabled."); + return; + } - var stopwatch = Stopwatch.StartNew(); + logger.LogInfo("Running Performance Auto Instrumentation in PostBuildScriptPhase."); - try - { - var workingDirectory = Path.Combine(Application.dataPath, "..", "Temp", "StagingArea", "Data", "Managed"); - var playerReaderWriter = SentryPlayerReaderWriter.ReadAssemblies(workingDirectory); - ModifyPlayerAssembly(logger, playerReaderWriter); - } - catch (Exception e) - { - logger.LogError(e, "Failed to add the performance auto instrumentation. " + - "The assembly has not been modified."); - } + var stopwatch = Stopwatch.StartNew(); - stopwatch.Stop(); - logger.LogInfo("Auto Instrumentation finished in {0} seconds.", stopwatch.Elapsed.Seconds); + try + { + var workingDirectory = Path.Combine(Application.dataPath, "..", "Temp", "StagingArea", "Data", "Managed"); + var playerReaderWriter = SentryPlayerReaderWriter.ReadAssemblies(workingDirectory); + ModifyPlayerAssembly(logger, playerReaderWriter); + } + catch (Exception e) + { + logger.LogError(e, "Failed to add the performance auto instrumentation. " + + "The assembly has not been modified."); } - private static void ModifyPlayerAssembly(IDiagnosticLogger logger, SentryPlayerReaderWriter playerReaderWriter) + stopwatch.Stop(); + logger.LogInfo("Auto Instrumentation finished in {0} seconds.", stopwatch.Elapsed.Seconds); + } + + private static void ModifyPlayerAssembly(IDiagnosticLogger logger, SentryPlayerReaderWriter playerReaderWriter) + { + var getInstanceMethod = playerReaderWriter.ImportSentryMonoBehaviourMethod("get_Instance"); + var startAwakeSpanMethod = playerReaderWriter.ImportSentryMonoBehaviourMethod("StartAwakeSpan", new[] { typeof(MonoBehaviour) }); + var finishAwakeSpanMethod = playerReaderWriter.ImportSentryMonoBehaviourMethod("FinishAwakeSpan"); + + var monoBehaviourReference = playerReaderWriter.ImportType(typeof(MonoBehaviour)); + + foreach (var type in playerReaderWriter.GetTypes()) { - var getInstanceMethod = playerReaderWriter.ImportSentryMonoBehaviourMethod("get_Instance"); - var startAwakeSpanMethod = playerReaderWriter.ImportSentryMonoBehaviourMethod("StartAwakeSpan", new[] { typeof(MonoBehaviour) }); - var finishAwakeSpanMethod = playerReaderWriter.ImportSentryMonoBehaviourMethod("FinishAwakeSpan"); + if (type.BaseType?.FullName != monoBehaviourReference?.FullName) + { + continue; + } - var monoBehaviourReference = playerReaderWriter.ImportType(typeof(MonoBehaviour)); + logger.LogDebug("\tChecking: '{0}'", type.FullName); - foreach (var type in playerReaderWriter.GetTypes()) + foreach (var method in type.Methods.Where(method => method.Name == "Awake")) { - if (type.BaseType?.FullName != monoBehaviourReference?.FullName) + logger.LogDebug("\tDetected 'Awake' method."); + + if (method.Body is null) { + logger.LogDebug("\tMethod body is null. Skipping."); continue; } - logger.LogDebug("\tChecking: '{0}'", type.FullName); - - foreach (var method in type.Methods.Where(method => method.Name == "Awake")) + if (method.Body.Instructions is null) { - logger.LogDebug("\tDetected 'Awake' method."); - - if (method.Body is null) - { - logger.LogDebug("\tMethod body is null. Skipping."); - continue; - } - - if (method.Body.Instructions is null) - { - logger.LogDebug("\tInstructions are null. Skipping."); - continue; - } + logger.LogDebug("\tInstructions are null. Skipping."); + continue; + } - logger.LogDebug("\t\tAdding 'Start Awake Span'."); + logger.LogDebug("\t\tAdding 'Start Awake Span'."); - var processor = method.Body.GetILProcessor(); + var processor = method.Body.GetILProcessor(); - // Adding in reverse order because we're inserting *before* the 0ths element - processor.InsertBefore(method.Body.Instructions[0], processor.Create(OpCodes.Callvirt, startAwakeSpanMethod)); - processor.InsertBefore(method.Body.Instructions[0], processor.Create(OpCodes.Ldarg_0)); - processor.InsertBefore(method.Body.Instructions[0], processor.Create(OpCodes.Call, getInstanceMethod)); + // Adding in reverse order because we're inserting *before* the 0ths element + processor.InsertBefore(method.Body.Instructions[0], processor.Create(OpCodes.Callvirt, startAwakeSpanMethod)); + processor.InsertBefore(method.Body.Instructions[0], processor.Create(OpCodes.Ldarg_0)); + processor.InsertBefore(method.Body.Instructions[0], processor.Create(OpCodes.Call, getInstanceMethod)); - logger.LogDebug("\t\tAdding 'Finish Awake Span'."); + logger.LogDebug("\t\tAdding 'Finish Awake Span'."); - // We're checking the instructions for OpCode.Ret so we can insert the finish span instruction before it. - // Iterating over the collection backwards because we're modifying it's length - for (var i = method.Body.Instructions.Count - 1; i >= 0; i--) + // We're checking the instructions for OpCode.Ret so we can insert the finish span instruction before it. + // Iterating over the collection backwards because we're modifying it's length + for (var i = method.Body.Instructions.Count - 1; i >= 0; i--) + { + if (method.Body.Instructions[i].OpCode == OpCodes.Ret) { - if (method.Body.Instructions[i].OpCode == OpCodes.Ret) - { - processor.InsertBefore(method.Body.Instructions[i], processor.Create(OpCodes.Call, getInstanceMethod)); - processor.InsertBefore(method.Body.Instructions[i + 1], processor.Create(OpCodes.Call, finishAwakeSpanMethod)); // +1 because return just moved one back - } + processor.InsertBefore(method.Body.Instructions[i], processor.Create(OpCodes.Call, getInstanceMethod)); + processor.InsertBefore(method.Body.Instructions[i + 1], processor.Create(OpCodes.Call, finishAwakeSpanMethod)); // +1 because return just moved one back } } } - - logger.LogInfo("Applying Auto Instrumentation."); - playerReaderWriter.Write(); } + + logger.LogInfo("Applying Auto Instrumentation."); + playerReaderWriter.Write(); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs index 6047b1775..b5fee75bf 100644 --- a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs +++ b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs @@ -4,167 +4,166 @@ using System.Linq; using Mono.Cecil; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal class SentryPlayerReaderWriter { - internal class SentryPlayerReaderWriter - { - private const string PlayerAssemblyName = "Assembly-CSharp.dll"; - private const string SentryUnityAssemblyName = "Sentry.Unity.dll"; + private const string PlayerAssemblyName = "Assembly-CSharp.dll"; + private const string SentryUnityAssemblyName = "Sentry.Unity.dll"; - private readonly string _workingDirectory; - private readonly string _playerAssemblyPath; - private readonly string _sentryUnityAssemblyPath; + private readonly string _workingDirectory; + private readonly string _playerAssemblyPath; + private readonly string _sentryUnityAssemblyPath; - private bool _playerModuleHasSymbols; - private ModuleDefinition _playerModule = null!; // Set when reading the assemblies - private ModuleDefinition _sentryUnityModule = null!; // Set when reading the assemblies + private bool _playerModuleHasSymbols; + private ModuleDefinition _playerModule = null!; // Set when reading the assemblies + private ModuleDefinition _sentryUnityModule = null!; // Set when reading the assemblies - private SentryPlayerReaderWriter( - string workingDirectory, - string playerAssemblyPath, - string sentryUnityAssemblyPath) + private SentryPlayerReaderWriter( + string workingDirectory, + string playerAssemblyPath, + string sentryUnityAssemblyPath) + { + _workingDirectory = workingDirectory; + _playerAssemblyPath = playerAssemblyPath; + _sentryUnityAssemblyPath = sentryUnityAssemblyPath; + } + + public static SentryPlayerReaderWriter ReadAssemblies(string workingDirectory) + { + if (!Directory.Exists(workingDirectory)) { - _workingDirectory = workingDirectory; - _playerAssemblyPath = playerAssemblyPath; - _sentryUnityAssemblyPath = sentryUnityAssemblyPath; + throw new DirectoryNotFoundException($"Failed to find the working directory at '{workingDirectory}'"); } - public static SentryPlayerReaderWriter ReadAssemblies(string workingDirectory) + var playerAssemblyPath = Path.Combine(workingDirectory, PlayerAssemblyName); + if (!File.Exists(playerAssemblyPath)) { - if (!Directory.Exists(workingDirectory)) - { - throw new DirectoryNotFoundException($"Failed to find the working directory at '{workingDirectory}'"); - } + throw new FileNotFoundException($"Failed to find '{PlayerAssemblyName}' at '{workingDirectory}'."); + } - var playerAssemblyPath = Path.Combine(workingDirectory, PlayerAssemblyName); - if (!File.Exists(playerAssemblyPath)) - { - throw new FileNotFoundException($"Failed to find '{PlayerAssemblyName}' at '{workingDirectory}'."); - } + var sentryUnityAssemblyPath = Path.Combine(workingDirectory, SentryUnityAssemblyName); + if (!File.Exists(playerAssemblyPath)) + { + throw new FileNotFoundException($"Failed to find '{SentryUnityAssemblyName}' at '{workingDirectory}'."); + } - var sentryUnityAssemblyPath = Path.Combine(workingDirectory, SentryUnityAssemblyName); - if (!File.Exists(playerAssemblyPath)) - { - throw new FileNotFoundException($"Failed to find '{SentryUnityAssemblyName}' at '{workingDirectory}'."); - } + var moduleReaderWriter = new SentryPlayerReaderWriter(workingDirectory, playerAssemblyPath, sentryUnityAssemblyPath); + moduleReaderWriter.ReadFromWorkingDirectory(); - var moduleReaderWriter = new SentryPlayerReaderWriter(workingDirectory, playerAssemblyPath, sentryUnityAssemblyPath); - moduleReaderWriter.ReadFromWorkingDirectory(); + return moduleReaderWriter; + } - return moduleReaderWriter; - } + public void Write() + { + var parameters = new WriterParameters { WriteSymbols = _playerModuleHasSymbols }; + _playerModule.Write(_playerAssemblyPath, parameters); + } + + public MethodReference ImportSentryMonoBehaviourMethod(string methodName, Type[]? methodParameters = null) + { + var typeDefinition = GetTypeDefinition(_sentryUnityModule, typeof(SentryMonoBehaviour)); + var methodDefinition = GetMethodDefinition(typeDefinition, methodName, methodParameters); - public void Write() + var reference = _playerModule.ImportReference(methodDefinition); + if (reference is null) { - var parameters = new WriterParameters { WriteSymbols = _playerModuleHasSymbols }; - _playerModule.Write(_playerAssemblyPath, parameters); + throw new ArgumentException($"Failed to import requested reference in '{PlayerAssemblyName}'", methodDefinition.FullName); } - public MethodReference ImportSentryMonoBehaviourMethod(string methodName, Type[]? methodParameters = null) + return reference; + } + + public TypeReference ImportType(Type type) + { + var reference = _playerModule.ImportReference(type); + if (reference is null) { - var typeDefinition = GetTypeDefinition(_sentryUnityModule, typeof(SentryMonoBehaviour)); - var methodDefinition = GetMethodDefinition(typeDefinition, methodName, methodParameters); + throw new ArgumentException($"Failed to import requested type in '{PlayerAssemblyName}'", type.FullName); + } - var reference = _playerModule.ImportReference(methodDefinition); - if (reference is null) - { - throw new ArgumentException($"Failed to import requested reference in '{PlayerAssemblyName}'", methodDefinition.FullName); - } + return reference; + } - return reference; - } + public IEnumerable GetTypes() + { + return _playerModule.GetTypes(); + } - public TypeReference ImportType(Type type) + private void ReadFromWorkingDirectory() + { + (_playerModule, _playerModuleHasSymbols) = Read(_playerAssemblyPath); + (_sentryUnityModule, _) = Read(_sentryUnityAssemblyPath); + } + + private (ModuleDefinition module, bool hasSymbols) Read(string file) + { + try { - var reference = _playerModule.ImportReference(type); - if (reference is null) + var assemblyResolver = new DefaultAssemblyResolver(); + assemblyResolver.AddSearchDirectory(_workingDirectory); + + var parameters = new ReaderParameters { - throw new ArgumentException($"Failed to import requested type in '{PlayerAssemblyName}'", type.FullName); - } + InMemory = true, + AssemblyResolver = assemblyResolver, + }; - return reference; - } + var module = ModuleDefinition.ReadModule(file, parameters); + var hasSymbols = TryReadSymbols(module); - public IEnumerable GetTypes() - { - return _playerModule.GetTypes(); + return (module, hasSymbols); } - - private void ReadFromWorkingDirectory() + catch (Exception exception) { - (_playerModule, _playerModuleHasSymbols) = Read(_playerAssemblyPath); - (_sentryUnityModule, _) = Read(_sentryUnityAssemblyPath); + throw new ArgumentException($"Failed to read: '{file}'", exception); } + } - private (ModuleDefinition module, bool hasSymbols) Read(string file) + private static bool TryReadSymbols(ModuleDefinition module) + { + try { - try - { - var assemblyResolver = new DefaultAssemblyResolver(); - assemblyResolver.AddSearchDirectory(_workingDirectory); - - var parameters = new ReaderParameters - { - InMemory = true, - AssemblyResolver = assemblyResolver, - }; - - var module = ModuleDefinition.ReadModule(file, parameters); - var hasSymbols = TryReadSymbols(module); - - return (module, hasSymbols); - } - catch (Exception exception) - { - throw new ArgumentException($"Failed to read: '{file}'", exception); - } + module.ReadSymbols(); + return true; } - - private static bool TryReadSymbols(ModuleDefinition module) + catch { - try - { - module.ReadSymbols(); - return true; - } - catch - { - return false; - } + return false; } + } - private static TypeDefinition GetTypeDefinition(ModuleDefinition module, Type type) + private static TypeDefinition GetTypeDefinition(ModuleDefinition module, Type type) + { + var typeDefinition = module.GetType(type.FullName); + if (typeDefinition is null) { - var typeDefinition = module.GetType(type.FullName); - if (typeDefinition is null) - { - throw new ArgumentException($"Failed to get requested type definition in {module.Name}", type.FullName); - } - - return typeDefinition; + throw new ArgumentException($"Failed to get requested type definition in {module.Name}", type.FullName); } - private static MethodDefinition GetMethodDefinition(TypeDefinition typeDefinition, string name, Type[]? requiredParameters = null) - { - requiredParameters ??= Array.Empty(); + return typeDefinition; + } - var matchingMethod = typeDefinition.Methods - .FirstOrDefault(method => - method.Name == name && - requiredParameters.Length == method.Parameters.Count && - method.Parameters.Select(p => p.ParameterType.FullName) - .SequenceEqual(requiredParameters.Select(x => x.FullName)) - ); + private static MethodDefinition GetMethodDefinition(TypeDefinition typeDefinition, string name, Type[]? requiredParameters = null) + { + requiredParameters ??= Array.Empty(); - if (matchingMethod == null) - { - throw new Exception( - $"Failed to find method '{name}' " + - $"in '{typeDefinition.FullName}' " + - $"with parameters: '{string.Join(",", requiredParameters)}'"); - } + var matchingMethod = typeDefinition.Methods + .FirstOrDefault(method => + method.Name == name && + requiredParameters.Length == method.Parameters.Count && + method.Parameters.Select(p => p.ParameterType.FullName) + .SequenceEqual(requiredParameters.Select(x => x.FullName)) + ); - return matchingMethod; + if (matchingMethod == null) + { + throw new Exception( + $"Failed to find method '{name}' " + + $"in '{typeDefinition.FullName}' " + + $"with parameters: '{string.Join(",", requiredParameters)}'"); } + + return matchingMethod; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs index 3827c4475..ff7398ff6 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs @@ -2,207 +2,206 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal static class AdvancedTab { - internal static class AdvancedTab + private static bool UnfoldFailedStatusCodeRanges; + + internal static void Display(ScriptableSentryUnityOptions options, SentryCliOptions? cliOptions) { - private static bool UnfoldFailedStatusCodeRanges; + { + options.AutoSessionTracking = EditorGUILayout.BeginToggleGroup( + new GUIContent("Auto Session Tracking", "Whether the SDK should start and end sessions " + + "automatically. If the timeout is reached the old session will" + + "be ended and a new one started."), + options.AutoSessionTracking); + + options.AutoSessionTrackingInterval = EditorGUILayout.IntField( + new GUIContent("Session Timeout [ms]", "The duration of time a session can stay paused " + + "(i.e. the application has been put in the background) before " + + "it is considered ended."), + options.AutoSessionTrackingInterval); + options.AutoSessionTrackingInterval = Mathf.Max(0, options.AutoSessionTrackingInterval); + EditorGUILayout.EndToggleGroup(); + } + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - internal static void Display(ScriptableSentryUnityOptions options, SentryCliOptions? cliOptions) { - { - options.AutoSessionTracking = EditorGUILayout.BeginToggleGroup( - new GUIContent("Auto Session Tracking", "Whether the SDK should start and end sessions " + - "automatically. If the timeout is reached the old session will" + - "be ended and a new one started."), - options.AutoSessionTracking); - - options.AutoSessionTrackingInterval = EditorGUILayout.IntField( - new GUIContent("Session Timeout [ms]", "The duration of time a session can stay paused " + - "(i.e. the application has been put in the background) before " + - "it is considered ended."), - options.AutoSessionTrackingInterval); - options.AutoSessionTrackingInterval = Mathf.Max(0, options.AutoSessionTrackingInterval); - EditorGUILayout.EndToggleGroup(); - } + options.AnrDetectionEnabled = EditorGUILayout.BeginToggleGroup( + new GUIContent("ANR Detection", "Whether the SDK should report 'Application Not " + + "Responding' events."), + options.AnrDetectionEnabled); + + options.AnrTimeout = EditorGUILayout.IntField( + new GUIContent("ANR Timeout [ms]", "The duration in [ms] for how long the game has to be unresponsive " + + "before an ANR event is reported.\nDefault: 5000ms"), + options.AnrTimeout); + options.AnrTimeout = Math.Max(0, options.AnrTimeout); + + EditorGUILayout.EndToggleGroup(); + } - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + { + options.CaptureFailedRequests = EditorGUILayout.BeginToggleGroup( + new GUIContent("Capture Failed HTTP Requests", + "Whether the SDK should capture failed HTTP requests. This works out of the box for iOS only" + + "For the C# layer you need to add the 'SentryHttpMessageHandler' to your HTTP Client."), + options.CaptureFailedRequests); + + UnfoldFailedStatusCodeRanges = EditorGUILayout.BeginFoldoutHeaderGroup(UnfoldFailedStatusCodeRanges, "Failed Status Codes Ranges"); + if (UnfoldFailedStatusCodeRanges) { - options.AnrDetectionEnabled = EditorGUILayout.BeginToggleGroup( - new GUIContent("ANR Detection", "Whether the SDK should report 'Application Not " + - "Responding' events."), - options.AnrDetectionEnabled); - - options.AnrTimeout = EditorGUILayout.IntField( - new GUIContent("ANR Timeout [ms]", "The duration in [ms] for how long the game has to be unresponsive " + - "before an ANR event is reported.\nDefault: 5000ms"), - options.AnrTimeout); - options.AnrTimeout = Math.Max(0, options.AnrTimeout); - - EditorGUILayout.EndToggleGroup(); - } + var rangeCount = options.FailedRequestStatusCodes.Count / 2; + rangeCount = EditorGUILayout.IntField( + new GUIContent("Status Codes Range Count", "The amount of ranges of HTTP status codes to capture."), + rangeCount); - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + // Because it's a range, we need to double the count + rangeCount *= 2; - { - options.CaptureFailedRequests = EditorGUILayout.BeginToggleGroup( - new GUIContent("Capture Failed HTTP Requests", - "Whether the SDK should capture failed HTTP requests. This works out of the box for iOS only" + - "For the C# layer you need to add the 'SentryHttpMessageHandler' to your HTTP Client."), - options.CaptureFailedRequests); - - UnfoldFailedStatusCodeRanges = EditorGUILayout.BeginFoldoutHeaderGroup(UnfoldFailedStatusCodeRanges, "Failed Status Codes Ranges"); - if (UnfoldFailedStatusCodeRanges) + if (rangeCount <= 0) { - var rangeCount = options.FailedRequestStatusCodes.Count / 2; - rangeCount = EditorGUILayout.IntField( - new GUIContent("Status Codes Range Count", "The amount of ranges of HTTP status codes to capture."), - rangeCount); + options.FailedRequestStatusCodes.Clear(); + } - // Because it's a range, we need to double the count - rangeCount *= 2; + if (rangeCount < options.FailedRequestStatusCodes.Count) + { + options.FailedRequestStatusCodes.RemoveRange(rangeCount, options.FailedRequestStatusCodes.Count - rangeCount); + } - if (rangeCount <= 0) + if (rangeCount > options.FailedRequestStatusCodes.Count) + { + var rangedToAdd = rangeCount - options.FailedRequestStatusCodes.Count; + for (var i = 0; i < rangedToAdd; i += 2) { - options.FailedRequestStatusCodes.Clear(); + options.FailedRequestStatusCodes.Add(500); + options.FailedRequestStatusCodes.Add(599); } + } - if (rangeCount < options.FailedRequestStatusCodes.Count) - { - options.FailedRequestStatusCodes.RemoveRange(rangeCount, options.FailedRequestStatusCodes.Count - rangeCount); - } + for (var i = 0; i < options.FailedRequestStatusCodes.Count; i += 2) + { + GUILayout.BeginHorizontal(); - if (rangeCount > options.FailedRequestStatusCodes.Count) - { - var rangedToAdd = rangeCount - options.FailedRequestStatusCodes.Count; - for (var i = 0; i < rangedToAdd; i += 2) - { - options.FailedRequestStatusCodes.Add(500); - options.FailedRequestStatusCodes.Add(599); - } - } + options.FailedRequestStatusCodes[i] = EditorGUILayout.IntField("Start", options.FailedRequestStatusCodes[i]); + options.FailedRequestStatusCodes[i + 1] = EditorGUILayout.IntField("End", options.FailedRequestStatusCodes[i + 1]); - for (var i = 0; i < options.FailedRequestStatusCodes.Count; i += 2) - { - GUILayout.BeginHorizontal(); + GUILayout.EndHorizontal(); + } + } - options.FailedRequestStatusCodes[i] = EditorGUILayout.IntField("Start", options.FailedRequestStatusCodes[i]); - options.FailedRequestStatusCodes[i + 1] = EditorGUILayout.IntField("End", options.FailedRequestStatusCodes[i + 1]); + EditorGUILayout.EndFoldoutHeaderGroup(); + EditorGUILayout.EndToggleGroup(); + } - GUILayout.EndHorizontal(); - } - } + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - EditorGUILayout.EndFoldoutHeaderGroup(); - EditorGUILayout.EndToggleGroup(); - } + { + GUILayout.Label("Automatic Exception Filter", EditorStyles.boldLabel); + + options.FilterBadGatewayExceptions = EditorGUILayout.Toggle( + new GUIContent("BadGatewayException", "Whether the SDK automatically filters Bad Gateway " + + "exceptions before they are being sent to Sentry."), + options.FilterBadGatewayExceptions); + + options.FilterWebExceptions = EditorGUILayout.Toggle( + new GUIContent("WebException", "Whether the SDK automatically filters " + + "System.Net.WebException before they are being sent to Sentry."), + options.FilterWebExceptions); + + options.FilterSocketExceptions = EditorGUILayout.Toggle( + new GUIContent("SocketException", "Whether the SDK automatically filters " + + "System.Net.Sockets.SocketException with error code '10049' from " + + "being sent to Sentry."), + options.FilterSocketExceptions); + } + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + GUILayout.Label("Native Support", EditorStyles.boldLabel); + { + options.IosNativeSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("iOS Native Support", "Whether to enable Native iOS support to capture" + + "errors written in languages such as Objective-C, Swift, C and C++."), + options.IosNativeSupportEnabled); + + options.AndroidNativeSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("Android Native Support", "Whether to enable Native Android support to " + + "capture errors written in languages such as Java, Kotlin, C and C++."), + options.AndroidNativeSupportEnabled); + + if (options.AndroidNativeSupportEnabled + && PlayerSettings.GetScriptingBackend(BuildTargetGroup.Android) != ScriptingImplementation.IL2CPP) { - GUILayout.Label("Automatic Exception Filter", EditorStyles.boldLabel); - - options.FilterBadGatewayExceptions = EditorGUILayout.Toggle( - new GUIContent("BadGatewayException", "Whether the SDK automatically filters Bad Gateway " + - "exceptions before they are being sent to Sentry."), - options.FilterBadGatewayExceptions); - - options.FilterWebExceptions = EditorGUILayout.Toggle( - new GUIContent("WebException", "Whether the SDK automatically filters " + - "System.Net.WebException before they are being sent to Sentry."), - options.FilterWebExceptions); - - options.FilterSocketExceptions = EditorGUILayout.Toggle( - new GUIContent("SocketException", "Whether the SDK automatically filters " + - "System.Net.Sockets.SocketException with error code '10049' from " + - "being sent to Sentry."), - options.FilterSocketExceptions); + EditorGUILayout.HelpBox("Android native support requires IL2CPP scripting backend and is currently unsupported on Mono.", MessageType.Warning); } - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + EditorGUI.indentLevel++; + EditorGUI.BeginDisabledGroup(!options.AndroidNativeSupportEnabled); + options.NdkIntegrationEnabled = EditorGUILayout.Toggle( + new GUIContent("NDK Integration", "Whether to enable NDK Integration to capture" + + "errors written in languages such C and C++."), + options.NdkIntegrationEnabled); + EditorGUI.BeginDisabledGroup(!options.NdkIntegrationEnabled); + options.NdkScopeSyncEnabled = EditorGUILayout.Toggle( + new GUIContent("NDK Scope Sync", "Whether the SDK should sync the scope to the NDK layer."), + options.NdkScopeSyncEnabled); + EditorGUI.EndDisabledGroup(); + EditorGUI.EndDisabledGroup(); + options.PostGenerateGradleProjectCallbackOrder = EditorGUILayout.IntField( + new GUIContent("Android Callback Order", "Override the default callback order of " + + "Sentry Gradle modification script that adds Sentry dependencies " + + "to the gradle project files."), + options.PostGenerateGradleProjectCallbackOrder); + EditorGUI.indentLevel--; + + options.WindowsNativeSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("Windows Native Support", "Whether to enable native crashes support on Windows."), + options.WindowsNativeSupportEnabled); + + options.MacosNativeSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("macOS Native Support", "Whether to enable native crashes support on macOS."), + options.MacosNativeSupportEnabled); + + options.LinuxNativeSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("Linux Native Support", "Whether to enable native crashes support on Linux."), + options.LinuxNativeSupportEnabled); + } + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - GUILayout.Label("Native Support", EditorStyles.boldLabel); + { + options.Il2CppLineNumberSupportEnabled = EditorGUILayout.Toggle( + new GUIContent("IL2CPP line numbers", "Whether the SDK should try to to provide line " + + "numbers for exceptions in IL2CPP builds."), + options.Il2CppLineNumberSupportEnabled); + if (options.Il2CppLineNumberSupportEnabled) { - options.IosNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("iOS Native Support", "Whether to enable Native iOS support to capture" + - "errors written in languages such as Objective-C, Swift, C and C++."), - options.IosNativeSupportEnabled); - - options.AndroidNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("Android Native Support", "Whether to enable Native Android support to " + - "capture errors written in languages such as Java, Kotlin, C and C++."), - options.AndroidNativeSupportEnabled); - - if (options.AndroidNativeSupportEnabled - && PlayerSettings.GetScriptingBackend(BuildTargetGroup.Android) != ScriptingImplementation.IL2CPP) + if (!SentryUnityVersion.IsNewerOrEqualThan("2020.3")) { - EditorGUILayout.HelpBox("Android native support requires IL2CPP scripting backend and is currently unsupported on Mono.", MessageType.Warning); + EditorGUILayout.HelpBox("The IL2CPP line number feature is supported from Unity version 2020.3 or newer and 2021.3 or newer onwards", MessageType.Warning); } - - EditorGUI.indentLevel++; - EditorGUI.BeginDisabledGroup(!options.AndroidNativeSupportEnabled); - options.NdkIntegrationEnabled = EditorGUILayout.Toggle( - new GUIContent("NDK Integration", "Whether to enable NDK Integration to capture" + - "errors written in languages such C and C++."), - options.NdkIntegrationEnabled); - EditorGUI.BeginDisabledGroup(!options.NdkIntegrationEnabled); - options.NdkScopeSyncEnabled = EditorGUILayout.Toggle( - new GUIContent("NDK Scope Sync", "Whether the SDK should sync the scope to the NDK layer."), - options.NdkScopeSyncEnabled); - EditorGUI.EndDisabledGroup(); - EditorGUI.EndDisabledGroup(); - options.PostGenerateGradleProjectCallbackOrder = EditorGUILayout.IntField( - new GUIContent("Android Callback Order", "Override the default callback order of " + - "Sentry Gradle modification script that adds Sentry dependencies " + - "to the gradle project files."), - options.PostGenerateGradleProjectCallbackOrder); - EditorGUI.indentLevel--; - - options.WindowsNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("Windows Native Support", "Whether to enable native crashes support on Windows."), - options.WindowsNativeSupportEnabled); - - options.MacosNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("macOS Native Support", "Whether to enable native crashes support on macOS."), - options.MacosNativeSupportEnabled); - - options.LinuxNativeSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("Linux Native Support", "Whether to enable native crashes support on Linux."), - options.LinuxNativeSupportEnabled); - } - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - { - options.Il2CppLineNumberSupportEnabled = EditorGUILayout.Toggle( - new GUIContent("IL2CPP line numbers", "Whether the SDK should try to to provide line " + - "numbers for exceptions in IL2CPP builds."), - options.Il2CppLineNumberSupportEnabled); - - if (options.Il2CppLineNumberSupportEnabled) + else if (cliOptions is not null && !cliOptions.IsValid(null, EditorUserBuildSettings.development)) { - if (!SentryUnityVersion.IsNewerOrEqualThan("2020.3")) - { - EditorGUILayout.HelpBox("The IL2CPP line number feature is supported from Unity version 2020.3 or newer and 2021.3 or newer onwards", MessageType.Warning); - } - else if (cliOptions is not null && !cliOptions.IsValid(null, EditorUserBuildSettings.development)) - { - EditorGUILayout.HelpBox("The IL2CPP line number support relies on the Debug Symbol Upload to be properly set up.", MessageType.Error); - } + EditorGUILayout.HelpBox("The IL2CPP line number support relies on the Debug Symbol Upload to be properly set up.", MessageType.Error); } } } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs index b54f476be..7cc570b57 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs @@ -2,125 +2,124 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal static class CoreTab { - internal static class CoreTab + internal static void Display(ScriptableSentryUnityOptions options) { - internal static void Display(ScriptableSentryUnityOptions options) { - { - GUILayout.Label("Base Options", EditorStyles.boldLabel); - - options.Dsn = EditorGUILayout.TextField( - new GUIContent("DSN", "The URL to your Sentry project. " + - "Get yours on sentry.io -> Project Settings."), - options.Dsn)?.Trim(); + GUILayout.Label("Base Options", EditorStyles.boldLabel); - if (string.IsNullOrWhiteSpace(options.Dsn)) - { - EditorGUILayout.HelpBox("The SDK requires a DSN.", MessageType.Error); - } + options.Dsn = EditorGUILayout.TextField( + new GUIContent("DSN", "The URL to your Sentry project. " + + "Get yours on sentry.io -> Project Settings."), + options.Dsn)?.Trim(); - options.CaptureInEditor = EditorGUILayout.Toggle( - new GUIContent("Capture In Editor", "Capture errors while running in the Editor."), - options.CaptureInEditor); + if (string.IsNullOrWhiteSpace(options.Dsn)) + { + EditorGUILayout.HelpBox("The SDK requires a DSN.", MessageType.Error); } - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + options.CaptureInEditor = EditorGUILayout.Toggle( + new GUIContent("Capture In Editor", "Capture errors while running in the Editor."), + options.CaptureInEditor); + } - { - options.Debug = EditorGUILayout.BeginToggleGroup( - new GUIContent("Enable Debug Output", "Whether the Sentry SDK should print its " + - "diagnostic logs to the console."), - options.Debug); - - options.DebugOnlyInEditor = EditorGUILayout.Toggle( - new GUIContent("Only In Editor", "Only print logs when in the editor. Development " + - "builds of the player will not include Sentry's SDK diagnostics."), - options.DebugOnlyInEditor); - - options.DiagnosticLevel = (SentryLevel)EditorGUILayout.EnumPopup( - new GUIContent("Verbosity Level", "The minimum level allowed to be printed to the console. " + - "Log messages with a level below this level are dropped."), - options.DiagnosticLevel); - - EditorGUILayout.EndToggleGroup(); - } + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + { + options.Debug = EditorGUILayout.BeginToggleGroup( + new GUIContent("Enable Debug Output", "Whether the Sentry SDK should print its " + + "diagnostic logs to the console."), + options.Debug); + + options.DebugOnlyInEditor = EditorGUILayout.Toggle( + new GUIContent("Only In Editor", "Only print logs when in the editor. Development " + + "builds of the player will not include Sentry's SDK diagnostics."), + options.DebugOnlyInEditor); + + options.DiagnosticLevel = (SentryLevel)EditorGUILayout.EnumPopup( + new GUIContent("Verbosity Level", "The minimum level allowed to be printed to the console. " + + "Log messages with a level below this level are dropped."), + options.DiagnosticLevel); + + EditorGUILayout.EndToggleGroup(); + } - { - options.EnableLogDebouncing = EditorGUILayout.BeginToggleGroup( - new GUIContent("Enable Log Debouncing", "The SDK debounces log messages of the " + - "same type if they are more frequent than once per second."), - options.EnableLogDebouncing); - - options.DebounceTimeLog = EditorGUILayout.IntField( - new GUIContent("Log Debounce [ms]", "The time that has to pass between events of " + - "LogType.Log before the SDK sends it again."), - options.DebounceTimeLog); - options.DebounceTimeLog = Math.Max(0, options.DebounceTimeLog); - - options.DebounceTimeWarning = EditorGUILayout.IntField( - new GUIContent("Warning Debounce [ms]", "The time that has to pass between events of " + - "LogType.Warning before the SDK sends it again."), - options.DebounceTimeWarning); - options.DebounceTimeWarning = Math.Max(0, options.DebounceTimeWarning); - - options.DebounceTimeError = EditorGUILayout.IntField( - new GUIContent("Error Debounce [ms]", "The time that has to pass between events of " + - "LogType.Assert, LogType.Exception and LogType.Error before " + - "the SDK sends it again."), - options.DebounceTimeError); - options.DebounceTimeError = Math.Max(0, options.DebounceTimeError); - - EditorGUILayout.EndToggleGroup(); - } + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + { + options.EnableLogDebouncing = EditorGUILayout.BeginToggleGroup( + new GUIContent("Enable Log Debouncing", "The SDK debounces log messages of the " + + "same type if they are more frequent than once per second."), + options.EnableLogDebouncing); + + options.DebounceTimeLog = EditorGUILayout.IntField( + new GUIContent("Log Debounce [ms]", "The time that has to pass between events of " + + "LogType.Log before the SDK sends it again."), + options.DebounceTimeLog); + options.DebounceTimeLog = Math.Max(0, options.DebounceTimeLog); + + options.DebounceTimeWarning = EditorGUILayout.IntField( + new GUIContent("Warning Debounce [ms]", "The time that has to pass between events of " + + "LogType.Warning before the SDK sends it again."), + options.DebounceTimeWarning); + options.DebounceTimeWarning = Math.Max(0, options.DebounceTimeWarning); + + options.DebounceTimeError = EditorGUILayout.IntField( + new GUIContent("Error Debounce [ms]", "The time that has to pass between events of " + + "LogType.Assert, LogType.Exception and LogType.Error before " + + "the SDK sends it again."), + options.DebounceTimeError); + options.DebounceTimeError = Math.Max(0, options.DebounceTimeError); + + EditorGUILayout.EndToggleGroup(); + } - { - GUILayout.Label("Tracing - Performance Monitoring", EditorStyles.boldLabel); + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + { + GUILayout.Label("Tracing - Performance Monitoring", EditorStyles.boldLabel); - options.TracesSampleRate = EditorGUILayout.Slider( - new GUIContent("Traces Sample Rate", "Indicates the percentage of transactions that are " + - "captured. Setting this to 0 discards all trace data. " + - "Setting this to 1.0 captures all."), - (float)options.TracesSampleRate, 0.0f, 1.0f); + options.TracesSampleRate = EditorGUILayout.Slider( + new GUIContent("Traces Sample Rate", "Indicates the percentage of transactions that are " + + "captured. Setting this to 0 discards all trace data. " + + "Setting this to 1.0 captures all."), + (float)options.TracesSampleRate, 0.0f, 1.0f); - EditorGUI.BeginDisabledGroup(options.TracesSampleRate <= 0); + EditorGUI.BeginDisabledGroup(options.TracesSampleRate <= 0); - options.AutoStartupTraces = EditorGUILayout.Toggle( - new GUIContent("Auto Startup Traces ", "Whether the SDK should automatically create " + - "traces during startup. This integration is currently " + - "unavailable on WebGL."), - options.AutoStartupTraces); + options.AutoStartupTraces = EditorGUILayout.Toggle( + new GUIContent("Auto Startup Traces ", "Whether the SDK should automatically create " + + "traces during startup. This integration is currently " + + "unavailable on WebGL."), + options.AutoStartupTraces); - options.AutoSceneLoadTraces = EditorGUILayout.Toggle( - new GUIContent("Auto Scene Traces ", "Whether the SDK should automatically create traces " + - "during scene loading. Requires Unity 2020.3 or newer."), - options.AutoSceneLoadTraces); + options.AutoSceneLoadTraces = EditorGUILayout.Toggle( + new GUIContent("Auto Scene Traces ", "Whether the SDK should automatically create traces " + + "during scene loading. Requires Unity 2020.3 or newer."), + options.AutoSceneLoadTraces); - EditorGUILayout.Space(); + EditorGUILayout.Space(); - GUILayout.Label("Auto Instrumentation - Experimental", EditorStyles.boldLabel); + GUILayout.Label("Auto Instrumentation - Experimental", EditorStyles.boldLabel); - EditorGUILayout.HelpBox("The SDK will modify the compiled assembly during a post build step " + - "to create transaction and spans automatically.", MessageType.Info); + EditorGUILayout.HelpBox("The SDK will modify the compiled assembly during a post build step " + + "to create transaction and spans automatically.", MessageType.Info); - options.AutoAwakeTraces = EditorGUILayout.Toggle( - new GUIContent("Awake Calls", "Whether the SDK automatically captures all instances " + - "of Awake as Spans."), - options.AutoAwakeTraces); + options.AutoAwakeTraces = EditorGUILayout.Toggle( + new GUIContent("Awake Calls", "Whether the SDK automatically captures all instances " + + "of Awake as Spans."), + options.AutoAwakeTraces); - EditorGUI.EndDisabledGroup(); - } + EditorGUI.EndDisabledGroup(); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs index b09351387..5946d42f6 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs @@ -1,52 +1,51 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal static class DebugSymbolsTab { - internal static class DebugSymbolsTab + internal static void Display(SentryCliOptions cliOptions) { - internal static void Display(SentryCliOptions cliOptions) - { - cliOptions.UploadSymbols = EditorGUILayout.BeginToggleGroup( - new GUIContent("Upload Symbols", "Whether debug symbols should be uploaded automatically " + - "on release builds."), - cliOptions.UploadSymbols); - - cliOptions.UploadDevelopmentSymbols = EditorGUILayout.Toggle( - new GUIContent("Upload Dev Symbols", "Whether debug symbols should be uploaded automatically " + - "on development builds."), - cliOptions.UploadDevelopmentSymbols); - - cliOptions.UploadSources = EditorGUILayout.Toggle( - new GUIContent("Upload Sources", "Whether your source code should be uploaded to Sentry, so the stack trace in Sentry has the relevant code next to it."), - cliOptions.UploadSources); - - cliOptions.IgnoreCliErrors = EditorGUILayout.Toggle( - new GUIContent("Ignore CLI Errors", "Whether to ignore the Sentry CLI errors during the build. If this is ON, the errors will be logged, but the build will succeed."), - cliOptions.IgnoreCliErrors); - - EditorGUILayout.EndToggleGroup(); - - cliOptions.Auth = EditorGUILayout.TextField( - new GUIContent( - "Auth Token", - cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Auth) ? SentryWindow.ErrorIcon : null, - "The authorization token from your user settings in Sentry"), - cliOptions.Auth); - - cliOptions.Organization = EditorGUILayout.TextField( - new GUIContent( - "Org Slug", - cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Organization) ? SentryWindow.ErrorIcon : null, - "The organization slug in Sentry"), - cliOptions.Organization); - - cliOptions.Project = EditorGUILayout.TextField( - new GUIContent( - "Project Name", - cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Project) ? SentryWindow.ErrorIcon : null, - "The project name in Sentry"), - cliOptions.Project); - } + cliOptions.UploadSymbols = EditorGUILayout.BeginToggleGroup( + new GUIContent("Upload Symbols", "Whether debug symbols should be uploaded automatically " + + "on release builds."), + cliOptions.UploadSymbols); + + cliOptions.UploadDevelopmentSymbols = EditorGUILayout.Toggle( + new GUIContent("Upload Dev Symbols", "Whether debug symbols should be uploaded automatically " + + "on development builds."), + cliOptions.UploadDevelopmentSymbols); + + cliOptions.UploadSources = EditorGUILayout.Toggle( + new GUIContent("Upload Sources", "Whether your source code should be uploaded to Sentry, so the stack trace in Sentry has the relevant code next to it."), + cliOptions.UploadSources); + + cliOptions.IgnoreCliErrors = EditorGUILayout.Toggle( + new GUIContent("Ignore CLI Errors", "Whether to ignore the Sentry CLI errors during the build. If this is ON, the errors will be logged, but the build will succeed."), + cliOptions.IgnoreCliErrors); + + EditorGUILayout.EndToggleGroup(); + + cliOptions.Auth = EditorGUILayout.TextField( + new GUIContent( + "Auth Token", + cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Auth) ? SentryWindow.ErrorIcon : null, + "The authorization token from your user settings in Sentry"), + cliOptions.Auth); + + cliOptions.Organization = EditorGUILayout.TextField( + new GUIContent( + "Org Slug", + cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Organization) ? SentryWindow.ErrorIcon : null, + "The organization slug in Sentry"), + cliOptions.Organization); + + cliOptions.Project = EditorGUILayout.TextField( + new GUIContent( + "Project Name", + cliOptions.UploadSymbols && string.IsNullOrWhiteSpace(cliOptions.Project) ? SentryWindow.ErrorIcon : null, + "The project name in Sentry"), + cliOptions.Project); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs index cb46b165a..1ac11b504 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs @@ -1,167 +1,165 @@ using System; using UnityEditor; using UnityEngine; -using UnityEngine.Assertions; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal static class EnrichmentTab { - internal static class EnrichmentTab + internal static void Display(ScriptableSentryUnityOptions options) { - internal static void Display(ScriptableSentryUnityOptions options) - { - GUILayout.Label("Tag Overrides", EditorStyles.boldLabel); + GUILayout.Label("Tag Overrides", EditorStyles.boldLabel); - options.ReleaseOverride = EditorGUILayout.TextField( - new GUIContent("Override Release", "By default release is built from the Application info as: " + - "\"{productName}@{version}+{buildGUID}\". " + + options.ReleaseOverride = EditorGUILayout.TextField( + new GUIContent("Override Release", "By default release is built from the Application info as: " + + "\"{productName}@{version}+{buildGUID}\". " + + "\nThis option is an override."), + options.ReleaseOverride); + + options.EnvironmentOverride = EditorGUILayout.TextField( + new GUIContent("Override Environment", "Auto detects 'production' or 'editor' by " + + "default based on 'Application.isEditor." + "\nThis option is an override."), - options.ReleaseOverride); + options.EnvironmentOverride); - options.EnvironmentOverride = EditorGUILayout.TextField( - new GUIContent("Override Environment", "Auto detects 'production' or 'editor' by " + - "default based on 'Application.isEditor." + - "\nThis option is an override."), - options.EnvironmentOverride); + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + GUILayout.Label("Stacktrace", EditorStyles.boldLabel); - GUILayout.Label("Stacktrace", EditorStyles.boldLabel); + options.AttachStacktrace = EditorGUILayout.Toggle( + new GUIContent("Stacktrace For Logs", "Whether to include a stack trace for non " + + "error events like logs. Even when Unity didn't include and no " + + "exception was thrown. Refer to AttachStacktrace on sentry docs."), + options.AttachStacktrace); - options.AttachStacktrace = EditorGUILayout.Toggle( - new GUIContent("Stacktrace For Logs", "Whether to include a stack trace for non " + - "error events like logs. Even when Unity didn't include and no " + - "exception was thrown. Refer to AttachStacktrace on sentry docs."), - options.AttachStacktrace); + // Enhanced not supported on IL2CPP so not displaying this for the time being: + // Options.StackTraceMode = (StackTraceMode) EditorGUILayout.EnumPopup( + // new GUIContent("Stacktrace Mode", "Enhanced is the default." + + // "\n - Enhanced: Include async, return type, args,..." + + // "\n - Original - Default .NET stack trace format."), + // Options.StackTraceMode); - // Enhanced not supported on IL2CPP so not displaying this for the time being: - // Options.StackTraceMode = (StackTraceMode) EditorGUILayout.EnumPopup( - // new GUIContent("Stacktrace Mode", "Enhanced is the default." + - // "\n - Enhanced: Include async, return type, args,..." + - // "\n - Original - Default .NET stack trace format."), - // Options.StackTraceMode); + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + { + options.SendDefaultPii = EditorGUILayout.BeginToggleGroup( + new GUIContent("Send default PII", "Whether to include default Personal Identifiable " + + "Information."), + options.SendDefaultPii); + + options.IsEnvironmentUser = EditorGUILayout.Toggle( + new GUIContent("Auto Set UserName", "Whether to report the 'Environment.UserName' as " + + "the User affected in the event. Should be disabled for " + + "Android and iOS."), + options.IsEnvironmentUser); + EditorGUILayout.EndToggleGroup(); + } + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + GUILayout.Label("Breadcrumbs automatically added for LogType:", EditorStyles.boldLabel); + + options.BreadcrumbsForLogs = EditorGUILayout.Toggle( + new GUIContent("Log", "Whether the SDK automatically adds breadcrumbs 'Debug.Log'."), + options.BreadcrumbsForLogs); + options.BreadcrumbsForWarnings = EditorGUILayout.Toggle( + new GUIContent("Warning", "Whether the SDK automatically adds breadcrumbs for 'Debug.LogWarning'."), + options.BreadcrumbsForWarnings); + options.BreadcrumbsForAsserts = EditorGUILayout.Toggle( + new GUIContent("Assert", "Whether the SDK automatically adds breadcrumbs for 'Debug.Assert'."), + options.BreadcrumbsForAsserts); + options.BreadcrumbsForErrors = EditorGUILayout.Toggle( + new GUIContent("Error", "Whether the SDK automatically adds breadcrumbs for 'Debug.LogError'."), + options.BreadcrumbsForErrors); + options.BreadcrumbsForExceptions = EditorGUILayout.Toggle( + new GUIContent("Exception", "Whether the SDK automatically adds breadcrumbs for exceptions and 'Debug.LogException'."), + options.BreadcrumbsForExceptions); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + options.MaxBreadcrumbs = EditorGUILayout.IntField( + new GUIContent("Max Breadcrumbs", "Maximum number of breadcrumbs that get captured." + + "\nDefault: 100"), + options.MaxBreadcrumbs); + options.MaxBreadcrumbs = Math.Max(0, options.MaxBreadcrumbs); + + options.ReportAssembliesMode = (ReportAssembliesMode)EditorGUILayout.EnumPopup( + new GUIContent("Report Assemblies Mode", "Whether or not to include referenced assemblies " + + "Version or InformationalVersion in each event sent to sentry."), + options.ReportAssembliesMode); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + options.AttachScreenshot = EditorGUILayout.BeginToggleGroup( + new GUIContent("Attach Screenshot", "Try to attach current screenshot on events.\n" + + "This is an early-access feature and may not work on all platforms (it is explicitly disabled on WebGL).\n" + + "Additionally, the screenshot is captured mid-frame, when an event happens, so it may be incomplete.\n" + + "A screenshot might not be able to be attached, for example when the error happens on a background thread."), + options.AttachScreenshot); + + options.ScreenshotQuality = (ScreenshotQuality)EditorGUILayout.EnumPopup( + new GUIContent("Quality", "The resolution quality of the screenshot.\n" + + "'Full': Fully of the current resolution\n" + + "'High': 1080p\n" + + "'Medium': 720p\n" + + "'Low': 480p"), + options.ScreenshotQuality); + + options.ScreenshotCompression = EditorGUILayout.IntSlider( + new GUIContent("Compression", "The compression of the screenshot."), + options.ScreenshotCompression, 1, 100); + + EditorGUILayout.EndToggleGroup(); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + { + options.AttachViewHierarchy = EditorGUILayout.BeginToggleGroup( + new GUIContent("Attach Hierarchy", "Try to attach the current scene's hierarchy."), + options.AttachViewHierarchy); + + options.MaxViewHierarchyRootObjects = EditorGUILayout.IntField( + new GUIContent("Max Root GameObjects", "Maximum number of captured GameObjects in " + + "a scene root." + + "\nDefault: 100"), + options.MaxViewHierarchyRootObjects); + if (options.MaxViewHierarchyRootObjects <= 0) { - options.SendDefaultPii = EditorGUILayout.BeginToggleGroup( - new GUIContent("Send default PII", "Whether to include default Personal Identifiable " + - "Information."), - options.SendDefaultPii); - - options.IsEnvironmentUser = EditorGUILayout.Toggle( - new GUIContent("Auto Set UserName", "Whether to report the 'Environment.UserName' as " + - "the User affected in the event. Should be disabled for " + - "Android and iOS."), - options.IsEnvironmentUser); - EditorGUILayout.EndToggleGroup(); + options.MaxViewHierarchyRootObjects = 0; } - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - GUILayout.Label("Breadcrumbs automatically added for LogType:", EditorStyles.boldLabel); - - options.BreadcrumbsForLogs = EditorGUILayout.Toggle( - new GUIContent("Log", "Whether the SDK automatically adds breadcrumbs 'Debug.Log'."), - options.BreadcrumbsForLogs); - options.BreadcrumbsForWarnings = EditorGUILayout.Toggle( - new GUIContent("Warning", "Whether the SDK automatically adds breadcrumbs for 'Debug.LogWarning'."), - options.BreadcrumbsForWarnings); - options.BreadcrumbsForAsserts = EditorGUILayout.Toggle( - new GUIContent("Assert", "Whether the SDK automatically adds breadcrumbs for 'Debug.Assert'."), - options.BreadcrumbsForAsserts); - options.BreadcrumbsForErrors = EditorGUILayout.Toggle( - new GUIContent("Error", "Whether the SDK automatically adds breadcrumbs for 'Debug.LogError'."), - options.BreadcrumbsForErrors); - options.BreadcrumbsForExceptions = EditorGUILayout.Toggle( - new GUIContent("Exception", "Whether the SDK automatically adds breadcrumbs for exceptions and 'Debug.LogException'."), - options.BreadcrumbsForExceptions); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - options.MaxBreadcrumbs = EditorGUILayout.IntField( - new GUIContent("Max Breadcrumbs", "Maximum number of breadcrumbs that get captured." + - "\nDefault: 100"), - options.MaxBreadcrumbs); - options.MaxBreadcrumbs = Math.Max(0, options.MaxBreadcrumbs); - - options.ReportAssembliesMode = (ReportAssembliesMode)EditorGUILayout.EnumPopup( - new GUIContent("Report Assemblies Mode", "Whether or not to include referenced assemblies " + - "Version or InformationalVersion in each event sent to sentry."), - options.ReportAssembliesMode); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - options.AttachScreenshot = EditorGUILayout.BeginToggleGroup( - new GUIContent("Attach Screenshot", "Try to attach current screenshot on events.\n" + - "This is an early-access feature and may not work on all platforms (it is explicitly disabled on WebGL).\n" + - "Additionally, the screenshot is captured mid-frame, when an event happens, so it may be incomplete.\n" + - "A screenshot might not be able to be attached, for example when the error happens on a background thread."), - options.AttachScreenshot); - - options.ScreenshotQuality = (ScreenshotQuality)EditorGUILayout.EnumPopup( - new GUIContent("Quality", "The resolution quality of the screenshot.\n" + - "'Full': Fully of the current resolution\n" + - "'High': 1080p\n" + - "'Medium': 720p\n" + - "'Low': 480p"), - options.ScreenshotQuality); - - options.ScreenshotCompression = EditorGUILayout.IntSlider( - new GUIContent("Compression", "The compression of the screenshot."), - options.ScreenshotCompression, 1, 100); - - EditorGUILayout.EndToggleGroup(); + options.MaxViewHierarchyObjectChildCount = EditorGUILayout.IntField( + new GUIContent("Max Child Count Per Object", "Maximum number of child objects " + + "captured for each GameObject." + + "\nDefault: 20"), + options.MaxViewHierarchyObjectChildCount); + if (options.MaxViewHierarchyObjectChildCount <= 0) + { + options.MaxViewHierarchyObjectChildCount = 0; + } - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); + options.MaxViewHierarchyDepth = EditorGUILayout.IntField( + new GUIContent("Max Depth", "Maximum depth of the hierarchy to capture. " + + "For example, setting 1 will only capture root GameObjects." + + "\nDefault: 10"), + options.MaxViewHierarchyDepth); + if (options.MaxViewHierarchyDepth <= 0) { - options.AttachViewHierarchy = EditorGUILayout.BeginToggleGroup( - new GUIContent("Attach Hierarchy", "Try to attach the current scene's hierarchy."), - options.AttachViewHierarchy); - - options.MaxViewHierarchyRootObjects = EditorGUILayout.IntField( - new GUIContent("Max Root GameObjects", "Maximum number of captured GameObjects in " + - "a scene root." + - "\nDefault: 100"), - options.MaxViewHierarchyRootObjects); - if (options.MaxViewHierarchyRootObjects <= 0) - { - options.MaxViewHierarchyRootObjects = 0; - } - - options.MaxViewHierarchyObjectChildCount = EditorGUILayout.IntField( - new GUIContent("Max Child Count Per Object", "Maximum number of child objects " + - "captured for each GameObject." + - "\nDefault: 20"), - options.MaxViewHierarchyObjectChildCount); - if (options.MaxViewHierarchyObjectChildCount <= 0) - { - options.MaxViewHierarchyObjectChildCount = 0; - } - - options.MaxViewHierarchyDepth = EditorGUILayout.IntField( - new GUIContent("Max Depth", "Maximum depth of the hierarchy to capture. " + - "For example, setting 1 will only capture root GameObjects." + - "\nDefault: 10"), - options.MaxViewHierarchyDepth); - if (options.MaxViewHierarchyDepth <= 0) - { - options.MaxViewHierarchyDepth = 0; - } - - EditorGUILayout.EndToggleGroup(); + options.MaxViewHierarchyDepth = 0; } + + EditorGUILayout.EndToggleGroup(); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs index 69d65ece7..c21b0c8a0 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs @@ -3,177 +3,176 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal static class OptionsConfigurationTab +{ + public static void Display(ScriptableSentryUnityOptions options) + { + GUILayout.Label("Scriptable Options Configuration", EditorStyles.boldLabel); + + EditorGUILayout.Space(); + + EditorGUILayout.HelpBox( + "The scriptable options configuration allows you to programmatically modify Sentry options." + + "\n" + + "\n" + + "You can use the 'Runtime Configuration Script' to modify options just before Sentry SDK gets " + + "initialized. This allows you to access options and functionality otherwise unavailable from the " + + "Editor UI, e.g. set a custom BeforeSend callback." + + "\n" + + "\n" + + "Use the 'Build Time Configuration Script' in case you need to change build-time behavior, " + + "e.g. specify custom Sentry-CLI options or change settings for native SDKs that start before the " + + "managed layer does (such as Android, iOS, macOS).", + MessageType.Info); + + EditorGUILayout.HelpBox("Clicking the 'New' button will prompt you with selecting a location for " + + "your custom 'SentryConfiguration' script and automatically " + + "create a new asset instance.", MessageType.Info); + + EditorGUILayout.Space(); + + options.RuntimeOptionsConfiguration = OptionsConfigurationItem.Display( + options.RuntimeOptionsConfiguration, + "Runtime Configuration Script", + "SentryRuntimeConfiguration" + ); + + options.BuildTimeOptionsConfiguration = OptionsConfigurationItem.Display( + options.BuildTimeOptionsConfiguration, + "Build Time Configuration Script", + "SentryBuildTimeConfiguration" + ); + } +} + +internal static class OptionsConfigurationItem { - internal static class OptionsConfigurationTab + private const string CreateScriptableObjectFlag = "Sentry/CreateScriptableOptionsObject"; + private const string ScriptNameKey = "Sentry/ScriptableOptionsScript"; + + public static T? Display(T? value, string fieldName, string scriptName) where T : ScriptableObject { - public static void Display(ScriptableSentryUnityOptions options) + GUILayout.BeginHorizontal(); + var result = EditorGUILayout.ObjectField( + new GUIContent(fieldName, "A scriptable object that inherits from 'ScriptableOptionsConfiguration' " + + "and allows you to programmatically modify Sentry options."), + value, + typeof(T), + false + ) as T; + if (GUILayout.Button("New", GUILayout.ExpandWidth(false))) { - GUILayout.Label("Scriptable Options Configuration", EditorStyles.boldLabel); - - EditorGUILayout.Space(); - - EditorGUILayout.HelpBox( - "The scriptable options configuration allows you to programmatically modify Sentry options." + - "\n" + - "\n" + - "You can use the 'Runtime Configuration Script' to modify options just before Sentry SDK gets " + - "initialized. This allows you to access options and functionality otherwise unavailable from the " + - "Editor UI, e.g. set a custom BeforeSend callback." + - "\n" + - "\n" + - "Use the 'Build Time Configuration Script' in case you need to change build-time behavior, " + - "e.g. specify custom Sentry-CLI options or change settings for native SDKs that start before the " + - "managed layer does (such as Android, iOS, macOS).", - MessageType.Info); - - EditorGUILayout.HelpBox("Clicking the 'New' button will prompt you with selecting a location for " + - "your custom 'SentryConfiguration' script and automatically " + - "create a new asset instance.", MessageType.Info); - - EditorGUILayout.Space(); - - options.RuntimeOptionsConfiguration = OptionsConfigurationItem.Display( - options.RuntimeOptionsConfiguration, - "Runtime Configuration Script", - "SentryRuntimeConfiguration" - ); - - options.BuildTimeOptionsConfiguration = OptionsConfigurationItem.Display( - options.BuildTimeOptionsConfiguration, - "Build Time Configuration Script", - "SentryBuildTimeConfiguration" - ); + CreateScript(fieldName, scriptName); } + GUILayout.EndHorizontal(); + return result; } - internal static class OptionsConfigurationItem + private static string SentryAssetPath(string scriptName) => $"Assets/Resources/Sentry/{scriptName}.asset"; + + private static void CreateScript(string fieldName, string scriptName) { - private const string CreateScriptableObjectFlag = "Sentry/CreateScriptableOptionsObject"; - private const string ScriptNameKey = "Sentry/ScriptableOptionsScript"; + const string directory = "Assets/Scripts"; + if (!AssetDatabase.IsValidFolder(directory)) + { + AssetDatabase.CreateFolder(Path.GetDirectoryName(directory), Path.GetFileName(directory)); + } + + var scriptPath = EditorUtility.SaveFilePanel(fieldName, directory, scriptName, "cs"); + if (string.IsNullOrEmpty(scriptPath)) + { + return; + } - public static T? Display(T? value, string fieldName, string scriptName) where T : ScriptableObject + if (scriptPath.StartsWith(Application.dataPath)) { - GUILayout.BeginHorizontal(); - var result = EditorGUILayout.ObjectField( - new GUIContent(fieldName, "A scriptable object that inherits from 'ScriptableOptionsConfiguration' " + - "and allows you to programmatically modify Sentry options."), - value, - typeof(T), - false - ) as T; - if (GUILayout.Button("New", GUILayout.ExpandWidth(false))) - { - CreateScript(fieldName, scriptName); - } - GUILayout.EndHorizontal(); - return result; + // AssetDatabase prefers a relative path + scriptPath = "Assets" + scriptPath.Substring(Application.dataPath.Length); } - private static string SentryAssetPath(string scriptName) => $"Assets/Resources/Sentry/{scriptName}.asset"; + scriptName = Path.GetFileNameWithoutExtension(scriptPath); + + var template = new StringBuilder(); + template.AppendLine("using UnityEngine;"); + template.AppendLine("using Sentry.Unity;"); + template.AppendLine(); + template.AppendFormat("[CreateAssetMenu(fileName = \"{0}\", menuName = \"Sentry/{1}\", order = 999)]\n", SentryAssetPath(scriptName), scriptName); + template.AppendFormat("public class {0} : {1}\n", scriptName, typeof(T).FullName); + template.AppendLine("{"); - private static void CreateScript(string fieldName, string scriptName) + if (typeof(T) == typeof(SentryBuildTimeOptionsConfiguration)) + { + template.AppendLine(" /// Called during app build. Changes made here will affect build-time processing, symbol upload, etc."); + template.AppendLine(" /// Additionally, because iOS, macOS and Android native error handling is configured at build time,"); + template.AppendLine(" /// you can make changes to these options here."); + } + else { - const string directory = "Assets/Scripts"; - if (!AssetDatabase.IsValidFolder(directory)) - { - AssetDatabase.CreateFolder(Path.GetDirectoryName(directory), Path.GetFileName(directory)); - } - - var scriptPath = EditorUtility.SaveFilePanel(fieldName, directory, scriptName, "cs"); - if (string.IsNullOrEmpty(scriptPath)) - { - return; - } - - if (scriptPath.StartsWith(Application.dataPath)) - { - // AssetDatabase prefers a relative path - scriptPath = "Assets" + scriptPath.Substring(Application.dataPath.Length); - } - - scriptName = Path.GetFileNameWithoutExtension(scriptPath); - - var template = new StringBuilder(); - template.AppendLine("using UnityEngine;"); - template.AppendLine("using Sentry.Unity;"); - template.AppendLine(); - template.AppendFormat("[CreateAssetMenu(fileName = \"{0}\", menuName = \"Sentry/{1}\", order = 999)]\n", SentryAssetPath(scriptName), scriptName); - template.AppendFormat("public class {0} : {1}\n", scriptName, typeof(T).FullName); - template.AppendLine("{"); - - if (typeof(T) == typeof(SentryBuildTimeOptionsConfiguration)) - { - template.AppendLine(" /// Called during app build. Changes made here will affect build-time processing, symbol upload, etc."); - template.AppendLine(" /// Additionally, because iOS, macOS and Android native error handling is configured at build time,"); - template.AppendLine(" /// you can make changes to these options here."); - } - else - { - template.AppendLine(" /// Called at the player startup by SentryInitialization."); - template.AppendLine(" /// You can alter configuration for the C# error handling and also"); - template.AppendLine(" /// native error handling in platforms **other** than iOS, macOS and Android."); - } - - template.AppendLine(" /// Learn more at https://docs.sentry.io/platforms/unity/configuration/options/#programmatic-configuration"); - template.AppendFormat(" public override void Configure(SentryUnityOptions options{0})\n", - typeof(T) == typeof(SentryBuildTimeOptionsConfiguration) ? ", SentryCliOptions cliOptions" : ""); - template.AppendLine(" {"); - if (typeof(T) != typeof(SentryBuildTimeOptionsConfiguration)) - { - template.AppendLine(" // Note that changes to the options here will **not** affect iOS, macOS and Android events. (i.e. environment and release)"); - template.AppendLine(" // Take a look at `SentryBuildTimeOptionsConfiguration` instead."); - } - template.AppendLine(" // TODO implement"); - template.AppendLine(" }"); - template.AppendLine("}"); - - File.WriteAllText(scriptPath, template.ToString().Replace("\r\n", "\n")); - - // The created script has to be compiled and the scriptable object can't immediately be instantiated. - // So instead we work around this by setting a 'CreateScriptableObjectFlag' flag in the EditorPrefs to - // trigger the creation after the scripts reloaded. - EditorPrefs.SetBool(CreateScriptableObjectFlag, true); - EditorPrefs.SetString(ScriptNameKey, scriptName); - - AssetDatabase.ImportAsset(scriptPath); - Selection.activeObject = AssetDatabase.LoadAssetAtPath(scriptPath); + template.AppendLine(" /// Called at the player startup by SentryInitialization."); + template.AppendLine(" /// You can alter configuration for the C# error handling and also"); + template.AppendLine(" /// native error handling in platforms **other** than iOS, macOS and Android."); } - [UnityEditor.Callbacks.DidReloadScripts] - private static void OnScriptsReloaded() + template.AppendLine(" /// Learn more at https://docs.sentry.io/platforms/unity/configuration/options/#programmatic-configuration"); + template.AppendFormat(" public override void Configure(SentryUnityOptions options{0})\n", + typeof(T) == typeof(SentryBuildTimeOptionsConfiguration) ? ", SentryCliOptions cliOptions" : ""); + template.AppendLine(" {"); + if (typeof(T) != typeof(SentryBuildTimeOptionsConfiguration)) { - if (!EditorPrefs.GetBool(CreateScriptableObjectFlag)) - { - return; - } + template.AppendLine(" // Note that changes to the options here will **not** affect iOS, macOS and Android events. (i.e. environment and release)"); + template.AppendLine(" // Take a look at `SentryBuildTimeOptionsConfiguration` instead."); + } + template.AppendLine(" // TODO implement"); + template.AppendLine(" }"); + template.AppendLine("}"); + + File.WriteAllText(scriptPath, template.ToString().Replace("\r\n", "\n")); - var scriptName = EditorPrefs.GetString(ScriptNameKey); - EditorPrefs.DeleteKey(CreateScriptableObjectFlag); - EditorPrefs.DeleteKey(ScriptNameKey); + // The created script has to be compiled and the scriptable object can't immediately be instantiated. + // So instead we work around this by setting a 'CreateScriptableObjectFlag' flag in the EditorPrefs to + // trigger the creation after the scripts reloaded. + EditorPrefs.SetBool(CreateScriptableObjectFlag, true); + EditorPrefs.SetString(ScriptNameKey, scriptName); + + AssetDatabase.ImportAsset(scriptPath); + Selection.activeObject = AssetDatabase.LoadAssetAtPath(scriptPath); + } - SetScript(scriptName); + [UnityEditor.Callbacks.DidReloadScripts] + private static void OnScriptsReloaded() + { + if (!EditorPrefs.GetBool(CreateScriptableObjectFlag)) + { + return; } - internal static void SetScript(string scriptNameWithoutExtension) + var scriptName = EditorPrefs.GetString(ScriptNameKey); + EditorPrefs.DeleteKey(CreateScriptableObjectFlag); + EditorPrefs.DeleteKey(ScriptNameKey); + + SetScript(scriptName); + } + + internal static void SetScript(string scriptNameWithoutExtension) + { + var optionsConfigurationObject = ScriptableObject.CreateInstance(scriptNameWithoutExtension); + var isEditorScript = optionsConfigurationObject is SentryBuildTimeOptionsConfiguration; + AssetDatabase.CreateAsset(optionsConfigurationObject, SentryAssetPath(scriptNameWithoutExtension)); + AssetDatabase.Refresh(); + + var options = EditorWindow.GetWindow().Options; + + if (isEditorScript) { - var optionsConfigurationObject = ScriptableObject.CreateInstance(scriptNameWithoutExtension); - var isEditorScript = optionsConfigurationObject is SentryBuildTimeOptionsConfiguration; - AssetDatabase.CreateAsset(optionsConfigurationObject, SentryAssetPath(scriptNameWithoutExtension)); - AssetDatabase.Refresh(); - - var options = EditorWindow.GetWindow().Options; - - if (isEditorScript) - { - // Don't overwrite already set OptionsConfiguration - options.BuildTimeOptionsConfiguration ??= optionsConfigurationObject as SentryBuildTimeOptionsConfiguration; - } - else - { - // Don't overwrite already set OptionsConfiguration - options.RuntimeOptionsConfiguration ??= optionsConfigurationObject as SentryRuntimeOptionsConfiguration; - } + // Don't overwrite already set OptionsConfiguration + options.BuildTimeOptionsConfiguration ??= optionsConfigurationObject as SentryBuildTimeOptionsConfiguration; + } + else + { + // Don't overwrite already set OptionsConfiguration + options.RuntimeOptionsConfiguration ??= optionsConfigurationObject as SentryRuntimeOptionsConfiguration; } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs index 4b8d24ce8..af3738405 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs @@ -4,71 +4,70 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +public static class SentryEditorWindowInstrumentation { - public static class SentryEditorWindowInstrumentation + /// + /// Allows the configuration of Sentry Options using Unity Batch mode. + /// + public static void ConfigureOptions() => ConfigureOptions(CommandLineArgumentParser.Parse()); + + private static void ConfigureOptions(Dictionary args, [CallerMemberName] string functionName = "") { - /// - /// Allows the configuration of Sentry Options using Unity Batch mode. - /// - public static void ConfigureOptions() => ConfigureOptions(CommandLineArgumentParser.Parse()); + Debug.LogFormat("{0}: Invoking SentryOptions", functionName); - private static void ConfigureOptions(Dictionary args, [CallerMemberName] string functionName = "") + if (!EditorApplication.ExecuteMenuItem(SentryWindow.EditorMenuPath.Replace(" -> ", "/"))) { - Debug.LogFormat("{0}: Invoking SentryOptions", functionName); + throw new Exception($"{functionName} failed: Menu item '{SentryWindow.EditorMenuPath}' not found. Was the Sentry UPM package installed?"); + } - if (!EditorApplication.ExecuteMenuItem(SentryWindow.EditorMenuPath.Replace(" -> ", "/"))) - { - throw new Exception($"{functionName} failed: Menu item '{SentryWindow.EditorMenuPath}' not found. Was the Sentry UPM package installed?"); - } + var optionsWindow = EditorWindow.GetWindow(); + var options = optionsWindow.Options; + var cliOptions = optionsWindow.CliOptions; - var optionsWindow = EditorWindow.GetWindow(); - var options = optionsWindow.Options; - var cliOptions = optionsWindow.CliOptions; + if (options is null || cliOptions is null) + { + throw new InvalidOperationException($"{functionName} failed: SentryOptions not found"); + } + Debug.LogFormat("{0}: Found SentryOptions", functionName); - if (options is null || cliOptions is null) - { - throw new InvalidOperationException($"{functionName} failed: SentryOptions not found"); - } - Debug.LogFormat("{0}: Found SentryOptions", functionName); + var value = ""; + if (args.TryGetValue("runtimeOptionsScript", out value)) + { + Debug.LogFormat("{0}: Configuring Runtime Options Script to {1}", functionName, value); + OptionsConfigurationItem.SetScript(value); + } - var value = ""; - if (args.TryGetValue("runtimeOptionsScript", out value)) - { - Debug.LogFormat("{0}: Configuring Runtime Options Script to {1}", functionName, value); - OptionsConfigurationItem.SetScript(value); - } + if (args.TryGetValue("buildTimeOptionsScript", out value)) + { + Debug.LogFormat("{0}: Configuring Build Time Options Script to {1}", functionName, value); + OptionsConfigurationItem.SetScript(value); + } - if (args.TryGetValue("buildTimeOptionsScript", out value)) - { - Debug.LogFormat("{0}: Configuring Build Time Options Script to {1}", functionName, value); - OptionsConfigurationItem.SetScript(value); - } + if (args.TryGetValue("cliOptions.UrlOverride", out value)) + { + Debug.LogFormat("{0}: Configuring symbol-upload UrlOverride to {1}", functionName, value); + cliOptions.UrlOverride = value; + } - if (args.TryGetValue("cliOptions.UrlOverride", out value)) - { - Debug.LogFormat("{0}: Configuring symbol-upload UrlOverride to {1}", functionName, value); - cliOptions.UrlOverride = value; - } + optionsWindow.Close(); + Debug.LogFormat("{0}: SUCCESS", functionName); + } - optionsWindow.Close(); - Debug.LogFormat("{0}: SUCCESS", functionName); + public static bool TryGetValue(this Dictionary dict, String key, out bool value) + { + string strValue; + value = false; + if (!dict.TryGetValue(key, out strValue)) + { + return false; } - public static bool TryGetValue(this Dictionary dict, String key, out bool value) + if (!Boolean.TryParse(strValue, out value)) { - string strValue; - value = false; - if (!dict.TryGetValue(key, out strValue)) - { - return false; - } - - if (!Boolean.TryParse(strValue, out value)) - { - throw new ArgumentException("Unknown boolean argument value: " + strValue, key); - } - return true; + throw new ArgumentException("Unknown boolean argument value: " + strValue, key); } + return true; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs index 44cb3fe7c..d7eae8841 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs @@ -2,23 +2,22 @@ using System.IO; using UnityEditor; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +public sealed class SentryTestWindow : SentryWindow, IDisposable { - public sealed class SentryTestWindow : SentryWindow, IDisposable + public SentryTestWindow() { - public SentryTestWindow() - { - // static - SentryWindow.SentryOptionsAssetName = Path.GetRandomFileName(); - } + // static + SentryWindow.SentryOptionsAssetName = Path.GetRandomFileName(); + } - public static SentryTestWindow Open() - => (SentryTestWindow)GetWindow(typeof(SentryTestWindow)); + public static SentryTestWindow Open() + => (SentryTestWindow)GetWindow(typeof(SentryTestWindow)); - public void Dispose() - { - Close(); // calls 'OnLostFocus' implicitly - AssetDatabase.DeleteAsset(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName)); - } + public void Dispose() + { + Close(); // calls 'OnLostFocus' implicitly + AssetDatabase.DeleteAsset(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName)); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs index 81a07c800..8fbc73222 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs @@ -1,239 +1,237 @@ using System; -using System.Data.Common; using System.IO; using Sentry.Extensibility; using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +public class SentryWindow : EditorWindow { - public class SentryWindow : EditorWindow - { - private const string LinkXmlPath = "Assets/Plugins/Sentry/link.xml"; + private const string LinkXmlPath = "Assets/Plugins/Sentry/link.xml"; - public const string EditorMenuPath = "Tools -> Sentry"; + public const string EditorMenuPath = "Tools -> Sentry"; - [MenuItem("Tools/Sentry")] - public static void OnMenuClick() + [MenuItem("Tools/Sentry")] + public static void OnMenuClick() + { + if (Wizard.InProgress) { - if (Wizard.InProgress) - { - Debug.Log("Wizard in progress, ignoring Tools/Sentry menu click"); - return; - } - if (Instance is null && IsFirstLoad && EditorUtility.DisplayDialog("Start a setup wizard?", + Debug.Log("Wizard in progress, ignoring Tools/Sentry menu click"); + return; + } + if (Instance is null && IsFirstLoad && EditorUtility.DisplayDialog("Start a setup wizard?", "It looks like you're setting up Sentry for the first time in this project.\n\n" + "Would you like to start a setup wizard to connect to sentry.io?", "Start wizard", "I'll set it up manually")) - { - Wizard.Start(CreateLogger()); - } - else - { - OpenSentryWindow(); - } + { + Wizard.Start(CreateLogger()); } - - public static void OpenSentryWindow() + else { - Instance = GetWindow(); - Instance.minSize = new Vector2(600, 420); + OpenSentryWindow(); } + } - public static SentryWindow? Instance; + public static void OpenSentryWindow() + { + Instance = GetWindow(); + Instance.minSize = new Vector2(600, 420); + } - protected static string SentryOptionsAssetName { get; set; } = ScriptableSentryUnityOptions.ConfigName; - protected static string SentryCliAssetName { get; } = SentryCliOptions.ConfigName; + public static SentryWindow? Instance; - public ScriptableSentryUnityOptions Options { get; private set; } = null!; // Set by OnEnable() - public SentryCliOptions CliOptions { get; private set; } = null!; // Set by OnEnable() - public static Texture2D? ErrorIcon { get; private set; } + protected static string SentryOptionsAssetName { get; set; } = ScriptableSentryUnityOptions.ConfigName; + protected static string SentryCliAssetName { get; } = SentryCliOptions.ConfigName; - public event Action OnValidationError = _ => { }; + public ScriptableSentryUnityOptions Options { get; private set; } = null!; // Set by OnEnable() + public SentryCliOptions CliOptions { get; private set; } = null!; // Set by OnEnable() + public static Texture2D? ErrorIcon { get; private set; } - private int _currentTab = 0; - private readonly string[] _tabs = - { - "Core", - "Enrichment", - "Transport", - "Advanced", - "Options Config", - "Debug Symbols" - }; + public event Action OnValidationError = _ => { }; - private IDiagnosticLogger _logger; + private int _currentTab = 0; + private readonly string[] _tabs = + { + "Core", + "Enrichment", + "Transport", + "Advanced", + "Options Config", + "Debug Symbols" + }; - private static string OptionsPath => ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName); - private static string CliOptionsPath => SentryCliOptions.GetConfigPath(SentryCliAssetName); + private IDiagnosticLogger _logger; - public SentryWindow() - { - _logger = CreateLogger(); - } + private static string OptionsPath => ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName); + private static string CliOptionsPath => SentryCliOptions.GetConfigPath(SentryCliAssetName); - private static IDiagnosticLogger CreateLogger() => - new UnityLogger(new SentryOptions() { Debug = SentryPackageInfo.IsDevPackage }); + public SentryWindow() + { + _logger = CreateLogger(); + } - void OnDestroy() - { - Instance = null; - } + private static IDiagnosticLogger CreateLogger() => + new UnityLogger(new SentryOptions() { Debug = SentryPackageInfo.IsDevPackage }); - private void OnEnable() - { - // Note: these are not allowed to be called in constructors - SetTitle(this); - Options = SentryScriptableObject.CreateOrLoad(OptionsPath); - CliOptions = SentryScriptableObject.CreateOrLoad(CliOptionsPath); - ErrorIcon = EditorGUIUtility.Load("icons/console.erroricon.png") as Texture2D; - } + void OnDestroy() + { + Instance = null; + } + + private void OnEnable() + { + // Note: these are not allowed to be called in constructors + SetTitle(this); + Options = SentryScriptableObject.CreateOrLoad(OptionsPath); + CliOptions = SentryScriptableObject.CreateOrLoad(CliOptionsPath); + ErrorIcon = EditorGUIUtility.Load("icons/console.erroricon.png") as Texture2D; + } + + internal static void SaveWizardResult(WizardConfiguration config) + { + var options = SentryScriptableObject.CreateOrLoad(OptionsPath); + var cliOptions = SentryScriptableObject.CreateOrLoad(CliOptionsPath); + options.Dsn = config.Dsn; + cliOptions.UploadSymbols = !string.IsNullOrWhiteSpace(config.Token); + cliOptions.Auth = config.Token; + cliOptions.Organization = config.OrgSlug; + cliOptions.Project = config.ProjectSlug; + + EditorUtility.SetDirty(options); + EditorUtility.SetDirty(cliOptions); + AssetDatabase.SaveAssets(); + } - internal static void SaveWizardResult(WizardConfiguration config) + private static bool IsFirstLoad => + !File.Exists(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName)) && + !File.Exists(SentryCliOptions.GetConfigPath(SentryCliAssetName)); + + // ReSharper disable once UnusedMember.Local + private void OnGUI() + { + EditorGUILayout.Space(); + GUILayout.Label("SDK Options", EditorStyles.boldLabel); + + Options.Enabled = EditorGUILayout.ToggleLeft( + new GUIContent("Enable Sentry", "Controls if the SDK should initialize itself or not."), + Options.Enabled); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + var selectedTab = GUILayout.Toolbar(_currentTab, _tabs); + if (selectedTab != _currentTab) { - var options = SentryScriptableObject.CreateOrLoad(OptionsPath); - var cliOptions = SentryScriptableObject.CreateOrLoad(CliOptionsPath); - options.Dsn = config.Dsn; - cliOptions.UploadSymbols = !string.IsNullOrWhiteSpace(config.Token); - cliOptions.Auth = config.Token; - cliOptions.Organization = config.OrgSlug; - cliOptions.Project = config.ProjectSlug; - - EditorUtility.SetDirty(options); - EditorUtility.SetDirty(cliOptions); - AssetDatabase.SaveAssets(); + // Edge-case: Lose focus so currently selected fields don't "bleed" through like DSN -> Override Release + GUI.FocusControl(null); + _currentTab = selectedTab; } - private static bool IsFirstLoad => - !File.Exists(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName)) && - !File.Exists(SentryCliOptions.GetConfigPath(SentryCliAssetName)); + EditorGUI.BeginDisabledGroup(!Options.Enabled); + EditorGUILayout.Space(); - // ReSharper disable once UnusedMember.Local - private void OnGUI() + switch (_currentTab) { - EditorGUILayout.Space(); - GUILayout.Label("SDK Options", EditorStyles.boldLabel); - - Options.Enabled = EditorGUILayout.ToggleLeft( - new GUIContent("Enable Sentry", "Controls if the SDK should initialize itself or not."), - Options.Enabled); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - var selectedTab = GUILayout.Toolbar(_currentTab, _tabs); - if (selectedTab != _currentTab) - { - // Edge-case: Lose focus so currently selected fields don't "bleed" through like DSN -> Override Release - GUI.FocusControl(null); - _currentTab = selectedTab; - } - - EditorGUI.BeginDisabledGroup(!Options.Enabled); - EditorGUILayout.Space(); - - switch (_currentTab) - { - case 0: - CoreTab.Display(Options); - break; - case 1: - EnrichmentTab.Display(Options); - break; - case 2: - TransportTab.Display(Options); - break; - case 3: - AdvancedTab.Display(Options, CliOptions); - break; - case 4: - OptionsConfigurationTab.Display(Options); - break; - case 5: - DebugSymbolsTab.Display(CliOptions); - break; - default: - break; - } - - EditorGUI.EndDisabledGroup(); + case 0: + CoreTab.Display(Options); + break; + case 1: + EnrichmentTab.Display(Options); + break; + case 2: + TransportTab.Display(Options); + break; + case 3: + AdvancedTab.Display(Options, CliOptions); + break; + case 4: + OptionsConfigurationTab.Display(Options); + break; + case 5: + DebugSymbolsTab.Display(CliOptions); + break; + default: + break; } - private void OnLostFocus() + EditorGUI.EndDisabledGroup(); + } + + private void OnLostFocus() + { + // Make sure the actual config asset exists before validating/saving. Crashes the editor otherwise. + if (!File.Exists(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName))) { - // Make sure the actual config asset exists before validating/saving. Crashes the editor otherwise. - if (!File.Exists(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName))) - { - _logger.LogWarning("Options could not been saved. The configuration asset is missing."); - return; - } - - Validate(); - - EditorUtility.SetDirty(Options); - EditorUtility.SetDirty(CliOptions); - AssetDatabase.SaveAssets(); + _logger.LogWarning("Options could not been saved. The configuration asset is missing."); + return; } - private void Validate() - { - if (!Options.Enabled) - { - return; - } + Validate(); - ValidateDsn(); - } + EditorUtility.SetDirty(Options); + EditorUtility.SetDirty(CliOptions); + AssetDatabase.SaveAssets(); + } - internal void ValidateDsn() + private void Validate() + { + if (!Options.Enabled) { - if (string.IsNullOrWhiteSpace(Options.Dsn)) - { - return; - } - - if (Uri.IsWellFormedUriString(Options.Dsn, UriKind.Absolute)) - { - return; - } + return; + } - var fullFieldName = $"{nameof(Options)}.{nameof(Options.Dsn)}"; - var validationError = new ValidationError(fullFieldName, "Invalid DSN format. Expected a URL."); - OnValidationError(validationError); + ValidateDsn(); + } - _logger.LogWarning(validationError.ToString()); + internal void ValidateDsn() + { + if (string.IsNullOrWhiteSpace(Options.Dsn)) + { + return; } - internal static void SetTitle(EditorWindow window, string title = "Sentry", string description = "Sentry SDK options") + if (Uri.IsWellFormedUriString(Options.Dsn, UriKind.Absolute)) { - var isDarkMode = EditorGUIUtility.isProSkin; - var texture = new Texture2D(16, 16); - using var memStream = new MemoryStream(); - using var stream = window.GetType().Assembly - .GetManifestResourceStream( - $"Sentry.Unity.Editor.Resources.SentryLogo{(isDarkMode ? "Light" : "Dark")}.png"); - stream.CopyTo(memStream); - stream.Flush(); - memStream.Position = 0; - texture.LoadImage(memStream.ToArray()); - - window.titleContent = new GUIContent(title, texture, description); + return; } + + var fullFieldName = $"{nameof(Options)}.{nameof(Options.Dsn)}"; + var validationError = new ValidationError(fullFieldName, "Invalid DSN format. Expected a URL."); + OnValidationError(validationError); + + _logger.LogWarning(validationError.ToString()); } - public readonly struct ValidationError + internal static void SetTitle(EditorWindow window, string title = "Sentry", string description = "Sentry SDK options") { - public readonly string PropertyName; + var isDarkMode = EditorGUIUtility.isProSkin; + var texture = new Texture2D(16, 16); + using var memStream = new MemoryStream(); + using var stream = window.GetType().Assembly + .GetManifestResourceStream( + $"Sentry.Unity.Editor.Resources.SentryLogo{(isDarkMode ? "Light" : "Dark")}.png"); + stream.CopyTo(memStream); + stream.Flush(); + memStream.Position = 0; + texture.LoadImage(memStream.ToArray()); + + window.titleContent = new GUIContent(title, texture, description); + } +} - public readonly string Reason; +public readonly struct ValidationError +{ + public readonly string PropertyName; - public ValidationError(string propertyName, string reason) - { - PropertyName = propertyName; - Reason = reason; - } + public readonly string Reason; - public override string ToString() - => $"[{PropertyName}] Reason: {Reason}"; + public ValidationError(string propertyName, string reason) + { + PropertyName = propertyName; + Reason = reason; } -} + + public override string ToString() + => $"[{PropertyName}] Reason: {Reason}"; +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs index 7ae5c0b6d..88d27fc29 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs @@ -2,63 +2,62 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal static class TransportTab { - internal static class TransportTab + internal static void Display(ScriptableSentryUnityOptions options) { - internal static void Display(ScriptableSentryUnityOptions options) - { - options.EnableOfflineCaching = EditorGUILayout.BeginToggleGroup( - new GUIContent("Enable Offline Caching", ""), - options.EnableOfflineCaching); - - options.MaxCacheItems = EditorGUILayout.IntField( - new GUIContent("Max Cache Items", "The maximum number of files to keep in the disk cache. " + - "The SDK deletes the oldest when the limit is reached.\nDefault: 30"), - options.MaxCacheItems); - options.MaxCacheItems = Math.Max(0, options.MaxCacheItems); - - options.InitCacheFlushTimeout = EditorGUILayout.IntField( - new GUIContent("Init Flush Timeout [ms]", "The timeout that limits how long the SDK " + - "will attempt to flush existing cache during initialization, " + - "potentially slowing down app start up to the specified time." + - "\nThis features allows capturing errors that happen during " + - "game startup and would not be captured because the process " + - "would be killed before Sentry had a chance to capture the event."), - options.InitCacheFlushTimeout); - options.InitCacheFlushTimeout = Math.Max(0, options.InitCacheFlushTimeout); - - EditorGUILayout.EndToggleGroup(); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - // Options.RequestBodyCompressionLevel = (CompressionLevelWithAuto)EditorGUILayout.EnumPopup( - // new GUIContent("Compress Payload", "The level of which to compress the Sentry event " + - // "before sending to Sentry."), - // Options.RequestBodyCompressionLevel); - - options.SampleRate = EditorGUILayout.Slider( - new GUIContent("Event Sample Rate", "Indicates the percentage of events that are " + - "captured. Setting this to 0.1 captures 10% of events. " + - "Setting this to 1.0 captures all events." + - "\nThis affects only errors and logs, not performance " + - "(transactions) data. See TraceSampleRate for that."), - options.SampleRate, 0.01f, 1); - - options.ShutdownTimeout = EditorGUILayout.IntField( - new GUIContent("Shut Down Timeout [ms]", "How many milliseconds to wait before shutting down to " + - "give Sentry time to send events from the background queue."), - options.ShutdownTimeout); - options.ShutdownTimeout = Mathf.Clamp(options.ShutdownTimeout, 0, int.MaxValue); - - options.MaxQueueItems = EditorGUILayout.IntField( - new GUIContent("Max Queue Items", "The maximum number of events to keep in memory while " + - "the worker attempts to send them."), - options.MaxQueueItems - ); - options.MaxQueueItems = Math.Max(0, options.MaxQueueItems); - } + options.EnableOfflineCaching = EditorGUILayout.BeginToggleGroup( + new GUIContent("Enable Offline Caching", ""), + options.EnableOfflineCaching); + + options.MaxCacheItems = EditorGUILayout.IntField( + new GUIContent("Max Cache Items", "The maximum number of files to keep in the disk cache. " + + "The SDK deletes the oldest when the limit is reached.\nDefault: 30"), + options.MaxCacheItems); + options.MaxCacheItems = Math.Max(0, options.MaxCacheItems); + + options.InitCacheFlushTimeout = EditorGUILayout.IntField( + new GUIContent("Init Flush Timeout [ms]", "The timeout that limits how long the SDK " + + "will attempt to flush existing cache during initialization, " + + "potentially slowing down app start up to the specified time." + + "\nThis features allows capturing errors that happen during " + + "game startup and would not be captured because the process " + + "would be killed before Sentry had a chance to capture the event."), + options.InitCacheFlushTimeout); + options.InitCacheFlushTimeout = Math.Max(0, options.InitCacheFlushTimeout); + + EditorGUILayout.EndToggleGroup(); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + // Options.RequestBodyCompressionLevel = (CompressionLevelWithAuto)EditorGUILayout.EnumPopup( + // new GUIContent("Compress Payload", "The level of which to compress the Sentry event " + + // "before sending to Sentry."), + // Options.RequestBodyCompressionLevel); + + options.SampleRate = EditorGUILayout.Slider( + new GUIContent("Event Sample Rate", "Indicates the percentage of events that are " + + "captured. Setting this to 0.1 captures 10% of events. " + + "Setting this to 1.0 captures all events." + + "\nThis affects only errors and logs, not performance " + + "(transactions) data. See TraceSampleRate for that."), + options.SampleRate, 0.01f, 1); + + options.ShutdownTimeout = EditorGUILayout.IntField( + new GUIContent("Shut Down Timeout [ms]", "How many milliseconds to wait before shutting down to " + + "give Sentry time to send events from the background queue."), + options.ShutdownTimeout); + options.ShutdownTimeout = Mathf.Clamp(options.ShutdownTimeout, 0, int.MaxValue); + + options.MaxQueueItems = EditorGUILayout.IntField( + new GUIContent("Max Queue Items", "The maximum number of events to keep in memory while " + + "the worker attempts to send them."), + options.MaxQueueItems + ); + options.MaxQueueItems = Math.Max(0, options.MaxQueueItems); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs index 480dae8ca..e462eed5c 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs @@ -8,350 +8,349 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.ConfigurationWindow +namespace Sentry.Unity.Editor.ConfigurationWindow; + +internal class Wizard : EditorWindow { - internal class Wizard : EditorWindow - { - private int _projectSelected = 0; - private int _orgSelected = 0; + private int _projectSelected = 0; + private int _orgSelected = 0; - private static Wizard? Instance; - internal WizardStep2Response? Response { get; set; } - private IDiagnosticLogger _logger = null!; - private WizardLoader? _task; + private static Wizard? Instance; + internal WizardStep2Response? Response { get; set; } + private IDiagnosticLogger _logger = null!; + private WizardLoader? _task; - public static void Start(IDiagnosticLogger logger) + public static void Start(IDiagnosticLogger logger) + { + if (Instance is null) { - if (Instance is null) - { - Instance = CreateInstance(); - Instance._logger = logger; + Instance = CreateInstance(); + Instance._logger = logger; - SentryWindow.SetTitle(Instance, description: "Setup wizard"); + SentryWindow.SetTitle(Instance, description: "Setup wizard"); - Instance.ShowUtility(); - Instance.minSize = new Vector2(600, 200); - Instance.StartLoader(); - } + Instance.ShowUtility(); + Instance.minSize = new Vector2(600, 200); + Instance.StartLoader(); } + } - private void StartLoader() + private void StartLoader() + { + _task = new WizardLoader(_logger); + Task.Run(async () => Response = await _task.Load()).ContinueWith(t => { - _task = new WizardLoader(_logger); - Task.Run(async () => Response = await _task.Load()).ContinueWith(t => + if (t.Exception is not null) { - if (t.Exception is not null) - { - _logger.Log(SentryLevel.Warning, "Wizard loader failed", t.Exception); - } - }); + _logger.Log(SentryLevel.Warning, "Wizard loader failed", t.Exception); + } + }); + } + + public static bool InProgress => Instance is not null; + + private void OnGUI() + { + if (Response is null) + { + return; } - public static bool InProgress => Instance is not null; + WizardConfiguration? wizardConfiguration = null; + + EditorGUILayout.Space(); - private void OnGUI() + if (Response.projects.Count == 0) { - if (Response is null) + wizardConfiguration = new WizardConfiguration { - return; - } - - WizardConfiguration? wizardConfiguration = null; + Token = Response.apiKeys!.token + }; + EditorGUILayout.LabelField("There don't seem to be any projects in your sentry.io account."); + } + else + { + EditorGUILayout.LabelField("Please select the organization and project you'd like to use."); EditorGUILayout.Space(); - if (Response.projects.Count == 0) + var blankEntry = new string(' ', 60); + + // sort "unity" projects first + Response.projects.Sort((a, b) => { - wizardConfiguration = new WizardConfiguration + if (a.IsUnity == b.IsUnity) { - Token = Response.apiKeys!.token - }; + return (a.name ?? "").CompareTo(b.name ?? ""); + } + else if (a.IsUnity) + { + return -1; + } + else + { + return 1; + } + }); - EditorGUILayout.LabelField("There don't seem to be any projects in your sentry.io account."); - } - else + var orgsAndProjects = Response.projects.GroupBy(k => k.organization!.name, v => v); + var orgs = orgsAndProjects.Select(k => k.Key).ToArray(); + if (orgs.Length > 1) { - EditorGUILayout.LabelField("Please select the organization and project you'd like to use."); - EditorGUILayout.Space(); - - var blankEntry = new string(' ', 60); + orgs = orgs.Prepend(blankEntry).ToArray(); + } - // sort "unity" projects first - Response.projects.Sort((a, b) => - { - if (a.IsUnity == b.IsUnity) - { - return (a.name ?? "").CompareTo(b.name ?? ""); - } - else if (a.IsUnity) - { - return -1; - } - else - { - return 1; - } - }); + _orgSelected = EditorGUILayout.Popup("Organization", _orgSelected, orgs); - var orgsAndProjects = Response.projects.GroupBy(k => k.organization!.name, v => v); - var orgs = orgsAndProjects.Select(k => k.Key).ToArray(); - if (orgs.Length > 1) + if (orgs.Length == 1 || _orgSelected > 0) + { + var projects = orgsAndProjects.Where(k => k.Key == orgs[_orgSelected]).SelectMany(p => p).ToArray(); + var projectNames = projects.Select(v => v.name).ToArray(); + if (projectNames.Length > 1) { - orgs = orgs.Prepend(blankEntry).ToArray(); + projectNames = projectNames.Prepend(blankEntry).ToArray(); } - _orgSelected = EditorGUILayout.Popup("Organization", _orgSelected, orgs); + _projectSelected = EditorGUILayout.Popup("Project", _projectSelected, projectNames); - if (orgs.Length == 1 || _orgSelected > 0) + if (projects.Length == 1 || _projectSelected > 0) { - var projects = orgsAndProjects.Where(k => k.Key == orgs[_orgSelected]).SelectMany(p => p).ToArray(); - var projectNames = projects.Select(v => v.name).ToArray(); - if (projectNames.Length > 1) - { - projectNames = projectNames.Prepend(blankEntry).ToArray(); - } - - _projectSelected = EditorGUILayout.Popup("Project", _projectSelected, projectNames); - - if (projects.Length == 1 || _projectSelected > 0) + var project = projects.Where(p => p.name == projectNames[_projectSelected]).ToArray()[0]; + wizardConfiguration = new WizardConfiguration { - var project = projects.Where(p => p.name == projectNames[_projectSelected]).ToArray()[0]; - wizardConfiguration = new WizardConfiguration - { - Token = Response.apiKeys!.token, - Dsn = project.keys.First().dsn!.@public, - OrgSlug = project.organization!.slug, - ProjectSlug = project.slug, - }; - } + Token = Response.apiKeys!.token, + Dsn = project.keys.First().dsn!.@public, + OrgSlug = project.organization!.slug, + ProjectSlug = project.slug, + }; } } + } - if (wizardConfiguration != null) - { - GUILayout.FlexibleSpace(); + if (wizardConfiguration != null) + { + GUILayout.FlexibleSpace(); - EditorGUILayout.LabelField("We have updated the default options with your selection."); + EditorGUILayout.LabelField("We have updated the default options with your selection."); - EditorGUILayout.HelpBox( - "Sentry options persist in two assets in your project directory:\n" + - " Resources/Sentry/SentryOptions.asset contains the main configuration,\n" + - " Plugins/Sentry/SentryCliOptions.asset contains the settings for debug symbol upload.", - MessageType.Info); - EditorGUILayout.HelpBox( - "Make sure to keep the SentryCliOptions.asset private because it contains an API authentication token linked to your account.", - MessageType.Warning); + EditorGUILayout.HelpBox( + "Sentry options persist in two assets in your project directory:\n" + + " Resources/Sentry/SentryOptions.asset contains the main configuration,\n" + + " Plugins/Sentry/SentryCliOptions.asset contains the settings for debug symbol upload.", + MessageType.Info); + EditorGUILayout.HelpBox( + "Make sure to keep the SentryCliOptions.asset private because it contains an API authentication token linked to your account.", + MessageType.Warning); - GUILayout.FlexibleSpace(); + GUILayout.FlexibleSpace(); - EditorGUILayout.LabelField("Would you like to inspect the settings or leave at defaults for now? "); - EditorGUILayout.LabelField("You can always make changes later in the Tools/Sentry menu."); - EditorGUILayout.Space(); + EditorGUILayout.LabelField("Would you like to inspect the settings or leave at defaults for now? "); + EditorGUILayout.LabelField("You can always make changes later in the Tools/Sentry menu."); + EditorGUILayout.Space(); - GUILayout.BeginHorizontal(); - if (GUILayout.Button("I want to see all the options!", GUILayout.ExpandWidth(false))) - { - SentryWindow.SaveWizardResult(wizardConfiguration); - Close(); - SentryWindow.OpenSentryWindow(); - } - GUILayout.FlexibleSpace(); - if (GUILayout.Button("I'm fine with the defaults, take me back to Unity!", GUILayout.ExpandWidth(false))) - { - SentryWindow.SaveWizardResult(wizardConfiguration); - Close(); - } - GUILayout.EndHorizontal(); + GUILayout.BeginHorizontal(); + if (GUILayout.Button("I want to see all the options!", GUILayout.ExpandWidth(false))) + { + SentryWindow.SaveWizardResult(wizardConfiguration); + Close(); + SentryWindow.OpenSentryWindow(); } + GUILayout.FlexibleSpace(); + if (GUILayout.Button("I'm fine with the defaults, take me back to Unity!", GUILayout.ExpandWidth(false))) + { + SentryWindow.SaveWizardResult(wizardConfiguration); + Close(); + } + GUILayout.EndHorizontal(); } + } - void OnDestroy() + void OnDestroy() + { + Instance = null; + } + + // called multiple times per second to update status on the UI thread. + internal void Update() + { + if (_task is null) { - Instance = null; + return; } - // called multiple times per second to update status on the UI thread. - internal void Update() + if (_task._cancelled) { - if (_task is null) - { - return; - } - - if (_task._cancelled) - { - EditorUtility.ClearProgressBar(); - Close(); - return; - } + EditorUtility.ClearProgressBar(); + Close(); + return; + } - if (_task._done) - { - EditorUtility.ClearProgressBar(); - if (_task._exception is not null && EditorUtility.DisplayDialog("Wizard error", + if (_task._done) + { + EditorUtility.ClearProgressBar(); + if (_task._exception is not null && EditorUtility.DisplayDialog("Wizard error", $"Couldn't launch the wizard. Would you like to try again? The error was:\n\n{_task._exception.Message}", "Retry wizard", "I'll set it up manually")) - { - StartLoader(); - } - return; - } - - if (_task._progress > 0.0f) { - _task._cancelled = EditorUtility.DisplayCancelableProgressBar( - "Sentry setup wizard", _task._progressText, _task._progress); + StartLoader(); } - - _task._uiAction?.Invoke(); + return; } - internal static void OpenUrl(string url) + if (_task._progress > 0.0f) { - // Verify that the URL is a valid HTTPS - we don't want to launch just about any process (say... malware.exe) - var parsedUri = new Uri(url); - if (parsedUri.Scheme != Uri.UriSchemeHttps) - { - throw new Exception($"Can't open given URL - only HTTPS scheme is allowed, but got: {url}"); - } - - Application.OpenURL(parsedUri.ToString()); + _task._cancelled = EditorUtility.DisplayCancelableProgressBar( + "Sentry setup wizard", _task._progressText, _task._progress); } + + _task._uiAction?.Invoke(); } - internal class WizardConfiguration + internal static void OpenUrl(string url) { - public string? Token { get; set; } - public string? Dsn { get; set; } - public string? OrgSlug { get; set; } - public string? ProjectSlug { get; set; } + // Verify that the URL is a valid HTTPS - we don't want to launch just about any process (say... malware.exe) + var parsedUri = new Uri(url); + if (parsedUri.Scheme != Uri.UriSchemeHttps) + { + throw new Exception($"Can't open given URL - only HTTPS scheme is allowed, but got: {url}"); + } + + Application.OpenURL(parsedUri.ToString()); } +} - internal class WizardCancelled : Exception +internal class WizardConfiguration +{ + public string? Token { get; set; } + public string? Dsn { get; set; } + public string? OrgSlug { get; set; } + public string? ProjectSlug { get; set; } +} + +internal class WizardCancelled : Exception +{ + internal WizardCancelled() : base() { } + internal WizardCancelled(string message) : base(message) { } + internal WizardCancelled(string message, Exception innerException) : base(message, innerException) { } +} + +internal class WizardLoader +{ + private IDiagnosticLogger _logger; + internal float _progress = 0.0f; + internal string _progressText = ""; + internal bool _done = false; + internal bool _cancelled = false; + internal Exception? _exception = null; + internal Action? _uiAction = null; + private const int StepCount = 5; + + public WizardLoader(IDiagnosticLogger logger) { - internal WizardCancelled() : base() { } - internal WizardCancelled(string message) : base(message) { } - internal WizardCancelled(string message, Exception innerException) : base(message, innerException) { } + _logger = logger; } - internal class WizardLoader + private void Progress(string status, int step) => Progress(status, (float)step / StepCount); + private void Done(string status) => Progress(status, 1.0f); + + private void Progress(string status, float current) { - private IDiagnosticLogger _logger; - internal float _progress = 0.0f; - internal string _progressText = ""; - internal bool _done = false; - internal bool _cancelled = false; - internal Exception? _exception = null; - internal Action? _uiAction = null; - private const int StepCount = 5; - - public WizardLoader(IDiagnosticLogger logger) + if (_cancelled) { - _logger = logger; + throw new WizardCancelled(); } - private void Progress(string status, int step) => Progress(status, (float)step / StepCount); - private void Done(string status) => Progress(status, 1.0f); - - private void Progress(string status, float current) - { - if (_cancelled) - { - throw new WizardCancelled(); - } - - _logger.LogDebug("Wizard: {0,3} % - {1}", (int)Math.Floor(100 * current), status); - _progressText = status; - _progress = current; - } + _logger.LogDebug("Wizard: {0,3} % - {1}", (int)Math.Floor(100 * current), status); + _progressText = status; + _progress = current; + } - internal async Task Load() + internal async Task Load() + { + WizardStep2Response? response = null; + try { - WizardStep2Response? response = null; - try - { - Progress("Started", 1); + Progress("Started", 1); - Progress("Connecting to sentry.io settings wizard...", 2); - var http = new HttpClient(); - var resp = await http.GetAsync("https://sentry.io/api/0/wizard/").ConfigureAwait(false); - var wizardHashResponse = await DeserializeJson(resp); + Progress("Connecting to sentry.io settings wizard...", 2); + var http = new HttpClient(); + var resp = await http.GetAsync("https://sentry.io/api/0/wizard/").ConfigureAwait(false); + var wizardHashResponse = await DeserializeJson(resp); - Progress("Opening sentry.io in the default browser...", 3); - await RunOnUiThread(() => Wizard.OpenUrl($"https://sentry.io/account/settings/wizard/{wizardHashResponse.hash}/")); + Progress("Opening sentry.io in the default browser...", 3); + await RunOnUiThread(() => Wizard.OpenUrl($"https://sentry.io/account/settings/wizard/{wizardHashResponse.hash}/")); - // Poll https://sentry.io/api/0/wizard/hash/ - var pollingUrl = $"https://sentry.io/api/0/wizard/{wizardHashResponse.hash}/"; + // Poll https://sentry.io/api/0/wizard/hash/ + var pollingUrl = $"https://sentry.io/api/0/wizard/{wizardHashResponse.hash}/"; - Progress("Waiting for the the response from the browser session...", 4); + Progress("Waiting for the the response from the browser session...", 4); - while (!_cancelled) + while (!_cancelled) + { + try { - try - { - resp = await http.GetAsync(pollingUrl).ConfigureAwait(false); - if (resp.StatusCode != HttpStatusCode.BadRequest) // not ready yet - { - response = await DeserializeJson(resp).ConfigureAwait(false); - break; - } - } - catch (Exception e) + resp = await http.GetAsync(pollingUrl).ConfigureAwait(false); + if (resp.StatusCode != HttpStatusCode.BadRequest) // not ready yet { - _logger.Log(SentryLevel.Warning, "Wizard polling error", e); + response = await DeserializeJson(resp).ConfigureAwait(false); + break; } - await Task.Delay(1000).ConfigureAwait(false); } - - await http.DeleteAsync(pollingUrl); - http.Dispose(); - Done("Finished"); + catch (Exception e) + { + _logger.Log(SentryLevel.Warning, "Wizard polling error", e); + } + await Task.Delay(1000).ConfigureAwait(false); } - catch (WizardCancelled) + + await http.DeleteAsync(pollingUrl); + http.Dispose(); + Done("Finished"); + } + catch (WizardCancelled) + { + _logger.Log(SentryLevel.Info, "Wizard cancelled"); + } + catch (Exception e) + { + _logger.Log(SentryLevel.Warning, "Wizard failed", e); + _exception = e; + Done("Failed"); + } + finally + { + _done = true; + } + return response; + } + + private async Task DeserializeJson(HttpResponseMessage response) + { + var content = await response.EnsureSuccessStatusCode().Content.ReadAsByteArrayAsync().ConfigureAwait(false); + return DeserializeJson(System.Text.Encoding.UTF8.GetString(content)); + } + + internal T DeserializeJson(string json) => JsonUtility.FromJson(json); + + private Task RunOnUiThread(Action callback) + { + var tcs = new TaskCompletionSource(); + _uiAction = () => + { + try { - _logger.Log(SentryLevel.Info, "Wizard cancelled"); + callback.Invoke(); + tcs.TrySetResult(true); } catch (Exception e) { - _logger.Log(SentryLevel.Warning, "Wizard failed", e); - _exception = e; - Done("Failed"); + tcs.TrySetException(e); } finally { - _done = true; + _uiAction = null; } - return response; - } - - private async Task DeserializeJson(HttpResponseMessage response) - { - var content = await response.EnsureSuccessStatusCode().Content.ReadAsByteArrayAsync().ConfigureAwait(false); - return DeserializeJson(System.Text.Encoding.UTF8.GetString(content)); - } - - internal T DeserializeJson(string json) => JsonUtility.FromJson(json); - - private Task RunOnUiThread(Action callback) - { - var tcs = new TaskCompletionSource(); - _uiAction = () => - { - try - { - callback.Invoke(); - tcs.TrySetResult(true); - } - catch (Exception e) - { - tcs.TrySetException(e); - } - finally - { - _uiAction = null; - } - }; - return tcs.Task; - } + }; + return tcs.Task; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs index 912409fb5..f02bb5dba 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs @@ -1,64 +1,54 @@ using System; using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Sentry.Extensibility; -using UnityEditor; -using UnityEngine; -namespace Sentry.Unity.Editor.WizardApi -{ - [Serializable] - internal class WizardStep1Response - { - public string? hash; - } +namespace Sentry.Unity.Editor.WizardApi; - [Serializable] - internal class WizardStep2Response - { - public ApiKeys? apiKeys; - public List projects = new List(0); - } +[Serializable] +internal class WizardStep1Response +{ + public string? hash; +} - [Serializable] - internal class ApiKeys - { - public string? token; - } +[Serializable] +internal class WizardStep2Response +{ + public ApiKeys? apiKeys; + public List projects = new List(0); +} - [Serializable] - internal class Project - { - public Organization? organization; - public string? slug; - public string? name; - public string? platform; - public List? keys; +[Serializable] +internal class ApiKeys +{ + public string? token; +} - public bool IsUnity => string.Equals(platform, "unity", StringComparison.InvariantCultureIgnoreCase); - } +[Serializable] +internal class Project +{ + public Organization? organization; + public string? slug; + public string? name; + public string? platform; + public List? keys; - [Serializable] - internal class Key - { - public Dsn? dsn; - } + public bool IsUnity => string.Equals(platform, "unity", StringComparison.InvariantCultureIgnoreCase); +} - [Serializable] - internal class Dsn - { - public string? @public; - } +[Serializable] +internal class Key +{ + public Dsn? dsn; +} - [Serializable] - internal class Organization - { - public string? name; - public string? slug; - } +[Serializable] +internal class Dsn +{ + public string? @public; } + +[Serializable] +internal class Organization +{ + public string? name; + public string? slug; +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index 9c4ecad97..bce4849a6 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -3,62 +3,60 @@ using UnityEditor; using UnityEditor.Build; using UnityEditor.Build.Reporting; -using UnityEngine; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport { - internal class Il2CppBuildPreProcess : IPreprocessBuildWithReport + internal const string SourceMappingArgument = "--emit-source-mapping"; + + public int callbackOrder => 0; + + public void OnPreprocessBuild(BuildReport report) { - internal const string SourceMappingArgument = "--emit-source-mapping"; + if (PlayerSettings.GetScriptingBackend(report.summary.platformGroup) != ScriptingImplementation.IL2CPP) + { + return; + } - public int callbackOrder => 0; - public void OnPreprocessBuild(BuildReport report) + var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); + + if (options is null) { - if (PlayerSettings.GetScriptingBackend(report.summary.platformGroup) != ScriptingImplementation.IL2CPP) - { - return; - } + return; + } + SetAdditionalIl2CppArguments(options, + PlayerSettings.GetAdditionalIl2CppArgs, + PlayerSettings.SetAdditionalIl2CppArgs); + } - var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); + internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Func getArguments, Action setArguments) + { + if (options.Il2CppLineNumberSupportEnabled) + { + options.DiagnosticLogger?.LogDebug("IL2CPP line number support enabled - Adding additional IL2CPP arguments."); - if (options is null) + var arguments = getArguments.Invoke(); + if (arguments.Contains(SourceMappingArgument)) { + options.DiagnosticLogger?.LogDebug("Additional argument '{0}' already present.", SourceMappingArgument); return; } - SetAdditionalIl2CppArguments(options, - PlayerSettings.GetAdditionalIl2CppArgs, - PlayerSettings.SetAdditionalIl2CppArgs); + setArguments.Invoke(getArguments.Invoke() + $" {SourceMappingArgument}"); } - - internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Func getArguments, Action setArguments) + else { - if (options.Il2CppLineNumberSupportEnabled) - { - options.DiagnosticLogger?.LogDebug("IL2CPP line number support enabled - Adding additional IL2CPP arguments."); - - var arguments = getArguments.Invoke(); - if (arguments.Contains(SourceMappingArgument)) - { - options.DiagnosticLogger?.LogDebug("Additional argument '{0}' already present.", SourceMappingArgument); - return; - } - - setArguments.Invoke(getArguments.Invoke() + $" {SourceMappingArgument}"); - } - else + var arguments = getArguments.Invoke(); + if (arguments.Contains(SourceMappingArgument)) { - var arguments = getArguments.Invoke(); - if (arguments.Contains(SourceMappingArgument)) - { - options.DiagnosticLogger?.LogDebug("IL2CPP line number support disabled - Removing additional IL2CPP arguments."); + options.DiagnosticLogger?.LogDebug("IL2CPP line number support disabled - Removing additional IL2CPP arguments."); - arguments = arguments.Replace(SourceMappingArgument, ""); - setArguments.Invoke(arguments); - } + arguments = arguments.Replace(SourceMappingArgument, ""); + setArguments.Invoke(arguments); } } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs index 36ef069dd..179725d5b 100644 --- a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs @@ -10,265 +10,264 @@ using UnityEditor.Callbacks; using System.Diagnostics; -namespace Sentry.Unity.Editor.Native +namespace Sentry.Unity.Editor.Native; + +public static class BuildPostProcess { - public static class BuildPostProcess + [PostProcessBuild(1)] + public static void OnPostProcessBuild(BuildTarget target, string executablePath) { - [PostProcessBuild(1)] - public static void OnPostProcessBuild(BuildTarget target, string executablePath) + var targetGroup = BuildPipeline.GetBuildTargetGroup(target); + if (targetGroup is not BuildTargetGroup.Standalone) { - var targetGroup = BuildPipeline.GetBuildTargetGroup(target); - if (targetGroup is not BuildTargetGroup.Standalone) + return; + } + + var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); + var logger = options?.DiagnosticLogger ?? new UnityLogger(options ?? new SentryUnityOptions()); + var isMono = PlayerSettings.GetScriptingBackend(targetGroup) == ScriptingImplementation.Mono2x; + + try + { + if (options is null) { + logger.LogWarning("Native support disabled because Sentry has not been configured. " + + "You can do that through the editor: {0}", SentryWindow.EditorMenuPath); return; } - var (options, cliOptions) = SentryScriptableObject.ConfiguredBuildTimeOptions(); - var logger = options?.DiagnosticLogger ?? new UnityLogger(options ?? new SentryUnityOptions()); - var isMono = PlayerSettings.GetScriptingBackend(targetGroup) == ScriptingImplementation.Mono2x; + if (!options.IsValid()) + { + logger.LogDebug("Native support disabled."); + return; + } - try + if (!IsEnabledForPlatform(target, options)) { - if (options is null) - { - logger.LogWarning("Native support disabled because Sentry has not been configured. " + - "You can do that through the editor: {0}", SentryWindow.EditorMenuPath); - return; - } + logger.LogDebug("Native support for the current platform is disabled in the configuration."); + return; + } - if (!options.IsValid()) - { - logger.LogDebug("Native support disabled."); - return; - } + logger.LogDebug("Adding native support."); - if (!IsEnabledForPlatform(target, options)) - { - logger.LogDebug("Native support for the current platform is disabled in the configuration."); - return; - } + var buildOutputDir = Path.GetDirectoryName(executablePath); + var executableName = Path.GetFileName(executablePath); + AddCrashHandler(logger, target, buildOutputDir, executableName); + UploadDebugSymbols(logger, target, buildOutputDir, executableName, options, cliOptions, isMono); + } + catch (Exception e) + { + logger.LogError(e, "Failed to add the Sentry native integration to the built application"); + throw new BuildFailedException("Sentry Native BuildPostProcess failed"); + } + } - logger.LogDebug("Adding native support."); + private static bool IsEnabledForPlatform(BuildTarget target, SentryUnityOptions options) => target switch + { + BuildTarget.StandaloneWindows64 => options.WindowsNativeSupportEnabled, + BuildTarget.StandaloneOSX => options.MacosNativeSupportEnabled, + BuildTarget.StandaloneLinux64 => options.LinuxNativeSupportEnabled, + _ => false, + }; - var buildOutputDir = Path.GetDirectoryName(executablePath); - var executableName = Path.GetFileName(executablePath); - AddCrashHandler(logger, target, buildOutputDir, executableName); - UploadDebugSymbols(logger, target, buildOutputDir, executableName, options, cliOptions, isMono); - } - catch (Exception e) - { - logger.LogError(e, "Failed to add the Sentry native integration to the built application"); - throw new BuildFailedException("Sentry Native BuildPostProcess failed"); - } + private static void AddCrashHandler(IDiagnosticLogger logger, BuildTarget target, string buildOutputDir, string executableName) + { + string crashpadPath; + if (target is BuildTarget.StandaloneWindows64) + { + crashpadPath = Path.Combine("Windows", "Sentry", "crashpad_handler.exe"); } - - private static bool IsEnabledForPlatform(BuildTarget target, SentryUnityOptions options) => target switch + else if (target is BuildTarget.StandaloneLinux64) { - BuildTarget.StandaloneWindows64 => options.WindowsNativeSupportEnabled, - BuildTarget.StandaloneOSX => options.MacosNativeSupportEnabled, - BuildTarget.StandaloneLinux64 => options.LinuxNativeSupportEnabled, - _ => false, - }; + // No standalone crash handler for Linux - uses built-in breakpad. + return; + } + else if (target is BuildTarget.StandaloneOSX) + { + // No standalone crash handler for macOS - uses built-in handler in sentry-cocoa. + return; + } + else + { + throw new ArgumentException($"Unsupported build target: {target}"); + } + + crashpadPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", crashpadPath)); + var targetPath = Path.Combine(buildOutputDir, Path.GetFileName(crashpadPath)); + logger.LogInfo("Copying the native crash handler '{0}' to {1}", Path.GetFileName(crashpadPath), targetPath); + File.Copy(crashpadPath, targetPath, true); + } - private static void AddCrashHandler(IDiagnosticLogger logger, BuildTarget target, string buildOutputDir, string executableName) + private static void UploadDebugSymbols(IDiagnosticLogger logger, BuildTarget target, string buildOutputDir, string executableName, SentryUnityOptions options, SentryCliOptions? cliOptions, bool isMono) + { + var projectDir = Directory.GetParent(Application.dataPath).FullName; + if (cliOptions?.IsValid(logger, EditorUserBuildSettings.development) is not true) { - string crashpadPath; - if (target is BuildTarget.StandaloneWindows64) + if (options.Il2CppLineNumberSupportEnabled) { - crashpadPath = Path.Combine("Windows", "Sentry", "crashpad_handler.exe"); + logger.LogWarning("The IL2CPP line number support requires the debug symbol upload to be enabled."); } - else if (target is BuildTarget.StandaloneLinux64) - { - // No standalone crash handler for Linux - uses built-in breakpad. - return; - } - else if (target is BuildTarget.StandaloneOSX) + + return; + } + + logger.LogInfo("Uploading debugging information using sentry-cli in {0}", buildOutputDir); + + var paths = ""; + Func addPath = (string name) => + { + var fullPath = Path.Combine(buildOutputDir, name); + if (fullPath.Contains("*") || Directory.Exists(fullPath) || File.Exists(fullPath)) { - // No standalone crash handler for macOS - uses built-in handler in sentry-cocoa. - return; + paths += $" \"{name}\""; + logger.LogDebug($"Adding '{name}' to the debug-info upload"); + return true; } else { - throw new ArgumentException($"Unsupported build target: {target}"); + logger.LogWarning($"Couldn't find '{name}' - debug symbol upload will be incomplete"); + return false; } + }; - crashpadPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", crashpadPath)); - var targetPath = Path.Combine(buildOutputDir, Path.GetFileName(crashpadPath)); - logger.LogInfo("Copying the native crash handler '{0}' to {1}", Path.GetFileName(crashpadPath), targetPath); - File.Copy(crashpadPath, targetPath, true); - } - - private static void UploadDebugSymbols(IDiagnosticLogger logger, BuildTarget target, string buildOutputDir, string executableName, SentryUnityOptions options, SentryCliOptions? cliOptions, bool isMono) + Action addFilesMatching = (string directory, string[] includePatterns) => { - var projectDir = Directory.GetParent(Application.dataPath).FullName; - if (cliOptions?.IsValid(logger, EditorUserBuildSettings.development) is not true) + Matcher matcher = new(); + matcher.AddIncludePatterns(includePatterns); + foreach (string file in matcher.GetResultsInFullPath(directory)) { - if (options.Il2CppLineNumberSupportEnabled) - { - logger.LogWarning("The IL2CPP line number support requires the debug symbol upload to be enabled."); - } - - return; + addPath(file); } + }; - logger.LogInfo("Uploading debugging information using sentry-cli in {0}", buildOutputDir); + addPath(executableName); - var paths = ""; - Func addPath = (string name) => - { - var fullPath = Path.Combine(buildOutputDir, name); - if (fullPath.Contains("*") || Directory.Exists(fullPath) || File.Exists(fullPath)) + switch (target) + { + case BuildTarget.StandaloneWindows64: + addPath("UnityPlayer.dll"); + addPath(Path.GetFileNameWithoutExtension(executableName) + "_Data/Plugins/x86_64/sentry.dll"); + addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Windows/Sentry/sentry.pdb")); + + if (isMono) { - paths += $" \"{name}\""; - logger.LogDebug($"Adding '{name}' to the debug-info upload"); - return true; + addPath("MonoBleedingEdge/EmbedRuntime"); + addFilesMatching(buildOutputDir, new[] { "*.pdb" }); + + // Unity stores the .pdb files in './Library/ScriptAssemblies/' and starting with 2020 in + // './Temp/ManagedSymbols/'. We want the one in 'Temp/ManagedSymbols/' specifically. + var managedSymbolsDirectory = $"{projectDir}/Temp/ManagedSymbols"; + if (Directory.Exists(managedSymbolsDirectory)) + { + addFilesMatching(managedSymbolsDirectory, new[] { "*.pdb" }); + } } - else + else // IL2CPP { - logger.LogWarning($"Couldn't find '{name}' - debug symbol upload will be incomplete"); - return false; + addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); + addPath("GameAssembly.dll"); } - }; + break; + case BuildTarget.StandaloneLinux64: + addPath("GameAssembly.so"); + addPath("UnityPlayer.so"); + addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Linux/Sentry/libsentry.dbg.so")); - Action addFilesMatching = (string directory, string[] includePatterns) => - { - Matcher matcher = new(); - matcher.AddIncludePatterns(includePatterns); - foreach (string file in matcher.GetResultsInFullPath(directory)) + if (isMono) { - addPath(file); - } - }; + addPath(Path.GetFileNameWithoutExtension(executableName) + "_Data/MonoBleedingEdge/x86_64"); + addFilesMatching(buildOutputDir, new[] { "*.debug" }); - addPath(executableName); - - switch (target) - { - case BuildTarget.StandaloneWindows64: - addPath("UnityPlayer.dll"); - addPath(Path.GetFileNameWithoutExtension(executableName) + "_Data/Plugins/x86_64/sentry.dll"); - addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Windows/Sentry/sentry.pdb")); - - if (isMono) + var managedSymbolsDirectory = $"{projectDir}/Temp/ManagedSymbols"; + if (Directory.Exists(managedSymbolsDirectory)) { - addPath("MonoBleedingEdge/EmbedRuntime"); - addFilesMatching(buildOutputDir, new[] { "*.pdb" }); - - // Unity stores the .pdb files in './Library/ScriptAssemblies/' and starting with 2020 in - // './Temp/ManagedSymbols/'. We want the one in 'Temp/ManagedSymbols/' specifically. - var managedSymbolsDirectory = $"{projectDir}/Temp/ManagedSymbols"; - if (Directory.Exists(managedSymbolsDirectory)) - { - addFilesMatching(managedSymbolsDirectory, new[] { "*.pdb" }); - } + addFilesMatching(managedSymbolsDirectory, new[] { "*.pdb" }); } - else // IL2CPP - { - addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); - addPath("GameAssembly.dll"); - } - break; - case BuildTarget.StandaloneLinux64: - addPath("GameAssembly.so"); - addPath("UnityPlayer.so"); - addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/Linux/Sentry/libsentry.dbg.so")); - - if (isMono) - { - addPath(Path.GetFileNameWithoutExtension(executableName) + "_Data/MonoBleedingEdge/x86_64"); - addFilesMatching(buildOutputDir, new[] { "*.debug" }); + } + else // IL2CPP + { + addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); + } + break; + case BuildTarget.StandaloneOSX: + addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/macOS/Sentry/Sentry.dylib.dSYM")); - var managedSymbolsDirectory = $"{projectDir}/Temp/ManagedSymbols"; - if (Directory.Exists(managedSymbolsDirectory)) - { - addFilesMatching(managedSymbolsDirectory, new[] { "*.pdb" }); - } - } - else // IL2CPP - { - addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); - } - break; - case BuildTarget.StandaloneOSX: - addPath(Path.GetFullPath($"Packages/{SentryPackageInfo.GetName()}/Plugins/macOS/Sentry/Sentry.dylib.dSYM")); + if (isMono) + { } + else // IL2CPP + { + addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); + } + break; + default: + logger.LogError($"Symbol upload for '{target}' is currently not supported."); + break; + } - if (isMono) - { } - else // IL2CPP - { - addPath(Path.GetFileNameWithoutExtension(executableName) + "_BackUpThisFolder_ButDontShipItWithYourGame"); - } - break; - default: - logger.LogError($"Symbol upload for '{target}' is currently not supported."); - break; - } + var cliArgs = "debug-files upload "; + if (!isMono) + { + cliArgs += "--il2cpp-mapping "; + } + if (cliOptions.UploadSources) + { + cliArgs += "--include-sources "; + } + cliArgs += paths; - var cliArgs = "debug-files upload "; - if (!isMono) - { - cliArgs += "--il2cpp-mapping "; - } - if (cliOptions.UploadSources) + // Configure the process using the StartInfo properties. + var process = new Process + { + StartInfo = new ProcessStartInfo { - cliArgs += "--include-sources "; + FileName = SentryCli.SetupSentryCli(), + WorkingDirectory = buildOutputDir, + Arguments = cliArgs, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true } - cliArgs += paths; + }; - // Configure the process using the StartInfo properties. - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = SentryCli.SetupSentryCli(), - WorkingDirectory = buildOutputDir, - Arguments = cliArgs, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - } - }; + var propertiesFile = SentryCli.CreateSentryProperties(buildOutputDir, cliOptions, options); + try + { + process.StartInfo.EnvironmentVariables["SENTRY_PROPERTIES"] = propertiesFile; - var propertiesFile = SentryCli.CreateSentryProperties(buildOutputDir, cliOptions, options); - try + DataReceivedEventHandler logForwarder = (object sender, DataReceivedEventArgs e) => { - process.StartInfo.EnvironmentVariables["SENTRY_PROPERTIES"] = propertiesFile; - - DataReceivedEventHandler logForwarder = (object sender, DataReceivedEventArgs e) => + if (!string.IsNullOrEmpty(e.Data)) { - if (!string.IsNullOrEmpty(e.Data)) + var msg = e.Data.Trim(); + var msgLower = msg.ToLowerInvariant(); + var level = SentryLevel.Info; + if (msgLower.StartsWith("error")) { - var msg = e.Data.Trim(); - var msgLower = msg.ToLowerInvariant(); - var level = SentryLevel.Info; - if (msgLower.StartsWith("error")) - { - level = SentryLevel.Error; - } - else if (msgLower.StartsWith("warn")) - { - level = SentryLevel.Warning; - } - - // Remove the level and timestamp from the beginning of the message. - // INFO 2022-06-20 15:10:03.613794800 +02:00 - msg = Regex.Replace(msg, "^[a-zA-Z]+ +[0-9\\-]{10} [0-9:]{8}\\.[0-9]+ \\+[0-9:]{5} +", ""); - logger.Log(level, "sentry-cli: {0}", null, msg); + level = SentryLevel.Error; + } + else if (msgLower.StartsWith("warn")) + { + level = SentryLevel.Warning; } - }; - process.OutputDataReceived += logForwarder; - process.ErrorDataReceived += logForwarder; - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - process.WaitForExit(); - } - finally - { - File.Delete(propertiesFile); - } + // Remove the level and timestamp from the beginning of the message. + // INFO 2022-06-20 15:10:03.613794800 +02:00 + msg = Regex.Replace(msg, "^[a-zA-Z]+ +[0-9\\-]{10} [0-9:]{8}\\.[0-9]+ \\+[0-9:]{5} +", ""); + logger.Log(level, "sentry-cli: {0}", null, msg); + } + }; + + process.OutputDataReceived += logForwarder; + process.ErrorDataReceived += logForwarder; + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + process.WaitForExit(); + } + finally + { + File.Delete(propertiesFile); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs b/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs index 602d3b1a3..ce63207ce 100644 --- a/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs +++ b/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs @@ -1,112 +1,111 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +[CustomEditor(typeof(ScriptableSentryUnityOptions))] +public class ScriptableSentryUnityOptionsEditor : UnityEditor.Editor { - [CustomEditor(typeof(ScriptableSentryUnityOptions))] - public class ScriptableSentryUnityOptionsEditor : UnityEditor.Editor + public override void OnInspectorGUI() { - public override void OnInspectorGUI() + if (target is not ScriptableSentryUnityOptions options) { - if (target is not ScriptableSentryUnityOptions options) - { - return; - } - - EditorGUI.BeginDisabledGroup(true); - - EditorGUILayout.LabelField("Core", EditorStyles.boldLabel); - EditorGUILayout.Toggle("Enable Sentry SDK", options.Enabled); - EditorGUILayout.TextField("DSN", options.Dsn); - EditorGUILayout.Toggle("Capture In Editor", options.CaptureInEditor); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.FloatField("Traces Sample Rate", (float)options.TracesSampleRate); - EditorGUILayout.Toggle("Auto Startup Traces", options.AutoStartupTraces); - EditorGUILayout.Toggle("Auto Scene Load Traces", options.AutoSceneLoadTraces); - EditorGUILayout.Toggle("Attach Stacktrace", options.AutoAwakeTraces); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.LabelField("Enrichment", EditorStyles.boldLabel); - EditorGUILayout.TextField("Release Override", options.ReleaseOverride); - EditorGUILayout.TextField("Environment Override", options.EnvironmentOverride); - EditorGUILayout.Toggle("Attach Stacktrace", options.AttachStacktrace); - EditorGUILayout.Toggle("Attach Screenshot", options.AttachScreenshot); - EditorGUILayout.Toggle("Attach Hierarchy", options.AttachViewHierarchy); - EditorGUILayout.IntField("Max Breadcrumbs", options.MaxBreadcrumbs); - EditorGUILayout.EnumPopup("Report Assemblies Mode", options.ReportAssembliesMode); - EditorGUILayout.Toggle("Send Default Pii", options.SendDefaultPii); - EditorGUILayout.Toggle("Auto Set UserName", options.IsEnvironmentUser); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.LabelField("Transport", EditorStyles.boldLabel); - EditorGUILayout.Toggle("Enable Offline Caching", options.EnableOfflineCaching); - EditorGUILayout.IntField("Max Cache Items", options.MaxCacheItems); - EditorGUILayout.IntField("Init Flush Timeout [ms]", options.InitCacheFlushTimeout); - EditorGUILayout.FloatField("Event Sample Rate", options.SampleRate); - EditorGUILayout.IntField("Shut Down Timeout [ms]", options.ShutdownTimeout); - EditorGUILayout.IntField("Max Queue Items", options.MaxQueueItems); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.LabelField("Session", EditorStyles.boldLabel); - EditorGUILayout.Toggle("Auto Session Tracking", options.AutoSessionTracking); - EditorGUILayout.IntField("Session Timeout [ms]", options.AutoSessionTrackingInterval); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.LabelField("Application Not Responding", EditorStyles.boldLabel); - EditorGUILayout.Toggle("Enable ANR Detection", options.AnrDetectionEnabled); - EditorGUILayout.IntField("ANR Timeout [ms]", options.AnrTimeout); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.Toggle("iOS Native Support", options.IosNativeSupportEnabled); - EditorGUILayout.Toggle("Android Native Support", options.AndroidNativeSupportEnabled); - EditorGUI.indentLevel++; - EditorGUILayout.Toggle("NDK Integration", options.NdkIntegrationEnabled); - EditorGUILayout.Toggle("NDK Scope Sync", options.NdkScopeSyncEnabled); - EditorGUI.indentLevel--; - EditorGUILayout.Toggle("Windows Native Support", options.WindowsNativeSupportEnabled); - EditorGUILayout.Toggle("macOS Native Support", options.MacosNativeSupportEnabled); - EditorGUILayout.Toggle("Linux Native Support", options.LinuxNativeSupportEnabled); - EditorGUILayout.Toggle("IL2CPP line numbers", options.Il2CppLineNumberSupportEnabled); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.LabelField("Options Configuration", EditorStyles.boldLabel); - EditorGUILayout.ObjectField("Runtime Configuration", options.RuntimeOptionsConfiguration, - typeof(SentryRuntimeOptionsConfiguration), false); - EditorGUILayout.ObjectField("Build Time Configuration", options.BuildTimeOptionsConfiguration, - typeof(SentryBuildTimeOptionsConfiguration), false); - - EditorGUILayout.Space(); - EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); - EditorGUILayout.Space(); - - EditorGUILayout.LabelField("Debug", EditorStyles.boldLabel); - EditorGUILayout.Toggle("Enable Debug Output", options.Debug); - EditorGUILayout.Toggle("Only In Editor", options.DebugOnlyInEditor); - EditorGUILayout.EnumPopup("Verbosity level", options.DiagnosticLevel); - - EditorGUI.EndDisabledGroup(); + return; } + + EditorGUI.BeginDisabledGroup(true); + + EditorGUILayout.LabelField("Core", EditorStyles.boldLabel); + EditorGUILayout.Toggle("Enable Sentry SDK", options.Enabled); + EditorGUILayout.TextField("DSN", options.Dsn); + EditorGUILayout.Toggle("Capture In Editor", options.CaptureInEditor); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.FloatField("Traces Sample Rate", (float)options.TracesSampleRate); + EditorGUILayout.Toggle("Auto Startup Traces", options.AutoStartupTraces); + EditorGUILayout.Toggle("Auto Scene Load Traces", options.AutoSceneLoadTraces); + EditorGUILayout.Toggle("Attach Stacktrace", options.AutoAwakeTraces); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Enrichment", EditorStyles.boldLabel); + EditorGUILayout.TextField("Release Override", options.ReleaseOverride); + EditorGUILayout.TextField("Environment Override", options.EnvironmentOverride); + EditorGUILayout.Toggle("Attach Stacktrace", options.AttachStacktrace); + EditorGUILayout.Toggle("Attach Screenshot", options.AttachScreenshot); + EditorGUILayout.Toggle("Attach Hierarchy", options.AttachViewHierarchy); + EditorGUILayout.IntField("Max Breadcrumbs", options.MaxBreadcrumbs); + EditorGUILayout.EnumPopup("Report Assemblies Mode", options.ReportAssembliesMode); + EditorGUILayout.Toggle("Send Default Pii", options.SendDefaultPii); + EditorGUILayout.Toggle("Auto Set UserName", options.IsEnvironmentUser); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Transport", EditorStyles.boldLabel); + EditorGUILayout.Toggle("Enable Offline Caching", options.EnableOfflineCaching); + EditorGUILayout.IntField("Max Cache Items", options.MaxCacheItems); + EditorGUILayout.IntField("Init Flush Timeout [ms]", options.InitCacheFlushTimeout); + EditorGUILayout.FloatField("Event Sample Rate", options.SampleRate); + EditorGUILayout.IntField("Shut Down Timeout [ms]", options.ShutdownTimeout); + EditorGUILayout.IntField("Max Queue Items", options.MaxQueueItems); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Session", EditorStyles.boldLabel); + EditorGUILayout.Toggle("Auto Session Tracking", options.AutoSessionTracking); + EditorGUILayout.IntField("Session Timeout [ms]", options.AutoSessionTrackingInterval); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Application Not Responding", EditorStyles.boldLabel); + EditorGUILayout.Toggle("Enable ANR Detection", options.AnrDetectionEnabled); + EditorGUILayout.IntField("ANR Timeout [ms]", options.AnrTimeout); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.Toggle("iOS Native Support", options.IosNativeSupportEnabled); + EditorGUILayout.Toggle("Android Native Support", options.AndroidNativeSupportEnabled); + EditorGUI.indentLevel++; + EditorGUILayout.Toggle("NDK Integration", options.NdkIntegrationEnabled); + EditorGUILayout.Toggle("NDK Scope Sync", options.NdkScopeSyncEnabled); + EditorGUI.indentLevel--; + EditorGUILayout.Toggle("Windows Native Support", options.WindowsNativeSupportEnabled); + EditorGUILayout.Toggle("macOS Native Support", options.MacosNativeSupportEnabled); + EditorGUILayout.Toggle("Linux Native Support", options.LinuxNativeSupportEnabled); + EditorGUILayout.Toggle("IL2CPP line numbers", options.Il2CppLineNumberSupportEnabled); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Options Configuration", EditorStyles.boldLabel); + EditorGUILayout.ObjectField("Runtime Configuration", options.RuntimeOptionsConfiguration, + typeof(SentryRuntimeOptionsConfiguration), false); + EditorGUILayout.ObjectField("Build Time Configuration", options.BuildTimeOptionsConfiguration, + typeof(SentryBuildTimeOptionsConfiguration), false); + + EditorGUILayout.Space(); + EditorGUI.DrawRect(EditorGUILayout.GetControlRect(false, 1), Color.gray); + EditorGUILayout.Space(); + + EditorGUILayout.LabelField("Debug", EditorStyles.boldLabel); + EditorGUILayout.Toggle("Enable Debug Output", options.Debug); + EditorGUILayout.Toggle("Only In Editor", options.DebugOnlyInEditor); + EditorGUILayout.EnumPopup("Verbosity level", options.DiagnosticLevel); + + EditorGUI.EndDisabledGroup(); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/SentryCli.cs b/src/Sentry.Unity.Editor/SentryCli.cs index 8dceafa96..546c2425a 100644 --- a/src/Sentry.Unity.Editor/SentryCli.cs +++ b/src/Sentry.Unity.Editor/SentryCli.cs @@ -1,129 +1,127 @@ using System; using System.IO; using System.Runtime.InteropServices; -using Sentry.Extensibility; using Sentry.Unity.Integrations; using UnityEngine; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal static class SentryCli { - internal static class SentryCli - { - internal const string SentryCliWindows = "sentry-cli-Windows-x86_64.exe"; - internal const string SentryCliMacOS = "sentry-cli-Darwin-universal"; - internal const string SentryCliLinux = "sentry-cli-Linux-x86_64"; + internal const string SentryCliWindows = "sentry-cli-Windows-x86_64.exe"; + internal const string SentryCliMacOS = "sentry-cli-Darwin-universal"; + internal const string SentryCliLinux = "sentry-cli-Linux-x86_64"; - [DllImport("libc", SetLastError = true)] - private static extern int chmod(string pathname, int mode); + [DllImport("libc", SetLastError = true)] + private static extern int chmod(string pathname, int mode); - public static string CreateSentryProperties(string propertiesPath, SentryCliOptions cliOptions, SentryOptions options) + public static string CreateSentryProperties(string propertiesPath, SentryCliOptions cliOptions, SentryOptions options) + { + var propertiesFile = Path.Combine(propertiesPath, "sentry.properties"); + using var properties = File.CreateText(propertiesFile); + + if (UrlOverride(options.Dsn, cliOptions.UrlOverride) is { } urlOverride) { - var propertiesFile = Path.Combine(propertiesPath, "sentry.properties"); - using var properties = File.CreateText(propertiesFile); + properties.WriteLine($"defaults.url={urlOverride}"); + } - if (UrlOverride(options.Dsn, cliOptions.UrlOverride) is { } urlOverride) - { - properties.WriteLine($"defaults.url={urlOverride}"); - } + properties.WriteLine($"defaults.org={cliOptions.Organization}"); + properties.WriteLine($"defaults.project={cliOptions.Project}"); + properties.WriteLine($"auth.token={cliOptions.Auth}"); + properties.WriteLine("dif.max_item_size=10485760"); // 10 MiB + properties.WriteLine($"log.level={options.DiagnosticLevel.ToCliLogLevel()}"); - properties.WriteLine($"defaults.org={cliOptions.Organization}"); - properties.WriteLine($"defaults.project={cliOptions.Project}"); - properties.WriteLine($"auth.token={cliOptions.Auth}"); - properties.WriteLine("dif.max_item_size=10485760"); // 10 MiB - properties.WriteLine($"log.level={options.DiagnosticLevel.ToCliLogLevel()}"); + return propertiesFile; + } - return propertiesFile; - } + public static string SetupSentryCli(string? outDirectory = null, RuntimePlatform? actualBuildHost = null) + { + var executableName = GetSentryCliPlatformExecutable(actualBuildHost); + var sentryCliPath = GetSentryCliPath(executableName); - public static string SetupSentryCli(string? outDirectory = null, RuntimePlatform? actualBuildHost = null) + if (outDirectory is not null) { - var executableName = GetSentryCliPlatformExecutable(actualBuildHost); - var sentryCliPath = GetSentryCliPath(executableName); - - if (outDirectory is not null) + if (!Directory.Exists(outDirectory)) { - if (!Directory.Exists(outDirectory)) - { - throw new DirectoryNotFoundException($"Output project directory not found: {outDirectory}"); - } - - var outCliPath = Path.Combine(outDirectory, executableName); - File.Copy(sentryCliPath, outCliPath, true); - sentryCliPath = outCliPath; + throw new DirectoryNotFoundException($"Output project directory not found: {outDirectory}"); } - SetExecutePermission(sentryCliPath); - return sentryCliPath; + var outCliPath = Path.Combine(outDirectory, executableName); + File.Copy(sentryCliPath, outCliPath, true); + sentryCliPath = outCliPath; } - internal static string GetSentryCliPlatformExecutable(RuntimePlatform? buildHost = null) - { - buildHost ??= ApplicationAdapter.Instance.Platform; + SetExecutePermission(sentryCliPath); + return sentryCliPath; + } - return buildHost switch - { - RuntimePlatform.WindowsEditor => SentryCliWindows, - RuntimePlatform.OSXEditor => SentryCliMacOS, - RuntimePlatform.LinuxEditor => SentryCliLinux, - _ => throw new InvalidOperationException($"Cannot get sentry-cli for {buildHost}") - }; - } + internal static string GetSentryCliPlatformExecutable(RuntimePlatform? buildHost = null) + { + buildHost ??= ApplicationAdapter.Instance.Platform; - internal static string GetSentryCliPath(string sentryCliPlatformName) + return buildHost switch { - var sentryCliPath = Path.GetFullPath( - Path.Combine("Packages", SentryPackageInfo.GetName(), "Editor", "sentry-cli", sentryCliPlatformName)); + RuntimePlatform.WindowsEditor => SentryCliWindows, + RuntimePlatform.OSXEditor => SentryCliMacOS, + RuntimePlatform.LinuxEditor => SentryCliLinux, + _ => throw new InvalidOperationException($"Cannot get sentry-cli for {buildHost}") + }; + } - if (!File.Exists(sentryCliPath)) - { - throw new FileNotFoundException($"Could not find sentry-cli at path: {sentryCliPath}"); - } + internal static string GetSentryCliPath(string sentryCliPlatformName) + { + var sentryCliPath = Path.GetFullPath( + Path.Combine("Packages", SentryPackageInfo.GetName(), "Editor", "sentry-cli", sentryCliPlatformName)); - return sentryCliPath; + if (!File.Exists(sentryCliPath)) + { + throw new FileNotFoundException($"Could not find sentry-cli at path: {sentryCliPath}"); } - internal static void SetExecutePermission(string? filePath = null, IApplication? application = null) + return sentryCliPath; + } + + internal static void SetExecutePermission(string? filePath = null, IApplication? application = null) + { + application ??= ApplicationAdapter.Instance; + if (application.Platform == RuntimePlatform.WindowsEditor) { - application ??= ApplicationAdapter.Instance; - if (application.Platform == RuntimePlatform.WindowsEditor) - { - return; - } + return; + } - // 493 is the integer value for permissions 755 - if (chmod(Path.GetFullPath(filePath), 493) != 0) - { - throw new UnauthorizedAccessException($"Failed to set permission to {filePath}"); - } + // 493 is the integer value for permissions 755 + if (chmod(Path.GetFullPath(filePath), 493) != 0) + { + throw new UnauthorizedAccessException($"Failed to set permission to {filePath}"); } + } - internal static string? UrlOverride(string? dsnOption, string? urlOverrideOption) + internal static string? UrlOverride(string? dsnOption, string? urlOverrideOption) + { + string? result = urlOverrideOption; + if (string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(dsnOption)) { - string? result = urlOverrideOption; - if (string.IsNullOrEmpty(result) && !string.IsNullOrEmpty(dsnOption)) - { - var uri = new Uri(dsnOption); + var uri = new Uri(dsnOption); - // Override the URL if the DSN is configured to a non-default server - if (!uri.DnsSafeHost.Equals("sentry.io") && !uri.DnsSafeHost.EndsWith(".sentry.io")) - { - result = new UriBuilder(uri.Scheme, uri.DnsSafeHost, uri.Port, "").Uri.AbsoluteUri.TrimEnd('/'); - } + // Override the URL if the DSN is configured to a non-default server + if (!uri.DnsSafeHost.Equals("sentry.io") && !uri.DnsSafeHost.EndsWith(".sentry.io")) + { + result = new UriBuilder(uri.Scheme, uri.DnsSafeHost, uri.Port, "").Uri.AbsoluteUri.TrimEnd('/'); } - return string.IsNullOrEmpty(result) ? null : result; } + return string.IsNullOrEmpty(result) ? null : result; + } - private static string ToCliLogLevel(this SentryLevel level) + private static string ToCliLogLevel(this SentryLevel level) + { + return level switch { - return level switch - { - SentryLevel.Debug => "debug", - SentryLevel.Info => "info", - SentryLevel.Warning => "warn", - SentryLevel.Error => "error", - SentryLevel.Fatal => "error", - _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) - }; - } + SentryLevel.Debug => "debug", + SentryLevel.Info => "info", + SentryLevel.Warning => "warn", + SentryLevel.Error => "error", + SentryLevel.Fatal => "error", + _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) + }; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs b/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs index 7c981cc10..54e3ed170 100644 --- a/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs +++ b/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs @@ -1,26 +1,25 @@ using UnityEditor; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +[CustomEditor(typeof(SentryCliOptions))] +public class SentryCliOptionsEditor : UnityEditor.Editor { - [CustomEditor(typeof(SentryCliOptions))] - public class SentryCliOptionsEditor : UnityEditor.Editor + public override void OnInspectorGUI() { - public override void OnInspectorGUI() + if (target is not SentryCliOptions cliOptions) { - if (target is not SentryCliOptions cliOptions) - { - return; - } + return; + } - EditorGUI.BeginDisabledGroup(true); + EditorGUI.BeginDisabledGroup(true); - EditorGUILayout.Toggle("Enable Symbols Upload", cliOptions.UploadSymbols); - EditorGUILayout.Toggle("Enable Dev Symbols Upload", cliOptions.UploadDevelopmentSymbols); - EditorGUILayout.TextField("Auth-Token", cliOptions.Auth); - EditorGUILayout.TextField("Org-Slug", cliOptions.Organization); - EditorGUILayout.TextField("Project Name", cliOptions.Project); + EditorGUILayout.Toggle("Enable Symbols Upload", cliOptions.UploadSymbols); + EditorGUILayout.Toggle("Enable Dev Symbols Upload", cliOptions.UploadDevelopmentSymbols); + EditorGUILayout.TextField("Auth-Token", cliOptions.Auth); + EditorGUILayout.TextField("Org-Slug", cliOptions.Organization); + EditorGUILayout.TextField("Project Name", cliOptions.Project); - EditorGUI.EndDisabledGroup(); - } + EditorGUI.EndDisabledGroup(); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/SentryFileUtil.cs b/src/Sentry.Unity.Editor/SentryFileUtil.cs index d7f245cfd..66106bb44 100644 --- a/src/Sentry.Unity.Editor/SentryFileUtil.cs +++ b/src/Sentry.Unity.Editor/SentryFileUtil.cs @@ -1,31 +1,30 @@ using System.IO; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +public static class SentryFileUtil { - public static class SentryFileUtil + internal static void CopyDirectory(string sourceDirectory, string destinationDirectory) { - internal static void CopyDirectory(string sourceDirectory, string destinationDirectory) + var directory = new DirectoryInfo(sourceDirectory); + if (!directory.Exists) { - var directory = new DirectoryInfo(sourceDirectory); - if (!directory.Exists) - { - throw new DirectoryNotFoundException($"Source directory not found: {directory.FullName}"); - } + throw new DirectoryNotFoundException($"Source directory not found: {directory.FullName}"); + } - var subDirectories = directory.GetDirectories(); - Directory.CreateDirectory(destinationDirectory); + var subDirectories = directory.GetDirectories(); + Directory.CreateDirectory(destinationDirectory); - foreach (var file in directory.GetFiles()) - { - var targetFilePath = Path.Combine(destinationDirectory, file.Name); - file.CopyTo(targetFilePath); - } + foreach (var file in directory.GetFiles()) + { + var targetFilePath = Path.Combine(destinationDirectory, file.Name); + file.CopyTo(targetFilePath); + } - foreach (var subDirectory in subDirectories) - { - var newDestinationDir = Path.Combine(destinationDirectory, subDirectory.Name); - CopyDirectory(subDirectory.FullName, newDestinationDir); - } + foreach (var subDirectory in subDirectories) + { + var newDestinationDir = Path.Combine(destinationDirectory, subDirectory.Name); + CopyDirectory(subDirectory.FullName, newDestinationDir); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/SentryPackageInfo.cs b/src/Sentry.Unity.Editor/SentryPackageInfo.cs index 100296cde..e84f4cfc8 100644 --- a/src/Sentry.Unity.Editor/SentryPackageInfo.cs +++ b/src/Sentry.Unity.Editor/SentryPackageInfo.cs @@ -1,29 +1,28 @@ using System.IO; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal static class SentryPackageInfo { - internal static class SentryPackageInfo - { - internal static string PackageName = SentryUnityOptions.PackageName; - internal static string PackageNameDev = SentryUnityOptions.PackageName + ".dev"; + internal static string PackageName = SentryUnityOptions.PackageName; + internal static string PackageNameDev = SentryUnityOptions.PackageName + ".dev"; - internal static string GetName() + internal static string GetName() + { + var packagePath = Path.Combine("Packages", PackageName); + if (Directory.Exists(Path.Combine(packagePath))) { - var packagePath = Path.Combine("Packages", PackageName); - if (Directory.Exists(Path.Combine(packagePath))) - { - return PackageName; - } - - packagePath = Path.Combine("Packages", PackageNameDev); - if (Directory.Exists(Path.Combine(packagePath))) - { - return PackageNameDev; - } + return PackageName; + } - throw new FileNotFoundException("Failed to locate the Sentry package"); + packagePath = Path.Combine("Packages", PackageNameDev); + if (Directory.Exists(Path.Combine(packagePath))) + { + return PackageNameDev; } - internal static bool IsDevPackage => GetName() == PackageNameDev; + throw new FileNotFoundException("Failed to locate the Sentry package"); } -} + + internal static bool IsDevPackage => GetName() == PackageNameDev; +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/SentryScriptableObject.cs b/src/Sentry.Unity.Editor/SentryScriptableObject.cs index 65d923125..4b3c672c7 100644 --- a/src/Sentry.Unity.Editor/SentryScriptableObject.cs +++ b/src/Sentry.Unity.Editor/SentryScriptableObject.cs @@ -2,51 +2,50 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal static class SentryScriptableObject { - internal static class SentryScriptableObject + internal static T CreateOrLoad(string path) where T : ScriptableObject { - internal static T CreateOrLoad(string path) where T : ScriptableObject + var options = Load(path); + if (options == null) { - var options = Load(path); - if (options == null) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)); - - options = ScriptableObject.CreateInstance(); + Directory.CreateDirectory(Path.GetDirectoryName(path)); - AssetDatabase.CreateAsset(options, path); - AssetDatabase.SaveAssets(); - } + options = ScriptableObject.CreateInstance(); - return options; + AssetDatabase.CreateAsset(options, path); + AssetDatabase.SaveAssets(); } - internal static T? Load(string path) where T : ScriptableObject => AssetDatabase.LoadAssetAtPath(path); + return options; + } - private static SentryCliOptions? LoadCliOptions() => Load(SentryCliOptions.GetConfigPath()); - internal static ScriptableSentryUnityOptions? LoadOptions() => - Load(ScriptableSentryUnityOptions.GetConfigPath()); + internal static T? Load(string path) where T : ScriptableObject => AssetDatabase.LoadAssetAtPath(path); - internal static (SentryUnityOptions?, SentryCliOptions?) ConfiguredBuildTimeOptions() - { - var scriptableOptions = LoadOptions(); - var cliOptions = LoadCliOptions(); + private static SentryCliOptions? LoadCliOptions() => Load(SentryCliOptions.GetConfigPath()); + internal static ScriptableSentryUnityOptions? LoadOptions() => + Load(ScriptableSentryUnityOptions.GetConfigPath()); - SentryUnityOptions? options = null; - if (scriptableOptions is not null) + internal static (SentryUnityOptions?, SentryCliOptions?) ConfiguredBuildTimeOptions() + { + var scriptableOptions = LoadOptions(); + var cliOptions = LoadCliOptions(); + + SentryUnityOptions? options = null; + if (scriptableOptions is not null) + { + options = scriptableOptions.ToSentryUnityOptions(isBuilding: true, unityInfo: null); + // Must be non-nullable in the interface otherwise Unity script compilation fails... + cliOptions ??= ScriptableObject.CreateInstance(); + var setupScript = scriptableOptions.BuildTimeOptionsConfiguration; + if (setupScript != null) { - options = scriptableOptions.ToSentryUnityOptions(isBuilding: true, unityInfo: null); - // Must be non-nullable in the interface otherwise Unity script compilation fails... - cliOptions ??= ScriptableObject.CreateInstance(); - var setupScript = scriptableOptions.BuildTimeOptionsConfiguration; - if (setupScript != null) - { - setupScript.Configure(options, cliOptions); - } + setupScript.Configure(options, cliOptions); } - - return (options, cliOptions); } + + return (options, cliOptions); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/SentryUnityVersion.cs b/src/Sentry.Unity.Editor/SentryUnityVersion.cs index 4705504ab..23240f3de 100644 --- a/src/Sentry.Unity.Editor/SentryUnityVersion.cs +++ b/src/Sentry.Unity.Editor/SentryUnityVersion.cs @@ -2,20 +2,19 @@ using System.Text.RegularExpressions; using Sentry.Unity.Integrations; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal static class SentryUnityVersion { - internal static class SentryUnityVersion - { - public static bool IsNewerOrEqualThan(string version, IApplication? application = null) - => GetVersion(application) >= new Version(version); + public static bool IsNewerOrEqualThan(string version, IApplication? application = null) + => GetVersion(application) >= new Version(version); - internal static Version? GetVersion(IApplication? application = null) - { - application ??= ApplicationAdapter.Instance; - // The Unity version format looks like this: '2019.4.38f1', '2022.1.0a17' or '2022.1.1b4', - // but Version() expects only the numerical parts, e.g. `2021.1.0` - var unityVersion = Regex.Replace(application.UnityVersion, "^([0-9]+\\.[0-9]+\\.[0-9]+)[a-z].*$", "$1"); - return new Version(unityVersion); - } + internal static Version? GetVersion(IApplication? application = null) + { + application ??= ApplicationAdapter.Instance; + // The Unity version format looks like this: '2019.4.38f1', '2022.1.0a17' or '2022.1.1b4', + // but Version() expects only the numerical parts, e.g. `2021.1.0` + var unityVersion = Regex.Replace(application.UnityVersion, "^([0-9]+\\.[0-9]+\\.[0-9]+)[a-z].*$", "$1"); + return new Version(unityVersion); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs b/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs index a2a2a5391..413bc2eb5 100644 --- a/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs +++ b/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs @@ -1,29 +1,28 @@ using System; using System.Collections.Generic; -namespace Sentry.Unity.Editor +namespace Sentry.Unity.Editor; + +internal static class CommandLineArgumentParser { - internal static class CommandLineArgumentParser + internal static Dictionary Parse() { - internal static Dictionary Parse() - { - var commandLineArguments = new Dictionary(); - var args = Environment.GetCommandLineArgs(); + var commandLineArguments = new Dictionary(); + var args = Environment.GetCommandLineArgs(); - for (int current = 0, next = 1; current < args.Length; current++, next++) + for (int current = 0, next = 1; current < args.Length; current++, next++) + { + if (!args[current].StartsWith("-")) { - if (!args[current].StartsWith("-")) - { - continue; - } + continue; + } - var flag = args[current].TrimStart('-'); - var flagHasValue = next < args.Length && !args[next].StartsWith("-"); - var flagValue = flagHasValue ? args[next].TrimStart('-') : ""; + var flag = args[current].TrimStart('-'); + var flagHasValue = next < args.Length && !args[next].StartsWith("-"); + var flagValue = flagHasValue ? args[next].TrimStart('-') : ""; - commandLineArguments.Add(flag, flagValue); - } - return commandLineArguments; + commandLineArguments.Add(flag, flagValue); } + return commandLineArguments; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Native/NativeContextWriter.cs b/src/Sentry.Unity.Native/NativeContextWriter.cs index fb5b1d79b..b3de0731f 100644 --- a/src/Sentry.Unity.Native/NativeContextWriter.cs +++ b/src/Sentry.Unity.Native/NativeContextWriter.cs @@ -1,85 +1,84 @@ using CWUtil = Sentry.Unity.NativeUtils.ContextWriter; -namespace Sentry.Unity.Native +namespace Sentry.Unity.Native; + +internal class NativeContextWriter : ContextWriter { - internal class NativeContextWriter : ContextWriter + protected override void WriteScope( + string? AppStartTime, + string? AppBuildType, + string? OperatingSystemRawDescription, + int? DeviceProcessorCount, + string? DeviceCpuDescription, + string? DeviceTimezone, + bool? DeviceSupportsVibration, + string? DeviceName, + bool? DeviceSimulator, + string? DeviceDeviceUniqueIdentifier, + string? DeviceDeviceType, + string? DeviceModel, + long? DeviceMemorySize, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel, + string? EditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode + ) { - protected override void WriteScope( - string? AppStartTime, - string? AppBuildType, - string? OperatingSystemRawDescription, - int? DeviceProcessorCount, - string? DeviceCpuDescription, - string? DeviceTimezone, - bool? DeviceSupportsVibration, - string? DeviceName, - bool? DeviceSimulator, - string? DeviceDeviceUniqueIdentifier, - string? DeviceDeviceType, - string? DeviceModel, - long? DeviceMemorySize, - int? GpuId, - string? GpuName, - string? GpuVendorName, - int? GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int? GpuMaxTextureSize, - bool? GpuSupportsDrawCallInstancing, - bool? GpuSupportsRayTracing, - bool? GpuSupportsComputeShaders, - bool? GpuSupportsGeometryShaders, - string? GpuVendorId, - bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel, - string? EditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode - ) - { - CWUtil.WriteApp(AppStartTime, AppBuildType); + CWUtil.WriteApp(AppStartTime, AppBuildType); - CWUtil.WriteOS(OperatingSystemRawDescription); + CWUtil.WriteOS(OperatingSystemRawDescription); - CWUtil.WriteDevice( - DeviceProcessorCount, - DeviceCpuDescription, - DeviceTimezone, - DeviceSupportsVibration, - DeviceName, - DeviceSimulator, - DeviceDeviceUniqueIdentifier, - DeviceDeviceType, - DeviceModel, - DeviceMemorySize - ); + CWUtil.WriteDevice( + DeviceProcessorCount, + DeviceCpuDescription, + DeviceTimezone, + DeviceSupportsVibration, + DeviceName, + DeviceSimulator, + DeviceDeviceUniqueIdentifier, + DeviceDeviceType, + DeviceModel, + DeviceMemorySize + ); - CWUtil.WriteGpu( - GpuId, - GpuName, - GpuVendorName, - GpuMemorySize, - GpuNpotSupport, - GpuVersion, - GpuApiType, - GpuMaxTextureSize, - GpuSupportsDrawCallInstancing, - GpuSupportsRayTracing, - GpuSupportsComputeShaders, - GpuSupportsGeometryShaders, - GpuVendorId, - GpuMultiThreadedRendering, - GpuGraphicsShaderLevel); + CWUtil.WriteGpu( + GpuId, + GpuName, + GpuVendorName, + GpuMemorySize, + GpuNpotSupport, + GpuVersion, + GpuApiType, + GpuMaxTextureSize, + GpuSupportsDrawCallInstancing, + GpuSupportsRayTracing, + GpuSupportsComputeShaders, + GpuSupportsGeometryShaders, + GpuVendorId, + GpuMultiThreadedRendering, + GpuGraphicsShaderLevel); - CWUtil.WriteUnity( - EditorVersion, - UnityInstallMode, - UnityTargetFrameRate, - UnityCopyTextureSupport, - UnityRenderingThreadingMode); - } + CWUtil.WriteUnity( + EditorVersion, + UnityInstallMode, + UnityTargetFrameRate, + UnityCopyTextureSupport, + UnityRenderingThreadingMode); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity.Native/NativeScopeObserver.cs b/src/Sentry.Unity.Native/NativeScopeObserver.cs index 98601a1c7..3c8618e76 100644 --- a/src/Sentry.Unity.Native/NativeScopeObserver.cs +++ b/src/Sentry.Unity.Native/NativeScopeObserver.cs @@ -1,51 +1,48 @@ using System; -using System.Runtime.InteropServices; -using Sentry.Extensibility; using C = Sentry.Unity.NativeUtils.C; -namespace Sentry.Unity.Native +namespace Sentry.Unity.Native; + +/// +/// Scope Observer for Native through P/Invoke. +/// +/// +public class NativeScopeObserver : ScopeObserver { - /// - /// Scope Observer for Native through P/Invoke. - /// - /// - public class NativeScopeObserver : ScopeObserver + public NativeScopeObserver(SentryOptions options) : base("Native", options) { } + + public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) + { + // see https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/ + var crumb = C.sentry_value_new_breadcrumb(breadcrumb.Type, breadcrumb.Message); + C.sentry_value_set_by_key(crumb, "level", C.sentry_value_new_string(breadcrumb.Level.ToString().ToLower())); + C.sentry_value_set_by_key(crumb, "timestamp", C.sentry_value_new_string(GetTimestamp(breadcrumb.Timestamp))); + C.SetValueIfNotNull(crumb, "category", breadcrumb.Category); + C.sentry_add_breadcrumb(crumb); + } + + public override void SetExtraImpl(string key, string? value) => + C.sentry_set_extra(key, value is null ? C.sentry_value_new_null() : C.sentry_value_new_string(value)); + + public override void SetTagImpl(string key, string value) => C.sentry_set_tag(key, value); + + public override void UnsetTagImpl(string key) => C.sentry_remove_tag(key); + + public override void SetUserImpl(SentryUser user) { - public NativeScopeObserver(SentryOptions options) : base("Native", options) { } - - public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) - { - // see https://develop.sentry.dev/sdk/event-payloads/breadcrumbs/ - var crumb = C.sentry_value_new_breadcrumb(breadcrumb.Type, breadcrumb.Message); - C.sentry_value_set_by_key(crumb, "level", C.sentry_value_new_string(breadcrumb.Level.ToString().ToLower())); - C.sentry_value_set_by_key(crumb, "timestamp", C.sentry_value_new_string(GetTimestamp(breadcrumb.Timestamp))); - C.SetValueIfNotNull(crumb, "category", breadcrumb.Category); - C.sentry_add_breadcrumb(crumb); - } - - public override void SetExtraImpl(string key, string? value) => - C.sentry_set_extra(key, value is null ? C.sentry_value_new_null() : C.sentry_value_new_string(value)); - - public override void SetTagImpl(string key, string value) => C.sentry_set_tag(key, value); - - public override void UnsetTagImpl(string key) => C.sentry_remove_tag(key); - - public override void SetUserImpl(SentryUser user) - { - // see https://develop.sentry.dev/sdk/event-payloads/user/ - var cUser = C.sentry_value_new_object(); - C.SetValueIfNotNull(cUser, "id", user.Id); - C.SetValueIfNotNull(cUser, "username", user.Username); - C.SetValueIfNotNull(cUser, "email", user.Email); - C.SetValueIfNotNull(cUser, "ip_address", user.IpAddress); - C.sentry_set_user(cUser); - } - - public override void UnsetUserImpl() => C.sentry_remove_user(); - - private static string GetTimestamp(DateTimeOffset timestamp) => - // "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly. - // https://docs.microsoft.com/en-gb/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip - timestamp.ToString("o"); + // see https://develop.sentry.dev/sdk/event-payloads/user/ + var cUser = C.sentry_value_new_object(); + C.SetValueIfNotNull(cUser, "id", user.Id); + C.SetValueIfNotNull(cUser, "username", user.Username); + C.SetValueIfNotNull(cUser, "email", user.Email); + C.SetValueIfNotNull(cUser, "ip_address", user.IpAddress); + C.sentry_set_user(cUser); } -} + + public override void UnsetUserImpl() => C.sentry_remove_user(); + + private static string GetTimestamp(DateTimeOffset timestamp) => + // "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly. + // https://docs.microsoft.com/en-gb/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip + timestamp.ToString("o"); +} \ No newline at end of file diff --git a/src/Sentry.Unity.Native/SentryNative.cs b/src/Sentry.Unity.Native/SentryNative.cs index 75bfed428..e0394e093 100644 --- a/src/Sentry.Unity.Native/SentryNative.cs +++ b/src/Sentry.Unity.Native/SentryNative.cs @@ -2,87 +2,85 @@ using Sentry.Extensibility; using Sentry.Unity.Integrations; using System.Collections.Generic; -using UnityEngine; using UnityEngine.Analytics; -namespace Sentry.Unity.Native +namespace Sentry.Unity.Native; + +/// +/// Access to the Sentry native support of the sentry-native SDK. +/// +public static class SentryNative { + private static readonly Dictionary PerDirectoryCrashInfo = new(); + /// - /// Access to the Sentry native support of the sentry-native SDK. + /// Configures the native SDK. /// - public static class SentryNative + /// The Sentry Unity options to use. + /// Infos about the current Unity environment + public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) { - private static readonly Dictionary PerDirectoryCrashInfo = new(); + options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Native SDK"); - /// - /// Configures the native SDK. - /// - /// The Sentry Unity options to use. - /// Infos about the current Unity environment - public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) + if (!sentryUnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) { - options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Native SDK"); - - if (!sentryUnityInfo.IsNativeSupportEnabled(options, ApplicationAdapter.Instance.Platform)) - { - options.DiagnosticLogger?.LogDebug("Native support is disabled for '{0}'.", ApplicationAdapter.Instance.Platform); - return; - } + options.DiagnosticLogger?.LogDebug("Native support is disabled for '{0}'.", ApplicationAdapter.Instance.Platform); + return; + } - try - { - if (!SentryNativeBridge.Init(options, sentryUnityInfo)) - { - options.DiagnosticLogger? - .LogWarning("Sentry native initialization failed - native crashes are not captured."); - return; - } - } - catch (Exception e) + try + { + if (!SentryNativeBridge.Init(options, sentryUnityInfo)) { options.DiagnosticLogger? - .LogError(e, "Sentry native initialization failed - native crashes are not captured."); + .LogWarning("Sentry native initialization failed - native crashes are not captured."); return; } + } + catch (Exception e) + { + options.DiagnosticLogger? + .LogError(e, "Sentry native initialization failed - native crashes are not captured."); + return; + } - ApplicationAdapter.Instance.Quitting += () => - { - options.DiagnosticLogger?.LogDebug("Closing the sentry-native SDK"); - SentryNativeBridge.Close(); - }; - options.ScopeObserver = new NativeScopeObserver(options); - options.EnableScopeSync = true; - options.NativeContextWriter = new NativeContextWriter(); + ApplicationAdapter.Instance.Quitting += () => + { + options.DiagnosticLogger?.LogDebug("Closing the sentry-native SDK"); + SentryNativeBridge.Close(); + }; + options.ScopeObserver = new NativeScopeObserver(options); + options.EnableScopeSync = true; + options.NativeContextWriter = new NativeContextWriter(); - // Use AnalyticsSessionInfo.userId as the default UserID in native & dotnet - options.DefaultUserId = AnalyticsSessionInfo.userId; - if (options.DefaultUserId is not null) - { - options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); - } + // Use AnalyticsSessionInfo.userId as the default UserID in native & dotnet + options.DefaultUserId = AnalyticsSessionInfo.userId; + if (options.DefaultUserId is not null) + { + options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); + } - // Note: we must actually call the function now and on every other call use the value we get here. - // Additionally, we cannot call this multiple times for the same directory, because the result changes - // on subsequent runs. Therefore, we cache the value during the whole runtime of the application. - var cacheDirectory = SentryNativeBridge.GetCacheDirectory(options); - var crashedLastRun = false; - // In the event the SDK is re-initialized with a different path on disk, we need to track which ones were already read - // Similarly we need to cache the value of each call since a subsequent call would return a different value - // as the file used on disk to mark it as crashed is deleted after we read it. - lock (PerDirectoryCrashInfo) + // Note: we must actually call the function now and on every other call use the value we get here. + // Additionally, we cannot call this multiple times for the same directory, because the result changes + // on subsequent runs. Therefore, we cache the value during the whole runtime of the application. + var cacheDirectory = SentryNativeBridge.GetCacheDirectory(options); + var crashedLastRun = false; + // In the event the SDK is re-initialized with a different path on disk, we need to track which ones were already read + // Similarly we need to cache the value of each call since a subsequent call would return a different value + // as the file used on disk to mark it as crashed is deleted after we read it. + lock (PerDirectoryCrashInfo) + { + if (!PerDirectoryCrashInfo.TryGetValue(cacheDirectory, out crashedLastRun)) { - if (!PerDirectoryCrashInfo.TryGetValue(cacheDirectory, out crashedLastRun)) - { - crashedLastRun = SentryNativeBridge.HandleCrashedLastRun(options); - PerDirectoryCrashInfo.Add(cacheDirectory, crashedLastRun); + crashedLastRun = SentryNativeBridge.HandleCrashedLastRun(options); + PerDirectoryCrashInfo.Add(cacheDirectory, crashedLastRun); - options.DiagnosticLogger? - .LogDebug("Native SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); - } + options.DiagnosticLogger? + .LogDebug("Native SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); } - options.CrashedLastRun = () => crashedLastRun; } - - public static void ReinstallBackend() => SentryNativeBridge.ReinstallBackend(); + options.CrashedLastRun = () => crashedLastRun; } -} + + public static void ReinstallBackend() => SentryNativeBridge.ReinstallBackend(); +} \ No newline at end of file diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index bde377f36..f6af73ae0 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -6,279 +6,278 @@ using UnityEngine; using AOT; -namespace Sentry.Unity.Native +namespace Sentry.Unity.Native; + +/// +/// P/Invoke to `sentry-native` functions. +/// +/// +public static class SentryNativeBridge { - /// - /// P/Invoke to `sentry-native` functions. - /// - /// - public static class SentryNativeBridge + public static bool CrashedLastRun; + + public static bool Init(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) { - public static bool CrashedLastRun; + _isLinux = sentryUnityInfo.IsLinux(); - public static bool Init(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) - { - _isLinux = sentryUnityInfo.IsLinux(); + var cOptions = sentry_options_new(); - var cOptions = sentry_options_new(); + // Note: DSN is not null because options.IsValid() must have returned true for this to be called. + sentry_options_set_dsn(cOptions, options.Dsn!); - // Note: DSN is not null because options.IsValid() must have returned true for this to be called. - sentry_options_set_dsn(cOptions, options.Dsn!); + if (options.Release is not null) + { + options.DiagnosticLogger?.LogDebug("Setting Release: {0}", options.Release); + sentry_options_set_release(cOptions, options.Release); + } - if (options.Release is not null) - { - options.DiagnosticLogger?.LogDebug("Setting Release: {0}", options.Release); - sentry_options_set_release(cOptions, options.Release); - } + if (options.Environment is not null) + { + options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment); + sentry_options_set_environment(cOptions, options.Environment); + } - if (options.Environment is not null) - { - options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment); - sentry_options_set_environment(cOptions, options.Environment); - } + options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug); + sentry_options_set_debug(cOptions, options.Debug ? 1 : 0); - options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug); - sentry_options_set_debug(cOptions, options.Debug ? 1 : 0); + if (options.SampleRate.HasValue) + { + options.DiagnosticLogger?.LogDebug("Setting Sample Rate: {0}", options.SampleRate.Value); + sentry_options_set_sample_rate(cOptions, options.SampleRate.Value); + } - if (options.SampleRate.HasValue) - { - options.DiagnosticLogger?.LogDebug("Setting Sample Rate: {0}", options.SampleRate.Value); - sentry_options_set_sample_rate(cOptions, options.SampleRate.Value); - } + // Disabling the native in favor of the C# layer for now + options.DiagnosticLogger?.LogDebug("Disabling native auto session tracking"); + sentry_options_set_auto_session_tracking(cOptions, 0); - // Disabling the native in favor of the C# layer for now - options.DiagnosticLogger?.LogDebug("Disabling native auto session tracking"); - sentry_options_set_auto_session_tracking(cOptions, 0); + var dir = GetCacheDirectory(options); + // Note: don't use RuntimeInformation.IsOSPlatform - it will report windows on WSL. + if (ApplicationAdapter.Instance.Platform is RuntimePlatform.WindowsPlayer) + { + options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath on Windows: {0}", dir); + sentry_options_set_database_pathw(cOptions, dir); + } + else + { + options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath: {0}", dir); + sentry_options_set_database_path(cOptions, dir); + } - var dir = GetCacheDirectory(options); - // Note: don't use RuntimeInformation.IsOSPlatform - it will report windows on WSL. - if (ApplicationAdapter.Instance.Platform is RuntimePlatform.WindowsPlayer) - { - options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath on Windows: {0}", dir); - sentry_options_set_database_pathw(cOptions, dir); - } - else - { - options.DiagnosticLogger?.LogDebug("Setting CacheDirectoryPath: {0}", dir); - sentry_options_set_database_path(cOptions, dir); - } + if (options.DiagnosticLogger is null) + { + _logger?.LogDebug("Unsetting the current native logger"); + _logger = null; + } + else + { + options.DiagnosticLogger.LogDebug($"{(_logger is null ? "Setting a" : "Replacing the")} native logger"); + _logger = options.DiagnosticLogger; + sentry_options_set_logger(cOptions, new sentry_logger_function_t(nativeLog), IntPtr.Zero); + } - if (options.DiagnosticLogger is null) - { - _logger?.LogDebug("Unsetting the current native logger"); - _logger = null; - } - else - { - options.DiagnosticLogger.LogDebug($"{(_logger is null ? "Setting a" : "Replacing the")} native logger"); - _logger = options.DiagnosticLogger; - sentry_options_set_logger(cOptions, new sentry_logger_function_t(nativeLog), IntPtr.Zero); - } + options.DiagnosticLogger?.LogDebug("Initializing sentry native"); + return 0 == sentry_init(cOptions); + } - options.DiagnosticLogger?.LogDebug("Initializing sentry native"); - return 0 == sentry_init(cOptions); - } + public static void Close() => sentry_close(); - public static void Close() => sentry_close(); + // Call after native init() to check if the application has crashed in the previous run and clear the status. + // Because the file is removed, the result will change on subsequent calls so it must be cached for the current runtime. + internal static bool HandleCrashedLastRun(SentryUnityOptions options) + { + var result = sentry_get_crashed_last_run() == 1; + sentry_clear_crashed_last_run(); + return result; + } - // Call after native init() to check if the application has crashed in the previous run and clear the status. - // Because the file is removed, the result will change on subsequent calls so it must be cached for the current runtime. - internal static bool HandleCrashedLastRun(SentryUnityOptions options) + internal static string GetCacheDirectory(SentryUnityOptions options) + { + if (options.CacheDirectoryPath is null) { - var result = sentry_get_crashed_last_run() == 1; - sentry_clear_crashed_last_run(); - return result; + // same as the default of sentry-native + return Path.Combine(Directory.GetCurrentDirectory(), ".sentry-native"); } - - internal static string GetCacheDirectory(SentryUnityOptions options) + else { - if (options.CacheDirectoryPath is null) - { - // same as the default of sentry-native - return Path.Combine(Directory.GetCurrentDirectory(), ".sentry-native"); - } - else - { - return Path.Combine(options.CacheDirectoryPath, "SentryNative"); - } + return Path.Combine(options.CacheDirectoryPath, "SentryNative"); } + } - internal static void ReinstallBackend() => sentry_reinstall_backend(); + internal static void ReinstallBackend() => sentry_reinstall_backend(); - // libsentry.so - [DllImport("sentry")] - private static extern IntPtr sentry_options_new(); + // libsentry.so + [DllImport("sentry")] + private static extern IntPtr sentry_options_new(); - [DllImport("sentry")] - private static extern void sentry_options_set_dsn(IntPtr options, string dsn); + [DllImport("sentry")] + private static extern void sentry_options_set_dsn(IntPtr options, string dsn); - [DllImport("sentry")] - private static extern void sentry_options_set_release(IntPtr options, string release); + [DllImport("sentry")] + private static extern void sentry_options_set_release(IntPtr options, string release); - [DllImport("sentry")] - private static extern void sentry_options_set_debug(IntPtr options, int debug); + [DllImport("sentry")] + private static extern void sentry_options_set_debug(IntPtr options, int debug); - [DllImport("sentry")] - private static extern void sentry_options_set_environment(IntPtr options, string environment); + [DllImport("sentry")] + private static extern void sentry_options_set_environment(IntPtr options, string environment); - [DllImport("sentry")] - private static extern void sentry_options_set_sample_rate(IntPtr options, double rate); + [DllImport("sentry")] + private static extern void sentry_options_set_sample_rate(IntPtr options, double rate); - [DllImport("sentry")] - private static extern void sentry_options_set_database_path(IntPtr options, string path); + [DllImport("sentry")] + private static extern void sentry_options_set_database_path(IntPtr options, string path); - [DllImport("sentry")] - private static extern void sentry_options_set_database_pathw(IntPtr options, [MarshalAs(UnmanagedType.LPWStr)] string path); + [DllImport("sentry")] + private static extern void sentry_options_set_database_pathw(IntPtr options, [MarshalAs(UnmanagedType.LPWStr)] string path); - [DllImport("sentry")] - private static extern void sentry_options_set_auto_session_tracking(IntPtr options, int debug); + [DllImport("sentry")] + private static extern void sentry_options_set_auto_session_tracking(IntPtr options, int debug); - [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] - private delegate void sentry_logger_function_t(int level, IntPtr message, IntPtr argsAddress, IntPtr userData); + [UnmanagedFunctionPointer(CallingConvention.Cdecl, SetLastError = true)] + private delegate void sentry_logger_function_t(int level, IntPtr message, IntPtr argsAddress, IntPtr userData); - [DllImport("sentry")] - private static extern void sentry_options_set_logger(IntPtr options, sentry_logger_function_t logger, IntPtr userData); + [DllImport("sentry")] + private static extern void sentry_options_set_logger(IntPtr options, sentry_logger_function_t logger, IntPtr userData); - // The logger we should forward native messages to. This is referenced by nativeLog() which in turn for. - private static IDiagnosticLogger? _logger; - private static bool _isLinux = false; + // The logger we should forward native messages to. This is referenced by nativeLog() which in turn for. + private static IDiagnosticLogger? _logger; + private static bool _isLinux = false; - // This method is called from the C library and forwards incoming messages to the currently set _logger. - [MonoPInvokeCallback(typeof(sentry_logger_function_t))] - private static void nativeLog(int cLevel, IntPtr format, IntPtr args, IntPtr userData) + // This method is called from the C library and forwards incoming messages to the currently set _logger. + [MonoPInvokeCallback(typeof(sentry_logger_function_t))] + private static void nativeLog(int cLevel, IntPtr format, IntPtr args, IntPtr userData) + { + try { - try - { - nativeLogImpl(cLevel, format, args, userData); - } - catch - { - // never allow an exception back to native code - it would crash the app - } + nativeLogImpl(cLevel, format, args, userData); + } + catch + { + // never allow an exception back to native code - it would crash the app } + } - private static void nativeLogImpl(int cLevel, IntPtr format, IntPtr args, IntPtr userData) + private static void nativeLogImpl(int cLevel, IntPtr format, IntPtr args, IntPtr userData) + { + var logger = _logger; + if (logger is null || format == IntPtr.Zero || args == IntPtr.Zero) { - var logger = _logger; - if (logger is null || format == IntPtr.Zero || args == IntPtr.Zero) - { - return; - } + return; + } - // see sentry.h: sentry_level_e - var level = cLevel switch - { - -1 => SentryLevel.Debug, - 0 => SentryLevel.Info, - 1 => SentryLevel.Warning, - 2 => SentryLevel.Error, - 3 => SentryLevel.Fatal, - _ => SentryLevel.Info, - }; - - if (!logger.IsEnabled(level)) - { - return; - } + // see sentry.h: sentry_level_e + var level = cLevel switch + { + -1 => SentryLevel.Debug, + 0 => SentryLevel.Info, + 1 => SentryLevel.Warning, + 2 => SentryLevel.Error, + 3 => SentryLevel.Fatal, + _ => SentryLevel.Info, + }; + + if (!logger.IsEnabled(level)) + { + return; + } - string? message = null; - try + string? message = null; + try + { + // We cannot access C var-arg (va_list) in c# thus we pass it back to vsnprintf to do the formatting. + // For Linux, we must make a copy of the VaList to be able to pass it back... + if (_isLinux) { - // We cannot access C var-arg (va_list) in c# thus we pass it back to vsnprintf to do the formatting. - // For Linux, we must make a copy of the VaList to be able to pass it back... - if (_isLinux) + var argsStruct = Marshal.PtrToStructure(args); + var formattedLength = 0; + WithMarshalledStruct(argsStruct, argsPtr => { - var argsStruct = Marshal.PtrToStructure(args); - var formattedLength = 0; - WithMarshalledStruct(argsStruct, argsPtr => - { - formattedLength = 1 + vsnprintf_linux(IntPtr.Zero, UIntPtr.Zero, format, argsPtr); - }); + formattedLength = 1 + vsnprintf_linux(IntPtr.Zero, UIntPtr.Zero, format, argsPtr); + }); - WithAllocatedPtr(formattedLength, buffer => + WithAllocatedPtr(formattedLength, buffer => WithMarshalledStruct(argsStruct, argsPtr => { vsnprintf_linux(buffer, (UIntPtr)formattedLength, format, argsPtr); message = Marshal.PtrToStringAnsi(buffer); })); - } - else - { - var formattedLength = 1 + vsnprintf_windows(IntPtr.Zero, UIntPtr.Zero, format, args); - WithAllocatedPtr(formattedLength, buffer => - { - vsnprintf_windows(buffer, (UIntPtr)formattedLength, format, args); - message = Marshal.PtrToStringAnsi(buffer); - }); - } } - catch (Exception err) + else { - logger.LogError(err, "Exception in native log forwarder."); + var formattedLength = 1 + vsnprintf_windows(IntPtr.Zero, UIntPtr.Zero, format, args); + WithAllocatedPtr(formattedLength, buffer => + { + vsnprintf_windows(buffer, (UIntPtr)formattedLength, format, args); + message = Marshal.PtrToStringAnsi(buffer); + }); } + } + catch (Exception err) + { + logger.LogError(err, "Exception in native log forwarder."); + } - if (message == null) - { - // try to at least print the unreplaced message pattern - message = Marshal.PtrToStringAnsi(format); - } + if (message == null) + { + // try to at least print the unreplaced message pattern + message = Marshal.PtrToStringAnsi(format); + } - if (message != null) - { - logger.Log(level, $"Native: {message}"); - } + if (message != null) + { + logger.Log(level, $"Native: {message}"); } + } - [DllImport("msvcrt", EntryPoint = "vsnprintf")] - private static extern int vsnprintf_windows(IntPtr buffer, UIntPtr bufferSize, IntPtr format, IntPtr args); + [DllImport("msvcrt", EntryPoint = "vsnprintf")] + private static extern int vsnprintf_windows(IntPtr buffer, UIntPtr bufferSize, IntPtr format, IntPtr args); - [DllImport("libc", EntryPoint = "vsnprintf")] - private static extern int vsnprintf_linux(IntPtr buffer, UIntPtr bufferSize, IntPtr format, IntPtr args); + [DllImport("libc", EntryPoint = "vsnprintf")] + private static extern int vsnprintf_linux(IntPtr buffer, UIntPtr bufferSize, IntPtr format, IntPtr args); - // https://stackoverflow.com/a/4958507/2386130 - [StructLayout(LayoutKind.Sequential, Pack = 4)] - private struct VaListLinux64 + // https://stackoverflow.com/a/4958507/2386130 + [StructLayout(LayoutKind.Sequential, Pack = 4)] + private struct VaListLinux64 + { + uint gp_offset; + uint fp_offset; + IntPtr overflow_arg_area; + IntPtr reg_save_area; + } + + private static void WithAllocatedPtr(int size, Action action) + { + var ptr = IntPtr.Zero; + try { - uint gp_offset; - uint fp_offset; - IntPtr overflow_arg_area; - IntPtr reg_save_area; + ptr = Marshal.AllocHGlobal(size); + action(ptr); } - - private static void WithAllocatedPtr(int size, Action action) + finally { - var ptr = IntPtr.Zero; - try - { - ptr = Marshal.AllocHGlobal(size); - action(ptr); - } - finally - { - Marshal.FreeHGlobal(ptr); - } + Marshal.FreeHGlobal(ptr); } + } - private static void WithMarshalledStruct(T structure, Action action) where T : notnull => - WithAllocatedPtr(Marshal.SizeOf(structure), ptr => - { - Marshal.StructureToPtr(structure, ptr, false); - action(ptr); - }); + private static void WithMarshalledStruct(T structure, Action action) where T : notnull => + WithAllocatedPtr(Marshal.SizeOf(structure), ptr => + { + Marshal.StructureToPtr(structure, ptr, false); + action(ptr); + }); - [DllImport("sentry")] - private static extern int sentry_init(IntPtr options); + [DllImport("sentry")] + private static extern int sentry_init(IntPtr options); - [DllImport("sentry")] - private static extern int sentry_close(); + [DllImport("sentry")] + private static extern int sentry_close(); - [DllImport("sentry")] - private static extern int sentry_get_crashed_last_run(); + [DllImport("sentry")] + private static extern int sentry_get_crashed_last_run(); - [DllImport("sentry")] - private static extern int sentry_clear_crashed_last_run(); + [DllImport("sentry")] + private static extern int sentry_clear_crashed_last_run(); - [DllImport("sentry")] - private static extern void sentry_reinstall_backend(); - } -} + [DllImport("sentry")] + private static extern void sentry_reinstall_backend(); +} \ No newline at end of file diff --git a/src/Sentry.Unity.iOS/NativeContextWriter.cs b/src/Sentry.Unity.iOS/NativeContextWriter.cs index ac10edf6b..aaff6d3f7 100644 --- a/src/Sentry.Unity.iOS/NativeContextWriter.cs +++ b/src/Sentry.Unity.iOS/NativeContextWriter.cs @@ -1,122 +1,117 @@ -using System; -using System.IO; using System.Runtime.InteropServices; -using Sentry.Extensibility; -using Sentry.Unity.Integrations; -namespace Sentry.Unity.iOS +namespace Sentry.Unity.iOS; + +internal class NativeContextWriter : ContextWriter { - internal class NativeContextWriter : ContextWriter - { - protected override void WriteScope( - string? AppStartTime, - string? AppBuildType, - string? OperatingSystemRawDescription, - int? DeviceProcessorCount, - string? DeviceCpuDescription, - string? DeviceTimezone, - bool? DeviceSupportsVibration, - string? DeviceName, - bool? DeviceSimulator, - string? DeviceDeviceUniqueIdentifier, - string? DeviceDeviceType, - string? DeviceModel, - long? DeviceMemorySize, - int? GpuId, - string? GpuName, - string? GpuVendorName, - int? GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int? GpuMaxTextureSize, - bool? GpuSupportsDrawCallInstancing, - bool? GpuSupportsRayTracing, - bool? GpuSupportsComputeShaders, - bool? GpuSupportsGeometryShaders, - string? GpuVendorId, - bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel, - string? UnityEditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode - ) => SentryNativeBridgeWriteScope( - // // AppStartTime, - // AppBuildType, - // // OperatingSystemRawDescription, - // DeviceProcessorCount ?? 0, - // DeviceCpuDescription, - // DeviceTimezone, - // marshallNullableBool(DeviceSupportsVibration), - // DeviceName, - // marshallNullableBool(DeviceSimulator), - // DeviceDeviceUniqueIdentifier, - // DeviceDeviceType, - // // DeviceModel, - // // DeviceMemorySize, - GpuId ?? 0, - GpuName, - GpuVendorName, - GpuMemorySize ?? 0, - GpuNpotSupport, - GpuVersion, - GpuApiType, - GpuMaxTextureSize ?? 0, - marshallNullableBool(GpuSupportsDrawCallInstancing), - marshallNullableBool(GpuSupportsRayTracing), - marshallNullableBool(GpuSupportsComputeShaders), - marshallNullableBool(GpuSupportsGeometryShaders), - GpuVendorId, - marshallNullableBool(GpuMultiThreadedRendering), - GpuGraphicsShaderLevel, - UnityEditorVersion, - UnityInstallMode, - UnityTargetFrameRate, - UnityCopyTextureSupport, - UnityRenderingThreadingMode - ); + protected override void WriteScope( + string? AppStartTime, + string? AppBuildType, + string? OperatingSystemRawDescription, + int? DeviceProcessorCount, + string? DeviceCpuDescription, + string? DeviceTimezone, + bool? DeviceSupportsVibration, + string? DeviceName, + bool? DeviceSimulator, + string? DeviceDeviceUniqueIdentifier, + string? DeviceDeviceType, + string? DeviceModel, + long? DeviceMemorySize, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel, + string? UnityEditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode + ) => SentryNativeBridgeWriteScope( + // // AppStartTime, + // AppBuildType, + // // OperatingSystemRawDescription, + // DeviceProcessorCount ?? 0, + // DeviceCpuDescription, + // DeviceTimezone, + // marshallNullableBool(DeviceSupportsVibration), + // DeviceName, + // marshallNullableBool(DeviceSimulator), + // DeviceDeviceUniqueIdentifier, + // DeviceDeviceType, + // // DeviceModel, + // // DeviceMemorySize, + GpuId ?? 0, + GpuName, + GpuVendorName, + GpuMemorySize ?? 0, + GpuNpotSupport, + GpuVersion, + GpuApiType, + GpuMaxTextureSize ?? 0, + marshallNullableBool(GpuSupportsDrawCallInstancing), + marshallNullableBool(GpuSupportsRayTracing), + marshallNullableBool(GpuSupportsComputeShaders), + marshallNullableBool(GpuSupportsGeometryShaders), + GpuVendorId, + marshallNullableBool(GpuMultiThreadedRendering), + GpuGraphicsShaderLevel, + UnityEditorVersion, + UnityInstallMode, + UnityTargetFrameRate, + UnityCopyTextureSupport, + UnityRenderingThreadingMode + ); - private static sbyte marshallNullableBool(bool? value) => (sbyte)(value.HasValue ? (value.Value ? 1 : 0) : -1); + private static sbyte marshallNullableBool(bool? value) => (sbyte)(value.HasValue ? (value.Value ? 1 : 0) : -1); - // Note: we only forward information that's missing or significantly different in cocoa SDK events. - // Additionally, there's currently no way to update existing contexts, so no more Device info for now... - [DllImport("__Internal")] - private static extern void SentryNativeBridgeWriteScope( - // // string? AppStartTime, - // string? AppBuildType, - // // string? OperatingSystemRawDescription, - // int DeviceProcessorCount, - // string? DeviceCpuDescription, - // string? DeviceTimezone, - // sbyte DeviceSupportsVibration, - // string? DeviceName, - // sbyte DeviceSimulator, - // string? DeviceDeviceUniqueIdentifier, - // string? DeviceDeviceType, - // // string? DeviceModel, - // // long? DeviceMemorySize, - int GpuId, - string? GpuName, - string? GpuVendorName, - int GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int GpuMaxTextureSize, - sbyte GpuSupportsDrawCallInstancing, - sbyte GpuSupportsRayTracing, - sbyte GpuSupportsComputeShaders, - sbyte GpuSupportsGeometryShaders, - string? GpuVendorId, - sbyte GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel, - string? UnityEditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode - ); - } + // Note: we only forward information that's missing or significantly different in cocoa SDK events. + // Additionally, there's currently no way to update existing contexts, so no more Device info for now... + [DllImport("__Internal")] + private static extern void SentryNativeBridgeWriteScope( + // // string? AppStartTime, + // string? AppBuildType, + // // string? OperatingSystemRawDescription, + // int DeviceProcessorCount, + // string? DeviceCpuDescription, + // string? DeviceTimezone, + // sbyte DeviceSupportsVibration, + // string? DeviceName, + // sbyte DeviceSimulator, + // string? DeviceDeviceUniqueIdentifier, + // string? DeviceDeviceType, + // // string? DeviceModel, + // // long? DeviceMemorySize, + int GpuId, + string? GpuName, + string? GpuVendorName, + int GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int GpuMaxTextureSize, + sbyte GpuSupportsDrawCallInstancing, + sbyte GpuSupportsRayTracing, + sbyte GpuSupportsComputeShaders, + sbyte GpuSupportsGeometryShaders, + string? GpuVendorId, + sbyte GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel, + string? UnityEditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode + ); } diff --git a/src/Sentry.Unity.iOS/NativeScopeObserver.cs b/src/Sentry.Unity.iOS/NativeScopeObserver.cs index 859ae7d51..7f035822a 100644 --- a/src/Sentry.Unity.iOS/NativeScopeObserver.cs +++ b/src/Sentry.Unity.iOS/NativeScopeObserver.cs @@ -1,46 +1,45 @@ using System; -namespace Sentry.Unity.iOS +namespace Sentry.Unity.iOS; + +public class NativeScopeObserver : ScopeObserver { - public class NativeScopeObserver : ScopeObserver - { - public NativeScopeObserver(string name, SentryOptions options) : base(name, options) { } + public NativeScopeObserver(string name, SentryOptions options) : base(name, options) { } - public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) - { - var level = GetBreadcrumbLevel(breadcrumb.Level); - var timestamp = GetTimestamp(breadcrumb.Timestamp); + public override void AddBreadcrumbImpl(Breadcrumb breadcrumb) + { + var level = GetBreadcrumbLevel(breadcrumb.Level); + var timestamp = GetTimestamp(breadcrumb.Timestamp); - SentryCocoaBridgeProxy.SentryNativeBridgeAddBreadcrumb(timestamp, breadcrumb.Message, breadcrumb.Type, breadcrumb.Category, level); - } + SentryCocoaBridgeProxy.SentryNativeBridgeAddBreadcrumb(timestamp, breadcrumb.Message, breadcrumb.Type, breadcrumb.Category, level); + } - public override void SetExtraImpl(string key, string? value) => - SentryCocoaBridgeProxy.SentryNativeBridgeSetExtra(key, value); + public override void SetExtraImpl(string key, string? value) => + SentryCocoaBridgeProxy.SentryNativeBridgeSetExtra(key, value); - public override void SetTagImpl(string key, string value) => SentryCocoaBridgeProxy.SentryNativeBridgeSetTag(key, value); + public override void SetTagImpl(string key, string value) => SentryCocoaBridgeProxy.SentryNativeBridgeSetTag(key, value); - public override void UnsetTagImpl(string key) => SentryCocoaBridgeProxy.SentryNativeBridgeUnsetTag(key); + public override void UnsetTagImpl(string key) => SentryCocoaBridgeProxy.SentryNativeBridgeUnsetTag(key); - public override void SetUserImpl(SentryUser user) => - SentryCocoaBridgeProxy.SentryNativeBridgeSetUser(user.Email, user.Id, user.IpAddress, user.Username); + public override void SetUserImpl(SentryUser user) => + SentryCocoaBridgeProxy.SentryNativeBridgeSetUser(user.Email, user.Id, user.IpAddress, user.Username); - public override void UnsetUserImpl() => SentryCocoaBridgeProxy.SentryNativeBridgeUnsetUser(); + public override void UnsetUserImpl() => SentryCocoaBridgeProxy.SentryNativeBridgeUnsetUser(); - internal static string GetTimestamp(DateTimeOffset timestamp) => - // "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly. - // https://docs.microsoft.com/en-gb/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip - timestamp.ToString("o"); + internal static string GetTimestamp(DateTimeOffset timestamp) => + // "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly. + // https://docs.microsoft.com/en-gb/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip + timestamp.ToString("o"); - internal static int GetBreadcrumbLevel(BreadcrumbLevel breadcrumbLevel) => - // https://github.com/getsentry/sentry-cocoa/blob/50f955aeb214601dd62b5dae7abdaddc8a1f24d9/Sources/Sentry/Public/SentryDefines.h#L99-L105 - breadcrumbLevel switch - { - BreadcrumbLevel.Debug => 1, - BreadcrumbLevel.Info => 2, - BreadcrumbLevel.Warning => 3, - BreadcrumbLevel.Error => 4, - BreadcrumbLevel.Critical => 5, - _ => 0 - }; - } -} + internal static int GetBreadcrumbLevel(BreadcrumbLevel breadcrumbLevel) => + // https://github.com/getsentry/sentry-cocoa/blob/50f955aeb214601dd62b5dae7abdaddc8a1f24d9/Sources/Sentry/Public/SentryDefines.h#L99-L105 + breadcrumbLevel switch + { + BreadcrumbLevel.Debug => 1, + BreadcrumbLevel.Info => 2, + BreadcrumbLevel.Warning => 3, + BreadcrumbLevel.Error => 4, + BreadcrumbLevel.Critical => 5, + _ => 0 + }; +} \ No newline at end of file diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index e21800dce..6bf59d7ec 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -2,107 +2,106 @@ using System.Runtime.InteropServices; using Sentry.Extensibility; -namespace Sentry.Unity.iOS +namespace Sentry.Unity.iOS; + +/// +/// P/Invoke to SentryNativeBridge.m which communicates with the `sentry-cocoa` SDK. +/// +/// +/// Functions are declared in `SentryNativeBridge.m` +/// +/// +internal static class SentryCocoaBridgeProxy { - /// - /// P/Invoke to SentryNativeBridge.m which communicates with the `sentry-cocoa` SDK. - /// - /// - /// Functions are declared in `SentryNativeBridge.m` - /// - /// - internal static class SentryCocoaBridgeProxy + // Note: used on macOS only + public static bool Init(SentryUnityOptions options) { - // Note: used on macOS only - public static bool Init(SentryUnityOptions options) + if (LoadLibrary() != 1) { - if (LoadLibrary() != 1) - { - return false; - } + return false; + } - var cOptions = OptionsNew(); + var cOptions = OptionsNew(); - // Note: DSN is not null because options.IsValid() must have returned true for this to be called. - OptionsSetString(cOptions, "dsn", options.Dsn!); + // Note: DSN is not null because options.IsValid() must have returned true for this to be called. + OptionsSetString(cOptions, "dsn", options.Dsn!); - if (options.Release is not null) - { - options.DiagnosticLogger?.LogDebug("Setting Release: {0}", options.Release); - OptionsSetString(cOptions, "release", options.Release); - } + if (options.Release is not null) + { + options.DiagnosticLogger?.LogDebug("Setting Release: {0}", options.Release); + OptionsSetString(cOptions, "release", options.Release); + } - if (options.Environment is not null) - { - options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment); - OptionsSetString(cOptions, "environment", options.Environment); - } + if (options.Environment is not null) + { + options.DiagnosticLogger?.LogDebug("Setting Environment: {0}", options.Environment); + OptionsSetString(cOptions, "environment", options.Environment); + } - options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug); - OptionsSetInt(cOptions, "debug", options.Debug ? 1 : 0); + options.DiagnosticLogger?.LogDebug("Setting Debug: {0}", options.Debug); + OptionsSetInt(cOptions, "debug", options.Debug ? 1 : 0); - var diagnosticLevel = options.DiagnosticLevel.ToString().ToLowerInvariant(); - options.DiagnosticLogger?.LogDebug("Setting DiagnosticLevel: {0}", diagnosticLevel); - OptionsSetString(cOptions, "diagnosticLevel", diagnosticLevel); + var diagnosticLevel = options.DiagnosticLevel.ToString().ToLowerInvariant(); + options.DiagnosticLogger?.LogDebug("Setting DiagnosticLevel: {0}", diagnosticLevel); + OptionsSetString(cOptions, "diagnosticLevel", diagnosticLevel); - options.DiagnosticLogger?.LogDebug("Setting SendDefaultPii: {0}", options.SendDefaultPii); - OptionsSetInt(cOptions, "sendDefaultPii", options.SendDefaultPii ? 1 : 0); + options.DiagnosticLogger?.LogDebug("Setting SendDefaultPii: {0}", options.SendDefaultPii); + OptionsSetInt(cOptions, "sendDefaultPii", options.SendDefaultPii ? 1 : 0); - // macOS screenshots currently don't work, because there's no UIKit. Cocoa logs: "Sentry - info:: NO UIKit" - // options.DiagnosticLogger?.LogDebug("Setting AttachScreenshot: {0}", options.AttachScreenshot); - // OptionsSetInt(cOptions, "attachScreenshot", options.AttachScreenshot ? 1 : 0); - OptionsSetInt(cOptions, "attachScreenshot", 0); + // macOS screenshots currently don't work, because there's no UIKit. Cocoa logs: "Sentry - info:: NO UIKit" + // options.DiagnosticLogger?.LogDebug("Setting AttachScreenshot: {0}", options.AttachScreenshot); + // OptionsSetInt(cOptions, "attachScreenshot", options.AttachScreenshot ? 1 : 0); + OptionsSetInt(cOptions, "attachScreenshot", 0); - options.DiagnosticLogger?.LogDebug("Setting MaxBreadcrumbs: {0}", options.MaxBreadcrumbs); - OptionsSetInt(cOptions, "maxBreadcrumbs", options.MaxBreadcrumbs); + options.DiagnosticLogger?.LogDebug("Setting MaxBreadcrumbs: {0}", options.MaxBreadcrumbs); + OptionsSetInt(cOptions, "maxBreadcrumbs", options.MaxBreadcrumbs); - options.DiagnosticLogger?.LogDebug("Setting MaxCacheItems: {0}", options.MaxCacheItems); - OptionsSetInt(cOptions, "maxCacheItems", options.MaxCacheItems); + options.DiagnosticLogger?.LogDebug("Setting MaxCacheItems: {0}", options.MaxCacheItems); + OptionsSetInt(cOptions, "maxCacheItems", options.MaxCacheItems); - StartWithOptions(cOptions); - return true; - } + StartWithOptions(cOptions); + return true; + } - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeLoadLibrary")] - private static extern int LoadLibrary(); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeLoadLibrary")] + private static extern int LoadLibrary(); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsNew")] - private static extern IntPtr OptionsNew(); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsNew")] + private static extern IntPtr OptionsNew(); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetString")] - private static extern void OptionsSetString(IntPtr options, string name, string value); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetString")] + private static extern void OptionsSetString(IntPtr options, string name, string value); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetInt")] - private static extern void OptionsSetInt(IntPtr options, string name, int value); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeOptionsSetInt")] + private static extern void OptionsSetInt(IntPtr options, string name, int value); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeStartWithOptions")] - private static extern void StartWithOptions(IntPtr options); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeStartWithOptions")] + private static extern void StartWithOptions(IntPtr options); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeCrashedLastRun")] - public static extern int CrashedLastRun(); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeCrashedLastRun")] + public static extern int CrashedLastRun(); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeClose")] - public static extern void Close(); + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeClose")] + public static extern void Close(); - [DllImport("__Internal")] - public static extern void SentryNativeBridgeAddBreadcrumb(string timestamp, string? message, string? type, string? category, int level); + [DllImport("__Internal")] + public static extern void SentryNativeBridgeAddBreadcrumb(string timestamp, string? message, string? type, string? category, int level); - [DllImport("__Internal")] - public static extern void SentryNativeBridgeSetExtra(string key, string? value); + [DllImport("__Internal")] + public static extern void SentryNativeBridgeSetExtra(string key, string? value); - [DllImport("__Internal")] - public static extern void SentryNativeBridgeSetTag(string key, string value); + [DllImport("__Internal")] + public static extern void SentryNativeBridgeSetTag(string key, string value); - [DllImport("__Internal")] - public static extern void SentryNativeBridgeUnsetTag(string key); + [DllImport("__Internal")] + public static extern void SentryNativeBridgeUnsetTag(string key); - [DllImport("__Internal")] - public static extern void SentryNativeBridgeSetUser(string? email, string? userId, string? ipAddress, string? username); + [DllImport("__Internal")] + public static extern void SentryNativeBridgeSetUser(string? email, string? userId, string? ipAddress, string? username); - [DllImport("__Internal")] - public static extern void SentryNativeBridgeUnsetUser(); + [DllImport("__Internal")] + public static extern void SentryNativeBridgeUnsetUser(); - [DllImport("__Internal", EntryPoint = "SentryNativeBridgeGetInstallationId")] - public static extern string GetInstallationId(); - } -} + [DllImport("__Internal", EntryPoint = "SentryNativeBridgeGetInstallationId")] + public static extern string GetInstallationId(); +} \ No newline at end of file diff --git a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs index e02a49830..92d4fc345 100644 --- a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs +++ b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs @@ -1,92 +1,89 @@ -using System; using Sentry.Extensibility; -using Sentry.PlatformAbstractions; using Sentry.Unity.Integrations; using UnityEngine; using UnityEngine.Analytics; -namespace Sentry.Unity.iOS +namespace Sentry.Unity.iOS; + +/// +/// Access to the Sentry native support on iOS/macOS. +/// +public static class SentryNativeCocoa { /// - /// Access to the Sentry native support on iOS/macOS. + /// Configures the native support. /// - public static class SentryNativeCocoa + /// The Sentry Unity options to use. + /// Infos about the current Unity environment + public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) => + Configure(options, sentryUnityInfo, ApplicationAdapter.Instance.Platform); + + internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo, RuntimePlatform platform) { - /// - /// Configures the native support. - /// - /// The Sentry Unity options to use. - /// Infos about the current Unity environment - public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo) => - Configure(options, sentryUnityInfo, ApplicationAdapter.Instance.Platform); + options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Cocoa SDK"); - internal static void Configure(SentryUnityOptions options, ISentryUnityInfo sentryUnityInfo, RuntimePlatform platform) + if (!sentryUnityInfo.IsNativeSupportEnabled(options, platform)) { - options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Cocoa SDK"); + options.DiagnosticLogger?.LogDebug("Native support is disabled for: '{0}'", platform); + return; + } - if (!sentryUnityInfo.IsNativeSupportEnabled(options, platform)) + if (platform == RuntimePlatform.IPhonePlayer) + { + options.ScopeObserver = new NativeScopeObserver("iOS", options); + } + else + { + if (!SentryCocoaBridgeProxy.Init(options)) { - options.DiagnosticLogger?.LogDebug("Native support is disabled for: '{0}'", platform); + options.DiagnosticLogger?.LogWarning("Failed to initialize the native SDK"); return; } + options.ScopeObserver = new NativeScopeObserver("macOS", options); + } - if (platform == RuntimePlatform.IPhonePlayer) - { - options.ScopeObserver = new NativeScopeObserver("iOS", options); - } - else - { - if (!SentryCocoaBridgeProxy.Init(options)) - { - options.DiagnosticLogger?.LogWarning("Failed to initialize the native SDK"); - return; - } - options.ScopeObserver = new NativeScopeObserver("macOS", options); - } - - options.NativeContextWriter = new NativeContextWriter(); - options.EnableScopeSync = true; - options.CrashedLastRun = () => - { - var crashedLastRun = SentryCocoaBridgeProxy.CrashedLastRun() == 1; - options.DiagnosticLogger? - .LogDebug("Native SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); + options.NativeContextWriter = new NativeContextWriter(); + options.EnableScopeSync = true; + options.CrashedLastRun = () => + { + var crashedLastRun = SentryCocoaBridgeProxy.CrashedLastRun() == 1; + options.DiagnosticLogger? + .LogDebug("Native SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); - return crashedLastRun; - }; + return crashedLastRun; + }; - options.NativeSupportCloseCallback += () => Close(options.DiagnosticLogger); - if (sentryUnityInfo.IL2CPP) + options.NativeSupportCloseCallback += () => Close(options.DiagnosticLogger); + if (sentryUnityInfo.IL2CPP) + { + options.DefaultUserId = SentryCocoaBridgeProxy.GetInstallationId(); + if (string.IsNullOrEmpty(options.DefaultUserId)) { - options.DefaultUserId = SentryCocoaBridgeProxy.GetInstallationId(); - if (string.IsNullOrEmpty(options.DefaultUserId)) - { - // In case we can't get an installation ID we create one and sync that down to the native layer - options.DiagnosticLogger?.LogDebug("Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); + // In case we can't get an installation ID we create one and sync that down to the native layer + options.DiagnosticLogger?.LogDebug("Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); - // We fall back to Unity's Analytics Session Info: https://docs.unity3d.com/ScriptReference/Analytics.AnalyticsSessionInfo-userId.html - // It's a randomly generated GUID that gets created immediately after installation helping - // to identify the same instance of the game - options.DefaultUserId = AnalyticsSessionInfo.userId; - if (options.DefaultUserId is not null) - { - options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); - } - else - { - options.DiagnosticLogger?.LogDebug("Failed to create new 'Default User ID'."); - } + // We fall back to Unity's Analytics Session Info: https://docs.unity3d.com/ScriptReference/Analytics.AnalyticsSessionInfo-userId.html + // It's a randomly generated GUID that gets created immediately after installation helping + // to identify the same instance of the game + options.DefaultUserId = AnalyticsSessionInfo.userId; + if (options.DefaultUserId is not null) + { + options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); + } + else + { + options.DiagnosticLogger?.LogDebug("Failed to create new 'Default User ID'."); } } } + } - /// - /// Closes the native Cocoa support. - /// - public static void Close(IDiagnosticLogger? logger = null) - { - logger?.LogDebug("Closing the sentry-cocoa SDK"); - SentryCocoaBridgeProxy.Close(); - } + /// + /// Closes the native Cocoa support. + /// + public static void Close(IDiagnosticLogger? logger = null) + { + logger?.LogDebug("Closing the sentry-cocoa SDK"); + SentryCocoaBridgeProxy.Close(); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/ContextWriter.cs b/src/Sentry.Unity/ContextWriter.cs index 25e3409c8..1e6fced8a 100644 --- a/src/Sentry.Unity/ContextWriter.cs +++ b/src/Sentry.Unity/ContextWriter.cs @@ -1,95 +1,94 @@ -namespace Sentry.Unity +namespace Sentry.Unity; + +/// +/// Allows synchronizing Context from .NET to native layers. +/// We're providing a single method that the implementations should override. +/// They can choose to either have the single method directly in native using p/invoke, +/// or use a more fine-grained interface, whatever is best for the platform. +/// +/// +/// WriteScope() is called in a new Task (background thread from a pool). +/// +internal abstract class ContextWriter { - /// - /// Allows synchronizing Context from .NET to native layers. - /// We're providing a single method that the implementations should override. - /// They can choose to either have the single method directly in native using p/invoke, - /// or use a more fine-grained interface, whatever is best for the platform. - /// - /// - /// WriteScope() is called in a new Task (background thread from a pool). - /// - internal abstract class ContextWriter + public void Write(Scope scope) { - public void Write(Scope scope) + if (!scope.Contexts.TryGetValue(Protocol.Unity.Type, out var getThatUnityContext)) { - if (!scope.Contexts.TryGetValue(Protocol.Unity.Type, out var getThatUnityContext)) - { - getThatUnityContext = new Protocol.Unity(); - } - var unityContext = (Protocol.Unity)getThatUnityContext; - - WriteScope( - scope.Contexts.App.StartTime?.ToString("o"), - scope.Contexts.App.BuildType, - scope.Contexts.OperatingSystem.RawDescription, - scope.Contexts.Device.ProcessorCount, - scope.Contexts.Device.CpuDescription, - scope.Contexts.Device.Timezone?.Id, - scope.Contexts.Device.SupportsVibration, - scope.Contexts.Device.Name, - scope.Contexts.Device.Simulator, - scope.Contexts.Device.DeviceUniqueIdentifier, - scope.Contexts.Device.DeviceType, - scope.Contexts.Device.Model, - scope.Contexts.Device.MemorySize, - scope.Contexts.Gpu.Id, - scope.Contexts.Gpu.Name, - scope.Contexts.Gpu.VendorName, - scope.Contexts.Gpu.MemorySize, - scope.Contexts.Gpu.NpotSupport, - scope.Contexts.Gpu.Version, - scope.Contexts.Gpu.ApiType, - scope.Contexts.Gpu.MaxTextureSize, - scope.Contexts.Gpu.SupportsDrawCallInstancing, - scope.Contexts.Gpu.SupportsRayTracing, - scope.Contexts.Gpu.SupportsComputeShaders, - scope.Contexts.Gpu.SupportsGeometryShaders, - scope.Contexts.Gpu.VendorId, - scope.Contexts.Gpu.MultiThreadedRendering, - scope.Contexts.Gpu.GraphicsShaderLevel, - unityContext.EditorVersion, - unityContext.InstallMode, - unityContext.TargetFrameRate, - unityContext.CopyTextureSupport, - unityContext.RenderingThreadingMode - ); + getThatUnityContext = new Protocol.Unity(); } + var unityContext = (Protocol.Unity)getThatUnityContext; - protected abstract void WriteScope( - string? AppStartTime, - string? AppBuildType, - string? OperatingSystemRawDescription, - int? DeviceProcessorCount, - string? DeviceCpuDescription, - string? DeviceTimezone, - bool? DeviceSupportsVibration, - string? DeviceName, - bool? DeviceSimulator, - string? DeviceDeviceUniqueIdentifier, - string? DeviceDeviceType, - string? DeviceModel, - long? DeviceMemorySize, - int? GpuId, - string? GpuName, - string? GpuVendorName, - int? GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int? GpuMaxTextureSize, - bool? GpuSupportsDrawCallInstancing, - bool? GpuSupportsRayTracing, - bool? GpuSupportsComputeShaders, - bool? GpuSupportsGeometryShaders, - string? GpuVendorId, - bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel, - string? UnityEditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode + WriteScope( + scope.Contexts.App.StartTime?.ToString("o"), + scope.Contexts.App.BuildType, + scope.Contexts.OperatingSystem.RawDescription, + scope.Contexts.Device.ProcessorCount, + scope.Contexts.Device.CpuDescription, + scope.Contexts.Device.Timezone?.Id, + scope.Contexts.Device.SupportsVibration, + scope.Contexts.Device.Name, + scope.Contexts.Device.Simulator, + scope.Contexts.Device.DeviceUniqueIdentifier, + scope.Contexts.Device.DeviceType, + scope.Contexts.Device.Model, + scope.Contexts.Device.MemorySize, + scope.Contexts.Gpu.Id, + scope.Contexts.Gpu.Name, + scope.Contexts.Gpu.VendorName, + scope.Contexts.Gpu.MemorySize, + scope.Contexts.Gpu.NpotSupport, + scope.Contexts.Gpu.Version, + scope.Contexts.Gpu.ApiType, + scope.Contexts.Gpu.MaxTextureSize, + scope.Contexts.Gpu.SupportsDrawCallInstancing, + scope.Contexts.Gpu.SupportsRayTracing, + scope.Contexts.Gpu.SupportsComputeShaders, + scope.Contexts.Gpu.SupportsGeometryShaders, + scope.Contexts.Gpu.VendorId, + scope.Contexts.Gpu.MultiThreadedRendering, + scope.Contexts.Gpu.GraphicsShaderLevel, + unityContext.EditorVersion, + unityContext.InstallMode, + unityContext.TargetFrameRate, + unityContext.CopyTextureSupport, + unityContext.RenderingThreadingMode ); } -} + + protected abstract void WriteScope( + string? AppStartTime, + string? AppBuildType, + string? OperatingSystemRawDescription, + int? DeviceProcessorCount, + string? DeviceCpuDescription, + string? DeviceTimezone, + bool? DeviceSupportsVibration, + string? DeviceName, + bool? DeviceSimulator, + string? DeviceDeviceUniqueIdentifier, + string? DeviceDeviceType, + string? DeviceModel, + long? DeviceMemorySize, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel, + string? UnityEditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode + ); +} \ No newline at end of file diff --git a/src/Sentry.Unity/EventCapture.cs b/src/Sentry.Unity/EventCapture.cs index cb3d02260..35dd777f1 100644 --- a/src/Sentry.Unity/EventCapture.cs +++ b/src/Sentry.Unity/EventCapture.cs @@ -1,7 +1,6 @@ -namespace Sentry.Unity +namespace Sentry.Unity; + +internal interface IEventCapture { - internal interface IEventCapture - { - SentryId Capture(SentryEvent sentryEvent); - } -} + SentryId Capture(SentryEvent sentryEvent); +} \ No newline at end of file diff --git a/src/Sentry.Unity/Extensions/JsonExtensions.cs b/src/Sentry.Unity/Extensions/JsonExtensions.cs index 21fa41c34..4fd87e562 100644 --- a/src/Sentry.Unity/Extensions/JsonExtensions.cs +++ b/src/Sentry.Unity/Extensions/JsonExtensions.cs @@ -1,47 +1,46 @@ using System; using System.Text.Json; -namespace Sentry.Unity.Extensions +namespace Sentry.Unity.Extensions; + +internal static class JsonExtensions { - internal static class JsonExtensions + // From Sentry.Internal.Extensions.JsonExtensions + public static JsonElement? GetPropertyOrNull(this JsonElement json, string name) { - // From Sentry.Internal.Extensions.JsonExtensions - public static JsonElement? GetPropertyOrNull(this JsonElement json, string name) + if (json.ValueKind != JsonValueKind.Object) + { + return null; + } + + if (json.TryGetProperty(name, out var result)) { - if (json.ValueKind != JsonValueKind.Object) + if (json.ValueKind == JsonValueKind.Undefined || + json.ValueKind == JsonValueKind.Null) { return null; } - if (json.TryGetProperty(name, out var result)) - { - if (json.ValueKind == JsonValueKind.Undefined || - json.ValueKind == JsonValueKind.Null) - { - return null; - } + return result; + } - return result; - } + return null; + } + public static TEnum? GetEnumOrNull(this JsonElement json, string name) + where TEnum : struct + { + var enumString = json.GetPropertyOrNull(name)?.ToString(); + if (string.IsNullOrWhiteSpace(enumString)) + { return null; } - public static TEnum? GetEnumOrNull(this JsonElement json, string name) - where TEnum : struct + if (!Enum.TryParse(enumString, true, out TEnum value)) { - var enumString = json.GetPropertyOrNull(name)?.ToString(); - if (string.IsNullOrWhiteSpace(enumString)) - { - return null; - } - - if (!Enum.TryParse(enumString, true, out TEnum value)) - { - return null; - } - - return value; + return null; } + + return value; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/ISentryUnityInfo.cs b/src/Sentry.Unity/ISentryUnityInfo.cs index 51792851a..a5788c4eb 100644 --- a/src/Sentry.Unity/ISentryUnityInfo.cs +++ b/src/Sentry.Unity/ISentryUnityInfo.cs @@ -1,42 +1,41 @@ using System; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +public interface ISentryUnityInfo { - public interface ISentryUnityInfo - { - public bool IL2CPP { get; } - public Il2CppMethods? Il2CppMethods { get; } - public bool IsKnownPlatform(); - public bool IsLinux(); - public bool IsNativeSupportEnabled(SentryUnityOptions options, RuntimePlatform platform); - public bool IsSupportedBySentryNative(RuntimePlatform platform); - public string GetDebugImageType(RuntimePlatform platform); - } + public bool IL2CPP { get; } + public Il2CppMethods? Il2CppMethods { get; } + public bool IsKnownPlatform(); + public bool IsLinux(); + public bool IsNativeSupportEnabled(SentryUnityOptions options, RuntimePlatform platform); + public bool IsSupportedBySentryNative(RuntimePlatform platform); + public string GetDebugImageType(RuntimePlatform platform); +} - public class Il2CppMethods +public class Il2CppMethods +{ + public Il2CppMethods( + Il2CppGcHandleGetTarget il2CppGcHandleGetTarget, + Il2CppNativeStackTrace il2CppNativeStackTrace, + Il2CppFree il2CppFree) { - public Il2CppMethods( - Il2CppGcHandleGetTarget il2CppGcHandleGetTarget, - Il2CppNativeStackTrace il2CppNativeStackTrace, - Il2CppFree il2CppFree) - { - Il2CppGcHandleGetTarget = il2CppGcHandleGetTarget; - Il2CppNativeStackTrace = il2CppNativeStackTrace; - Il2CppFree = il2CppFree; - } - - public Il2CppGcHandleGetTarget Il2CppGcHandleGetTarget { get; } - public Il2CppNativeStackTrace Il2CppNativeStackTrace { get; } - public Il2CppFree Il2CppFree { get; } + Il2CppGcHandleGetTarget = il2CppGcHandleGetTarget; + Il2CppNativeStackTrace = il2CppNativeStackTrace; + Il2CppFree = il2CppFree; } - public delegate IntPtr Il2CppGcHandleGetTarget(IntPtr gchandle); - public delegate void Il2CppNativeStackTrace( - IntPtr exc, - out IntPtr addresses, - out int numFrames, - out string? imageUUID, - out string? imageName); - public delegate void Il2CppFree(IntPtr ptr); + public Il2CppGcHandleGetTarget Il2CppGcHandleGetTarget { get; } + public Il2CppNativeStackTrace Il2CppNativeStackTrace { get; } + public Il2CppFree Il2CppFree { get; } } + +public delegate IntPtr Il2CppGcHandleGetTarget(IntPtr gchandle); +public delegate void Il2CppNativeStackTrace( + IntPtr exc, + out IntPtr addresses, + out int numFrames, + out string? imageUUID, + out string? imageName); +public delegate void Il2CppFree(IntPtr ptr); \ No newline at end of file diff --git a/src/Sentry.Unity/Il2CppEventProcessor.cs b/src/Sentry.Unity/Il2CppEventProcessor.cs index 577e5f35c..adf7218df 100644 --- a/src/Sentry.Unity/Il2CppEventProcessor.cs +++ b/src/Sentry.Unity/Il2CppEventProcessor.cs @@ -4,353 +4,351 @@ using System.Runtime.InteropServices; using Sentry.Extensibility; using Sentry.Protocol; -using Sentry.Unity.Integrations; using Sentry.Unity.NativeUtils; using UnityEngine; using Application = UnityEngine.Application; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class UnityIl2CppEventExceptionProcessor : ISentryEventExceptionProcessor { - internal class UnityIl2CppEventExceptionProcessor : ISentryEventExceptionProcessor + private static SentryUnityOptions Options = null!; // private static will be initialized in the constructor + private static ISentryUnityInfo UnityInfo = null!; // private static will be initialized in the constructor + private readonly Il2CppMethods _il2CppMethods; + + public UnityIl2CppEventExceptionProcessor(SentryUnityOptions options, ISentryUnityInfo unityInfo) { - private static SentryUnityOptions Options = null!; // private static will be initialized in the constructor - private static ISentryUnityInfo UnityInfo = null!; // private static will be initialized in the constructor - private readonly Il2CppMethods _il2CppMethods; + Options = options; + UnityInfo = unityInfo; + _il2CppMethods = unityInfo.Il2CppMethods ?? throw new ArgumentNullException(nameof(unityInfo.Il2CppMethods), + "Unity IL2CPP methods are not available."); - public UnityIl2CppEventExceptionProcessor(SentryUnityOptions options, ISentryUnityInfo unityInfo) - { - Options = options; - UnityInfo = unityInfo; - _il2CppMethods = unityInfo.Il2CppMethods ?? throw new ArgumentNullException(nameof(unityInfo.Il2CppMethods), - "Unity IL2CPP methods are not available."); + Options.SdkIntegrationNames.Add("IL2CPPLineNumbers"); + } - Options.SdkIntegrationNames.Add("IL2CPPLineNumbers"); - } + public void Process(Exception incomingException, SentryEvent sentryEvent) + { + Options.DiagnosticLogger?.LogDebug("Running Unity IL2CPP event exception processor on: Event {0}", sentryEvent.EventId); - public void Process(Exception incomingException, SentryEvent sentryEvent) + var sentryExceptions = sentryEvent.SentryExceptions; + if (sentryExceptions == null) { - Options.DiagnosticLogger?.LogDebug("Running Unity IL2CPP event exception processor on: Event {0}", sentryEvent.EventId); + return; + } + + var exceptions = EnumerateChainedExceptions(incomingException); + var usedImages = new HashSet(); - var sentryExceptions = sentryEvent.SentryExceptions; - if (sentryExceptions == null) + // Unity usually produces stack traces with relative offsets in the GameAssembly library. + // However, at least on Unity 2020 built Windows player, the offsets seem to be absolute. + // Therefore, we try to determine which one it is, depending on whether they match the loaded libraries. + // In case they don't we update the offsets to match the GameAssembly library. + foreach (var (sentryException, exception) in sentryExceptions.Zip(exceptions, (se, ex) => (se, ex))) + { + var sentryStacktrace = sentryException.Stacktrace; + if (sentryStacktrace == null) { - return; + // We will only augment an existing stack trace with native + // instructions, so with no stack trace, there is nothing to do + continue; } - var exceptions = EnumerateChainedExceptions(incomingException); - var usedImages = new HashSet(); - - // Unity usually produces stack traces with relative offsets in the GameAssembly library. - // However, at least on Unity 2020 built Windows player, the offsets seem to be absolute. - // Therefore, we try to determine which one it is, depending on whether they match the loaded libraries. - // In case they don't we update the offsets to match the GameAssembly library. - foreach (var (sentryException, exception) in sentryExceptions.Zip(exceptions, (se, ex) => (se, ex))) + sentryStacktrace.AddressAdjustment = + Application.platform == RuntimePlatform.Android + ? InstructionAddressAdjustment.None + : InstructionAddressAdjustment.All; + + var nativeStackTrace = GetNativeStackTrace(exception); + + Options.DiagnosticLogger?.LogDebug("NativeStackTrace Image: '{0}' (UUID: {1})", nativeStackTrace.ImageName, nativeStackTrace.ImageUuid); + + // Unity by definition only builds a single library which we add once to our list of debug images. + // We use this when we encounter stack frames with relative addresses. + // We want to use an address that is definitely outside of any address range used by real libraries. + // Canonical addresses on x64 leave a gap in the middle of the address space, which is unused. + // This is a range of addresses that we should be able to safely use. + // See https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details + var mainLibOffset = long.MaxValue; + DebugImage? mainLibImage = null; + + // TODO do we really want to continue if these two don't match? + // Wouldn't it cause invalid frame info? + var nativeLen = nativeStackTrace.Frames.Length; + var eventLen = sentryStacktrace.Frames.Count; + if (nativeLen != eventLen) { - var sentryStacktrace = sentryException.Stacktrace; - if (sentryStacktrace == null) - { - // We will only augment an existing stack trace with native - // instructions, so with no stack trace, there is nothing to do - continue; - } + Options.DiagnosticLogger?.LogWarning( + "Native and sentry stack trace lengths don't match '({0} != {1})' - this may cause invalid stack traces.", + nativeLen, eventLen); + } - sentryStacktrace.AddressAdjustment = - Application.platform == RuntimePlatform.Android - ? InstructionAddressAdjustment.None - : InstructionAddressAdjustment.All; - - var nativeStackTrace = GetNativeStackTrace(exception); - - Options.DiagnosticLogger?.LogDebug("NativeStackTrace Image: '{0}' (UUID: {1})", nativeStackTrace.ImageName, nativeStackTrace.ImageUuid); - - // Unity by definition only builds a single library which we add once to our list of debug images. - // We use this when we encounter stack frames with relative addresses. - // We want to use an address that is definitely outside of any address range used by real libraries. - // Canonical addresses on x64 leave a gap in the middle of the address space, which is unused. - // This is a range of addresses that we should be able to safely use. - // See https://en.wikipedia.org/wiki/X86-64#Virtual_address_space_details - var mainLibOffset = long.MaxValue; - DebugImage? mainLibImage = null; - - // TODO do we really want to continue if these two don't match? - // Wouldn't it cause invalid frame info? - var nativeLen = nativeStackTrace.Frames.Length; - var eventLen = sentryStacktrace.Frames.Count; - if (nativeLen != eventLen) + var len = Math.Min(eventLen, nativeLen); + for (var i = 0; i < len; i++) + { + // The sentry stack trace is sorted parent->child (caller->callee), + // whereas the native stack trace is sorted from callee to caller. + var frame = sentryStacktrace.Frames[i]; + var nativeFrame = nativeStackTrace.Frames[nativeLen - 1 - i]; + var mainImageUUID = NormalizeUuid(nativeStackTrace.ImageUuid); + + // TODO should we do this for all addresses or only relative ones? + // If the former, we should also update `frame.InstructionAddress` down below. + var instructionAddress = nativeFrame.ToInt64(); + + // We cannot determine whether this frame is a main library frame just from the address + // because even relative address on the frame may correspond to an absolute address of a loaded library. + // Therefore, if the frame package matches known prefixes, we assume it's a GameAssembly frame. + var isMainLibFrame = frame.Package is not null && ( + frame.Package.StartsWith("UnityEngine.", StringComparison.InvariantCultureIgnoreCase) || + frame.Package.StartsWith("Assembly-CSharp", StringComparison.InvariantCultureIgnoreCase) + ); + + string? notes = null; + DebugImage? image = null; + bool? isRelativeAddress = null; + if (!isMainLibFrame) { - Options.DiagnosticLogger?.LogWarning( - "Native and sentry stack trace lengths don't match '({0} != {1})' - this may cause invalid stack traces.", - nativeLen, eventLen); + image = FindDebugImageContainingAddress(instructionAddress); + if (image is null) + { + isRelativeAddress = true; + notes = "because it looks like a relative address."; + // falls through to the next `if (image is null)` + } + else + { + isRelativeAddress = false; + notes = "because it looks like an absolute address inside the range of this debug image."; + } } - var len = Math.Min(eventLen, nativeLen); - for (var i = 0; i < len; i++) + if (image is null) { - // The sentry stack trace is sorted parent->child (caller->callee), - // whereas the native stack trace is sorted from callee to caller. - var frame = sentryStacktrace.Frames[i]; - var nativeFrame = nativeStackTrace.Frames[nativeLen - 1 - i]; - var mainImageUUID = NormalizeUuid(nativeStackTrace.ImageUuid); - - // TODO should we do this for all addresses or only relative ones? - // If the former, we should also update `frame.InstructionAddress` down below. - var instructionAddress = nativeFrame.ToInt64(); - - // We cannot determine whether this frame is a main library frame just from the address - // because even relative address on the frame may correspond to an absolute address of a loaded library. - // Therefore, if the frame package matches known prefixes, we assume it's a GameAssembly frame. - var isMainLibFrame = frame.Package is not null && ( - frame.Package.StartsWith("UnityEngine.", StringComparison.InvariantCultureIgnoreCase) || - frame.Package.StartsWith("Assembly-CSharp", StringComparison.InvariantCultureIgnoreCase) - ); - - string? notes = null; - DebugImage? image = null; - bool? isRelativeAddress = null; - if (!isMainLibFrame) + if (mainImageUUID is null) { - image = FindDebugImageContainingAddress(instructionAddress); - if (image is null) - { - isRelativeAddress = true; - notes = "because it looks like a relative address."; - // falls through to the next `if (image is null)` - } - else - { - isRelativeAddress = false; - notes = "because it looks like an absolute address inside the range of this debug image."; - } + Options.DiagnosticLogger?.LogWarning("Couldn't process stack trace - main image UUID reported as NULL by Unity"); + continue; } - if (image is null) + // First, try to find the image among the loaded ones, otherwise create a dummy one. + mainLibImage ??= DebugImagesSorted.Value.Find((info) => string.Equals(NormalizeUuid(info.Image.DebugId), mainImageUUID))?.Image; + mainLibImage ??= new DebugImage + { + Type = UnityInfo.GetDebugImageType(Application.platform), + // NOTE: il2cpp in some circumstances does not return a correct `ImageName`. + // A null/missing `CodeFile` however would lead to a processing error in sentry. + // Since the code file is not strictly necessary for processing, we just fall back to + // a sentinel value here. + CodeFile = string.IsNullOrEmpty(nativeStackTrace.ImageName) ? "GameAssembly.fallback" : nativeStackTrace.ImageName, + DebugId = mainImageUUID, + ImageAddress = mainLibOffset, + }; + + image = mainLibImage; + if (isMainLibFrame) { - if (mainImageUUID is null) - { - Options.DiagnosticLogger?.LogWarning("Couldn't process stack trace - main image UUID reported as NULL by Unity"); - continue; - } - - // First, try to find the image among the loaded ones, otherwise create a dummy one. - mainLibImage ??= DebugImagesSorted.Value.Find((info) => string.Equals(NormalizeUuid(info.Image.DebugId), mainImageUUID))?.Image; - mainLibImage ??= new DebugImage - { - Type = UnityInfo.GetDebugImageType(Application.platform), - // NOTE: il2cpp in some circumstances does not return a correct `ImageName`. - // A null/missing `CodeFile` however would lead to a processing error in sentry. - // Since the code file is not strictly necessary for processing, we just fall back to - // a sentinel value here. - CodeFile = string.IsNullOrEmpty(nativeStackTrace.ImageName) ? "GameAssembly.fallback" : nativeStackTrace.ImageName, - DebugId = mainImageUUID, - ImageAddress = mainLibOffset, - }; - - image = mainLibImage; - if (isMainLibFrame) - { - notes ??= $"based on frame package name ({frame.Package})."; - } + notes ??= $"based on frame package name ({frame.Package})."; } + } - var imageAddress = image.ImageAddress!.Value; - isRelativeAddress ??= instructionAddress < imageAddress; + var imageAddress = image.ImageAddress!.Value; + isRelativeAddress ??= instructionAddress < imageAddress; - if (isRelativeAddress is true) - { - // Shift the instruction address to be absolute. - instructionAddress += imageAddress; - frame.InstructionAddress = instructionAddress; - } + if (isRelativeAddress is true) + { + // Shift the instruction address to be absolute. + instructionAddress += imageAddress; + frame.InstructionAddress = instructionAddress; + } - // sanity check that the instruction fits inside the range - var logLevel = SentryLevel.Debug; - if (image.ImageSize is not null) + // sanity check that the instruction fits inside the range + var logLevel = SentryLevel.Debug; + if (image.ImageSize is not null) + { + if (instructionAddress < imageAddress || instructionAddress > imageAddress + image.ImageSize) { - if (instructionAddress < imageAddress || instructionAddress > imageAddress + image.ImageSize) - { - logLevel = SentryLevel.Warning; - notes ??= "."; - notes += " However, the instruction address falls out of the range of the debug image."; - } + logLevel = SentryLevel.Warning; + notes ??= "."; + notes += " However, the instruction address falls out of the range of the debug image."; } + } - Options.DiagnosticLogger?.Log(logLevel, "Stack frame '{0}' at {1:X8} (originally {2:X8}) belongs to {3} {4}", - null, frame.Function, instructionAddress, nativeFrame.ToInt64(), image.CodeFile, notes ?? ""); + Options.DiagnosticLogger?.Log(logLevel, "Stack frame '{0}' at {1:X8} (originally {2:X8}) belongs to {3} {4}", + null, frame.Function, instructionAddress, nativeFrame.ToInt64(), image.CodeFile, notes ?? ""); - _ = usedImages.Add(image); - } + _ = usedImages.Add(image); } - - sentryEvent.DebugImages ??= new List(); - sentryEvent.DebugImages.AddRange(usedImages); } - // Normalizes Debug Image UUID so that we can compare the ones coming from - // native (contains dashes, all lower-case) & what Unity gives us (no dashes, uppercase). - // On Linux, the image also has shorter UUID coming from Unity, e.g. 3028cb80b0712541, - // while native image UUID we get is 3028cb80-b071-2541-0000-000000000000. - internal static string? NormalizeUuid(string? value) - { - if (value is null) - { - return null; - } + sentryEvent.DebugImages ??= new List(); + sentryEvent.DebugImages.AddRange(usedImages); + } - value = value.ToLowerInvariant(); - value = value.Replace("-0000-000000000000", ""); - return value.Replace("-", ""); + // Normalizes Debug Image UUID so that we can compare the ones coming from + // native (contains dashes, all lower-case) & what Unity gives us (no dashes, uppercase). + // On Linux, the image also has shorter UUID coming from Unity, e.g. 3028cb80b0712541, + // while native image UUID we get is 3028cb80-b071-2541-0000-000000000000. + internal static string? NormalizeUuid(string? value) + { + if (value is null) + { + return null; } - private class DebugImageInfo - { - public readonly DebugImage Image; - public readonly long? StartAddress; - public readonly long? EndAddress; + value = value.ToLowerInvariant(); + value = value.Replace("-0000-000000000000", ""); + return value.Replace("-", ""); + } - public DebugImageInfo(DebugImage image) - { - Image = image; - StartAddress = image.ImageAddress!.Value; - EndAddress = StartAddress + image.ImageSize!.Value; - } + private class DebugImageInfo + { + public readonly DebugImage Image; + public readonly long? StartAddress; + public readonly long? EndAddress; - public bool ContainsAddress(long address) => StartAddress <= address && address <= EndAddress; + public DebugImageInfo(DebugImage image) + { + Image = image; + StartAddress = image.ImageAddress!.Value; + EndAddress = StartAddress + image.ImageSize!.Value; } - private static readonly Lazy> DebugImagesSorted = new(() => - { - var result = new List(); + public bool ContainsAddress(long address) => StartAddress <= address && address <= EndAddress; + } - // Only on platforms where we actually use sentry-native. - if (UnityInfo.IsSupportedBySentryNative(Application.platform) && - UnityInfo.IsNativeSupportEnabled(Options, Application.platform)) + private static readonly Lazy> DebugImagesSorted = new(() => + { + var result = new List(); + + // Only on platforms where we actually use sentry-native. + if (UnityInfo.IsSupportedBySentryNative(Application.platform) && + UnityInfo.IsNativeSupportEnabled(Options, Application.platform)) + { + var nativeDebugImages = C.DebugImages.Value; + foreach (var image in nativeDebugImages) { - var nativeDebugImages = C.DebugImages.Value; - foreach (var image in nativeDebugImages) + if (image.ImageSize is null) { - if (image.ImageSize is null) - { - Options.DiagnosticLogger?.Log(SentryLevel.Debug, - "Skipping debug image '{0}' (CodeId {1} | DebugId: {2}) because its size is NULL", - null, image.CodeFile, image.CodeId, image.DebugId); - continue; - } + Options.DiagnosticLogger?.Log(SentryLevel.Debug, + "Skipping debug image '{0}' (CodeId {1} | DebugId: {2}) because its size is NULL", + null, image.CodeFile, image.CodeId, image.DebugId); + continue; + } - var info = new DebugImageInfo(image); - var i = 0; - for (; i < result.Count; i++) + var info = new DebugImageInfo(image); + var i = 0; + for (; i < result.Count; i++) + { + if (info.StartAddress < result[i].StartAddress) { - if (info.StartAddress < result[i].StartAddress) - { - // insert at index `i`, all the rest have a larger start address - break; - } + // insert at index `i`, all the rest have a larger start address + break; } - result.Insert(i, info); - - Options.DiagnosticLogger?.Log(SentryLevel.Debug, - "Found debug image '{0}' (CodeId {1} | DebugId: {2}) with addresses between {3:X8} and {4:X8}", - null, image.CodeFile, image.CodeId, image.DebugId, info.StartAddress, info.EndAddress); } + result.Insert(i, info); + + Options.DiagnosticLogger?.Log(SentryLevel.Debug, + "Found debug image '{0}' (CodeId {1} | DebugId: {2}) with addresses between {3:X8} and {4:X8}", + null, image.CodeFile, image.CodeId, image.DebugId, info.StartAddress, info.EndAddress); } - return result; - }); + } + return result; + }); + + private static DebugImage? FindDebugImageContainingAddress(long instructionAddress) + { + var list = DebugImagesSorted.Value; - private static DebugImage? FindDebugImageContainingAddress(long instructionAddress) + // Manual binary search implementation on "value in range". + var lowerBound = 0; + var upperBound = list.Count - 1; + while (lowerBound <= upperBound) { - var list = DebugImagesSorted.Value; + var mid = (lowerBound + upperBound) / 2; + var info = list[mid]; - // Manual binary search implementation on "value in range". - var lowerBound = 0; - var upperBound = list.Count - 1; - while (lowerBound <= upperBound) + if (info.StartAddress <= instructionAddress) { - var mid = (lowerBound + upperBound) / 2; - var info = list[mid]; - - if (info.StartAddress <= instructionAddress) - { - if (instructionAddress <= info.EndAddress) - { - return info.Image; - } - lowerBound = mid + 1; - } - else + if (instructionAddress <= info.EndAddress) { - upperBound = mid - 1; + return info.Image; } + lowerBound = mid + 1; + } + else + { + upperBound = mid - 1; } - return null; } + return null; + } - // This is the same logic as `MainExceptionProcessor` uses to create the `SentryEvent.SentryExceptions` list. - // It yields chained Exceptions in innermost to outer Exception order. - private IEnumerable EnumerateChainedExceptions(Exception exception) + // This is the same logic as `MainExceptionProcessor` uses to create the `SentryEvent.SentryExceptions` list. + // It yields chained Exceptions in innermost to outer Exception order. + private IEnumerable EnumerateChainedExceptions(Exception exception) + { + if (exception is AggregateException ae) { - if (exception is AggregateException ae) + foreach (var inner in ae.InnerExceptions.SelectMany(EnumerateChainedExceptions)) { - foreach (var inner in ae.InnerExceptions.SelectMany(EnumerateChainedExceptions)) - { - yield return inner; - } + yield return inner; } - else if (exception.InnerException != null) + } + else if (exception.InnerException != null) + { + foreach (var inner in EnumerateChainedExceptions(exception.InnerException)) { - foreach (var inner in EnumerateChainedExceptions(exception.InnerException)) - { - yield return inner; - } + yield return inner; } - yield return exception; } + yield return exception; + } - private NativeStackTrace GetNativeStackTrace(Exception e) + private NativeStackTrace GetNativeStackTrace(Exception e) + { + // Create a `GCHandle` for the exception, which we can then use to + // essentially get a pointer to the underlying `Il2CppException` C++ object. + var gch = GCHandle.Alloc(e); + // The `il2cpp_native_stack_trace` allocates and writes the native + // instruction pointers to the `addresses`/`numFrames` out-parameters. + var addresses = IntPtr.Zero; + try { - // Create a `GCHandle` for the exception, which we can then use to - // essentially get a pointer to the underlying `Il2CppException` C++ object. - var gch = GCHandle.Alloc(e); - // The `il2cpp_native_stack_trace` allocates and writes the native - // instruction pointers to the `addresses`/`numFrames` out-parameters. - var addresses = IntPtr.Zero; - try - { - var gchandle = GCHandle.ToIntPtr(gch); - var addr = _il2CppMethods.Il2CppGcHandleGetTarget(gchandle); + var gchandle = GCHandle.ToIntPtr(gch); + var addr = _il2CppMethods.Il2CppGcHandleGetTarget(gchandle); - var numFrames = 0; - string? imageUuid = null; - string? imageName = null; - _il2CppMethods.Il2CppNativeStackTrace(addr, out addresses, out numFrames, out imageUuid, out imageName); + var numFrames = 0; + string? imageUuid = null; + string? imageName = null; + _il2CppMethods.Il2CppNativeStackTrace(addr, out addresses, out numFrames, out imageUuid, out imageName); - // Convert the C-Array to a managed "C#" Array, and free the underlying memory. - var frames = new IntPtr[numFrames]; - Marshal.Copy(addresses, frames, 0, numFrames); + // Convert the C-Array to a managed "C#" Array, and free the underlying memory. + var frames = new IntPtr[numFrames]; + Marshal.Copy(addresses, frames, 0, numFrames); - return new NativeStackTrace - { - Frames = frames, - ImageUuid = imageUuid, - ImageName = imageName, - }; - } - finally + return new NativeStackTrace { - // We are done with the `GCHandle`. - gch.Free(); + Frames = frames, + ImageUuid = imageUuid, + ImageName = imageName, + }; + } + finally + { + // We are done with the `GCHandle`. + gch.Free(); - if (addresses != IntPtr.Zero) - { - _il2CppMethods.Il2CppFree(addresses); - } + if (addresses != IntPtr.Zero) + { + _il2CppMethods.Il2CppFree(addresses); } } } - - internal class NativeStackTrace - { - public IntPtr[] Frames { get; set; } = Array.Empty(); - public string? ImageUuid { get; set; } - public string? ImageName { get; set; } - } } + +internal class NativeStackTrace +{ + public IntPtr[] Frames { get; set; } = Array.Empty(); + public string? ImageUuid { get; set; } + public string? ImageName { get; set; } +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/AnrIntegration.cs b/src/Sentry.Unity/Integrations/AnrIntegration.cs index 21b97d9d3..4b7b4236e 100644 --- a/src/Sentry.Unity/Integrations/AnrIntegration.cs +++ b/src/Sentry.Unity/Integrations/AnrIntegration.cs @@ -6,197 +6,196 @@ using Sentry.Integrations; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class AnrIntegration : ISdkIntegration { - internal class AnrIntegration : ISdkIntegration - { - private static readonly object Lock = new(); - private static AnrWatchDog? Watchdog; - private readonly SentryMonoBehaviour _monoBehaviour; + private static readonly object Lock = new(); + private static AnrWatchDog? Watchdog; + private readonly SentryMonoBehaviour _monoBehaviour; - public AnrIntegration(SentryMonoBehaviour monoBehaviour) - { - _monoBehaviour = monoBehaviour; - } + public AnrIntegration(SentryMonoBehaviour monoBehaviour) + { + _monoBehaviour = monoBehaviour; + } - public void Register(IHub hub, SentryOptions sentryOptions) + public void Register(IHub hub, SentryOptions sentryOptions) + { + var options = (SentryUnityOptions)sentryOptions; + lock (Lock) { - var options = (SentryUnityOptions)sentryOptions; - lock (Lock) + if (Watchdog is null) { - if (Watchdog is null) + if (options.MultiThreading) + { + Watchdog = new AnrWatchDogMultiThreaded(options.DiagnosticLogger, + _monoBehaviour, + options.AnrTimeout); + } + else { - if (options.MultiThreading) - { - Watchdog = new AnrWatchDogMultiThreaded(options.DiagnosticLogger, - _monoBehaviour, - options.AnrTimeout); - } - else - { - Watchdog = new AnrWatchDogSingleThreaded(options.DiagnosticLogger, - _monoBehaviour, - options.AnrTimeout); - } + Watchdog = new AnrWatchDogSingleThreaded(options.DiagnosticLogger, + _monoBehaviour, + options.AnrTimeout); } } - Watchdog.OnApplicationNotResponding += (_, e) => hub.CaptureException(e); } + Watchdog.OnApplicationNotResponding += (_, e) => hub.CaptureException(e); } +} - internal abstract class AnrWatchDog +internal abstract class AnrWatchDog +{ + protected readonly int DetectionTimeoutMs; + // Note: we don't sleep for the whole detection timeout or we wouldn't capture if the ANR started later. + protected readonly int SleepIntervalMs; + protected readonly IDiagnosticLogger? Logger; + protected readonly SentryMonoBehaviour MonoBehaviour; + internal event EventHandler OnApplicationNotResponding = delegate { }; + protected bool Paused { get; private set; } = false; + + internal AnrWatchDog(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, TimeSpan detectionTimeout) { - protected readonly int DetectionTimeoutMs; - // Note: we don't sleep for the whole detection timeout or we wouldn't capture if the ANR started later. - protected readonly int SleepIntervalMs; - protected readonly IDiagnosticLogger? Logger; - protected readonly SentryMonoBehaviour MonoBehaviour; - internal event EventHandler OnApplicationNotResponding = delegate { }; - protected bool Paused { get; private set; } = false; - - internal AnrWatchDog(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, TimeSpan detectionTimeout) - { - MonoBehaviour = monoBehaviour; - Logger = logger; - DetectionTimeoutMs = (int)detectionTimeout.TotalMilliseconds; - SleepIntervalMs = Math.Max(1, DetectionTimeoutMs / 5); + MonoBehaviour = monoBehaviour; + Logger = logger; + DetectionTimeoutMs = (int)detectionTimeout.TotalMilliseconds; + SleepIntervalMs = Math.Max(1, DetectionTimeoutMs / 5); - MonoBehaviour.ApplicationPausing += () => Paused = true; - MonoBehaviour.ApplicationResuming += () => Paused = false; + MonoBehaviour.ApplicationPausing += () => Paused = true; + MonoBehaviour.ApplicationResuming += () => Paused = false; - // Stop when the app is being shut down. - MonoBehaviour.Application.Quitting += () => Stop(); - } + // Stop when the app is being shut down. + MonoBehaviour.Application.Quitting += () => Stop(); + } - internal abstract void Stop(bool wait = false); + internal abstract void Stop(bool wait = false); - protected void Report() + protected void Report() + { + // Don't report events while in the background. + if (!Paused) { - // Don't report events while in the background. - if (!Paused) - { - var message = $"Application not responding for at least {DetectionTimeoutMs} ms."; - Logger?.LogInfo("Detected an ANR event: {0}", message); - OnApplicationNotResponding?.Invoke(this, new ApplicationNotResponding(message)); - } + var message = $"Application not responding for at least {DetectionTimeoutMs} ms."; + Logger?.LogInfo("Detected an ANR event: {0}", message); + OnApplicationNotResponding?.Invoke(this, new ApplicationNotResponding(message)); } } +} - internal class AnrWatchDogMultiThreaded : AnrWatchDog - { - private int _ticksSinceUiUpdate; // how many _sleepIntervalMs have elapsed since the UI updated last time - private bool _reported; // don't report the same ANR instance multiple times - private bool _stop; - private readonly Thread _thread = null!; +internal class AnrWatchDogMultiThreaded : AnrWatchDog +{ + private int _ticksSinceUiUpdate; // how many _sleepIntervalMs have elapsed since the UI updated last time + private bool _reported; // don't report the same ANR instance multiple times + private bool _stop; + private readonly Thread _thread = null!; - internal AnrWatchDogMultiThreaded(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, TimeSpan detectionTimeout) - : base(logger, monoBehaviour, detectionTimeout) + internal AnrWatchDogMultiThreaded(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, TimeSpan detectionTimeout) + : base(logger, monoBehaviour, detectionTimeout) + { + _thread = new Thread(Run) { - _thread = new Thread(Run) - { - Name = "Sentry-ANR-WatchDog", - IsBackground = true, // do not block on app shutdown - Priority = System.Threading.ThreadPriority.BelowNormal, - }; - _thread.Start(); - - // Update the UI status periodically by running a coroutine on the UI thread - MonoBehaviour.StartCoroutine(UpdateUiStatus()); - } + Name = "Sentry-ANR-WatchDog", + IsBackground = true, // do not block on app shutdown + Priority = System.Threading.ThreadPriority.BelowNormal, + }; + _thread.Start(); + + // Update the UI status periodically by running a coroutine on the UI thread + MonoBehaviour.StartCoroutine(UpdateUiStatus()); + } - internal override void Stop(bool wait = false) + internal override void Stop(bool wait = false) + { + _stop = true; + if (wait) { - _stop = true; - if (wait) - { - _thread.Join(); - } + _thread.Join(); } + } - private IEnumerator UpdateUiStatus() - { - var waitForSeconds = new WaitForSecondsRealtime((float)SleepIntervalMs / 1000); + private IEnumerator UpdateUiStatus() + { + var waitForSeconds = new WaitForSecondsRealtime((float)SleepIntervalMs / 1000); + yield return waitForSeconds; + while (!_stop) + { + _ticksSinceUiUpdate = 0; + _reported = false; yield return waitForSeconds; - while (!_stop) - { - _ticksSinceUiUpdate = 0; - _reported = false; - yield return waitForSeconds; - } } + } - private void Run() + private void Run() + { + try { - try - { - var reportThreshold = DetectionTimeoutMs / SleepIntervalMs; + var reportThreshold = DetectionTimeoutMs / SleepIntervalMs; + + Logger?.Log(SentryLevel.Info, + "Starting an ANR WatchDog - detection timeout: {0} ms, check every {1} ms => report after {2} failed checks", + null, DetectionTimeoutMs, SleepIntervalMs, reportThreshold); - Logger?.Log(SentryLevel.Info, - "Starting an ANR WatchDog - detection timeout: {0} ms, check every {1} ms => report after {2} failed checks", - null, DetectionTimeoutMs, SleepIntervalMs, reportThreshold); + while (!_stop) + { + _ticksSinceUiUpdate++; + Thread.Sleep(SleepIntervalMs); - while (!_stop) + if (Paused) { - _ticksSinceUiUpdate++; - Thread.Sleep(SleepIntervalMs); - - if (Paused) - { - _ticksSinceUiUpdate = 0; - } - else if (_ticksSinceUiUpdate >= reportThreshold && !_reported) - { - Report(); - _reported = true; - } + _ticksSinceUiUpdate = 0; + } + else if (_ticksSinceUiUpdate >= reportThreshold && !_reported) + { + Report(); + _reported = true; } } - catch (ThreadAbortException e) - { - Logger?.Log(SentryLevel.Debug, "ANR watchdog thread aborted.", e); - } - catch (Exception e) - { - Logger?.Log(SentryLevel.Error, "Exception in the ANR watchdog.", e); - } + } + catch (ThreadAbortException e) + { + Logger?.Log(SentryLevel.Debug, "ANR watchdog thread aborted.", e); + } + catch (Exception e) + { + Logger?.Log(SentryLevel.Error, "Exception in the ANR watchdog.", e); } } +} - internal class AnrWatchDogSingleThreaded : AnrWatchDog - { - private readonly Stopwatch _watch = new(); - private bool _stop; +internal class AnrWatchDogSingleThreaded : AnrWatchDog +{ + private readonly Stopwatch _watch = new(); + private bool _stop; - internal AnrWatchDogSingleThreaded(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, TimeSpan detectionTimeout) - : base(logger, monoBehaviour, detectionTimeout) - { - // Check the UI status periodically by running a coroutine on the UI thread and checking the elapsed time - _watch.Start(); - MonoBehaviour.StartCoroutine(UpdateUiStatus()); - } + internal AnrWatchDogSingleThreaded(IDiagnosticLogger? logger, SentryMonoBehaviour monoBehaviour, TimeSpan detectionTimeout) + : base(logger, monoBehaviour, detectionTimeout) + { + // Check the UI status periodically by running a coroutine on the UI thread and checking the elapsed time + _watch.Start(); + MonoBehaviour.StartCoroutine(UpdateUiStatus()); + } - internal override void Stop(bool wait = false) => _stop = true; + internal override void Stop(bool wait = false) => _stop = true; - private IEnumerator UpdateUiStatus() + private IEnumerator UpdateUiStatus() + { + var waitForSeconds = new WaitForSecondsRealtime((float)SleepIntervalMs / 1000); + while (!_stop) { - var waitForSeconds = new WaitForSecondsRealtime((float)SleepIntervalMs / 1000); - while (!_stop) + if (_watch.ElapsedMilliseconds >= DetectionTimeoutMs) { - if (_watch.ElapsedMilliseconds >= DetectionTimeoutMs) - { - Report(); - } - _watch.Restart(); - yield return waitForSeconds; + Report(); } + _watch.Restart(); + yield return waitForSeconds; } } - - internal class ApplicationNotResponding : Exception - { - internal ApplicationNotResponding() : base() { } - internal ApplicationNotResponding(string message) : base(message) { } - internal ApplicationNotResponding(string message, Exception innerException) : base(message, innerException) { } - } } + +internal class ApplicationNotResponding : Exception +{ + internal ApplicationNotResponding() : base() { } + internal ApplicationNotResponding(string message) : base(message) { } + internal ApplicationNotResponding(string message, Exception innerException) : base(message, innerException) { } +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/IApplication.cs b/src/Sentry.Unity/Integrations/IApplication.cs index c67d513cd..c8492705c 100644 --- a/src/Sentry.Unity/Integrations/IApplication.cs +++ b/src/Sentry.Unity/Integrations/IApplication.cs @@ -2,59 +2,58 @@ using UnityEngine; using UnityEngine.SceneManagement; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal interface IApplication { - internal interface IApplication - { - event Application.LogCallback LogMessageReceived; - event Action Quitting; - string ActiveSceneName { get; } - bool IsEditor { get; } - string ProductName { get; } - string Version { get; } - string BuildGUID { get; } - string UnityVersion { get; } - string PersistentDataPath { get; } - RuntimePlatform Platform { get; } - } + event Application.LogCallback LogMessageReceived; + event Action Quitting; + string ActiveSceneName { get; } + bool IsEditor { get; } + string ProductName { get; } + string Version { get; } + string BuildGUID { get; } + string UnityVersion { get; } + string PersistentDataPath { get; } + RuntimePlatform Platform { get; } +} - /// - /// Semi-internal class to be used by other Sentry.Unity assemblies - /// - public sealed class ApplicationAdapter : IApplication - { - public static readonly ApplicationAdapter Instance = new(); +/// +/// Semi-internal class to be used by other Sentry.Unity assemblies +/// +public sealed class ApplicationAdapter : IApplication +{ + public static readonly ApplicationAdapter Instance = new(); - private ApplicationAdapter() - { - Application.logMessageReceivedThreaded += OnLogMessageReceived; - Application.quitting += OnQuitting; - } + private ApplicationAdapter() + { + Application.logMessageReceivedThreaded += OnLogMessageReceived; + Application.quitting += OnQuitting; + } - public event Application.LogCallback? LogMessageReceived; + public event Application.LogCallback? LogMessageReceived; - public event Action? Quitting; + public event Action? Quitting; - public string ActiveSceneName => SceneManager.GetActiveScene().name; + public string ActiveSceneName => SceneManager.GetActiveScene().name; - public bool IsEditor => Application.isEditor; + public bool IsEditor => Application.isEditor; - public string ProductName => Application.productName; + public string ProductName => Application.productName; - public string Version => Application.version; + public string Version => Application.version; - public string BuildGUID => Application.buildGUID; + public string BuildGUID => Application.buildGUID; - public string UnityVersion => Application.unityVersion; + public string UnityVersion => Application.unityVersion; - public string PersistentDataPath => Application.persistentDataPath; + public string PersistentDataPath => Application.persistentDataPath; - public RuntimePlatform Platform => Application.platform; + public RuntimePlatform Platform => Application.platform; - private void OnLogMessageReceived(string condition, string stackTrace, LogType type) - => LogMessageReceived?.Invoke(condition, stackTrace, type); + private void OnLogMessageReceived(string condition, string stackTrace, LogType type) + => LogMessageReceived?.Invoke(condition, stackTrace, type); - private void OnQuitting() - => Quitting?.Invoke(); - } -} + private void OnQuitting() + => Quitting?.Invoke(); +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/ISceneManager.cs b/src/Sentry.Unity/Integrations/ISceneManager.cs index 3e1abe6d1..343a2fa10 100644 --- a/src/Sentry.Unity/Integrations/ISceneManager.cs +++ b/src/Sentry.Unity/Integrations/ISceneManager.cs @@ -1,44 +1,43 @@ using System; using UnityEngine.SceneManagement; -namespace Sentry.Unity +namespace Sentry.Unity; + +// Accessors if UnityEngine.Scene do P/Invoke so we should map what we need only +internal readonly struct SceneAdapter { - // Accessors if UnityEngine.Scene do P/Invoke so we should map what we need only - internal readonly struct SceneAdapter - { - public string Name { get; } - public SceneAdapter(string name) => Name = name; - } + public string Name { get; } + public SceneAdapter(string name) => Name = name; +} - internal interface ISceneManager - { - public event Action SceneLoaded; - public event Action SceneUnloaded; - public event Action ActiveSceneChanged; - } +internal interface ISceneManager +{ + public event Action SceneLoaded; + public event Action SceneUnloaded; + public event Action ActiveSceneChanged; +} + +internal sealed class SceneManagerAdapter : ISceneManager +{ + public event Action? SceneLoaded; + public event Action? SceneUnloaded; + public event Action? ActiveSceneChanged; + + public static readonly SceneManagerAdapter Instance = new(); - internal sealed class SceneManagerAdapter : ISceneManager + private SceneManagerAdapter() { - public event Action? SceneLoaded; - public event Action? SceneUnloaded; - public event Action? ActiveSceneChanged; + SceneManager.sceneLoaded += (scene, mode) + => SceneLoaded?.Invoke(new SceneAdapter(scene.name), mode); - public static readonly SceneManagerAdapter Instance = new(); + SceneManager.sceneUnloaded += scene + => SceneUnloaded?.Invoke(new SceneAdapter(scene.name)); - private SceneManagerAdapter() + SceneManager.activeSceneChanged += (sceneFrom, sceneTo) => { - SceneManager.sceneLoaded += (scene, mode) - => SceneLoaded?.Invoke(new SceneAdapter(scene.name), mode); - - SceneManager.sceneUnloaded += scene - => SceneUnloaded?.Invoke(new SceneAdapter(scene.name)); - - SceneManager.activeSceneChanged += (sceneFrom, sceneTo) => - { - ActiveSceneChanged?.Invoke( - new SceneAdapter(sceneFrom.name), - new SceneAdapter(sceneTo.name)); - }; - } + ActiveSceneChanged?.Invoke( + new SceneAdapter(sceneFrom.name), + new SceneAdapter(sceneTo.name)); + }; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs b/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs index 4d904b868..f71f2d1c5 100644 --- a/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs +++ b/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs @@ -1,64 +1,63 @@ using Sentry.Integrations; using UnityEngine.SceneManagement; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal class SceneManagerIntegration : ISdkIntegration { - internal class SceneManagerIntegration : ISdkIntegration + private readonly ISceneManager _sceneManager; + + public SceneManagerIntegration() : this(SceneManagerAdapter.Instance) { - private readonly ISceneManager _sceneManager; + } - public SceneManagerIntegration() : this(SceneManagerAdapter.Instance) - { - } + internal SceneManagerIntegration(ISceneManager sceneManager) => _sceneManager = sceneManager; - internal SceneManagerIntegration(ISceneManager sceneManager) => _sceneManager = sceneManager; + public void Register(IHub hub, SentryOptions options) + { + _sceneManager.SceneLoaded += OnSceneManagerOnSceneLoaded; + _sceneManager.SceneUnloaded += SceneManagerOnSceneUnloaded; + _sceneManager.ActiveSceneChanged += SceneManagerOnActiveSceneChanged; - public void Register(IHub hub, SentryOptions options) + void OnSceneManagerOnSceneLoaded(SceneAdapter scene, LoadSceneMode mode) { - _sceneManager.SceneLoaded += OnSceneManagerOnSceneLoaded; - _sceneManager.SceneUnloaded += SceneManagerOnSceneUnloaded; - _sceneManager.ActiveSceneChanged += SceneManagerOnActiveSceneChanged; - - void OnSceneManagerOnSceneLoaded(SceneAdapter scene, LoadSceneMode mode) + // In case Hub is disabled, avoid allocations below + if (!hub.IsEnabled) { - // In case Hub is disabled, avoid allocations below - if (!hub.IsEnabled) - { - return; - } - - hub.AddBreadcrumb( - message: $"Scene '{scene.Name}' was loaded", - category: "scene.loaded"); + return; } - void SceneManagerOnSceneUnloaded(SceneAdapter scene) - { - // In case Hub is disabled, avoid allocations below - if (!hub.IsEnabled) - { - return; - } + hub.AddBreadcrumb( + message: $"Scene '{scene.Name}' was loaded", + category: "scene.loaded"); + } - hub.AddBreadcrumb( - message: $"Scene '{scene.Name}' was unloaded", - category: "scene.unloaded"); + void SceneManagerOnSceneUnloaded(SceneAdapter scene) + { + // In case Hub is disabled, avoid allocations below + if (!hub.IsEnabled) + { + return; } - void SceneManagerOnActiveSceneChanged(SceneAdapter fromScene, SceneAdapter toScene) - { - // In case Hub is disabled, avoid allocations below - if (!hub.IsEnabled) - { - return; - } + hub.AddBreadcrumb( + message: $"Scene '{scene.Name}' was unloaded", + category: "scene.unloaded"); + } - hub.AddBreadcrumb( - message: fromScene.Name == null - ? $"Changed active scene to '{toScene.Name}'" - : $"Changed active scene '{fromScene.Name}' to '{toScene.Name}'", - category: "scene.changed"); + void SceneManagerOnActiveSceneChanged(SceneAdapter fromScene, SceneAdapter toScene) + { + // In case Hub is disabled, avoid allocations below + if (!hub.IsEnabled) + { + return; } + + hub.AddBreadcrumb( + message: fromScene.Name == null + ? $"Changed active scene to '{toScene.Name}'" + : $"Changed active scene '{fromScene.Name}' to '{toScene.Name}'", + category: "scene.changed"); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/SessionIntegration.cs b/src/Sentry.Unity/Integrations/SessionIntegration.cs index 8bdcc2ca1..4202cc920 100644 --- a/src/Sentry.Unity/Integrations/SessionIntegration.cs +++ b/src/Sentry.Unity/Integrations/SessionIntegration.cs @@ -1,36 +1,35 @@ using Sentry.Extensibility; using Sentry.Integrations; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal class SessionIntegration : ISdkIntegration { - internal class SessionIntegration : ISdkIntegration + private readonly SentryMonoBehaviour _sentryMonoBehaviour; + + public SessionIntegration(SentryMonoBehaviour sentryMonoBehaviour) { - private readonly SentryMonoBehaviour _sentryMonoBehaviour; + _sentryMonoBehaviour = sentryMonoBehaviour; + } - public SessionIntegration(SentryMonoBehaviour sentryMonoBehaviour) + public void Register(IHub hub, SentryOptions options) + { + if (!options.AutoSessionTracking) { - _sentryMonoBehaviour = sentryMonoBehaviour; + return; } - public void Register(IHub hub, SentryOptions options) - { - if (!options.AutoSessionTracking) - { - return; - } + options.DiagnosticLogger?.LogDebug("Registering Session integration."); - options.DiagnosticLogger?.LogDebug("Registering Session integration."); - - _sentryMonoBehaviour.ApplicationResuming += () => - { - options.DiagnosticLogger?.LogDebug("Resuming session."); - hub.ResumeSession(); - }; - _sentryMonoBehaviour.ApplicationPausing += () => - { - options.DiagnosticLogger?.LogDebug("Pausing session."); - hub.PauseSession(); - }; - } + _sentryMonoBehaviour.ApplicationResuming += () => + { + options.DiagnosticLogger?.LogDebug("Resuming session."); + hub.ResumeSession(); + }; + _sentryMonoBehaviour.ApplicationPausing += () => + { + options.DiagnosticLogger?.LogDebug("Pausing session."); + hub.PauseSession(); + }; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs b/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs index aed77a48e..f5b8f3832 100644 --- a/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs +++ b/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs @@ -1,14 +1,13 @@ using System; using Sentry.Extensibility; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal class UnityBadGatewayExceptionFilter : IExceptionFilter { - internal class UnityBadGatewayExceptionFilter : IExceptionFilter - { - internal const string Message = "Error: HTTP/1.1 502 Bad Gateway"; + internal const string Message = "Error: HTTP/1.1 502 Bad Gateway"; - public bool Filter(Exception ex) => - ex.GetType() == typeof(Exception) && - ex.Message.StartsWith(Message); - } -} + public bool Filter(Exception ex) => + ex.GetType() == typeof(Exception) && + ex.Message.StartsWith(Message); +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs b/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs index 0aa5f65bb..5deb1631d 100644 --- a/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs @@ -1,22 +1,21 @@ using System.Collections.Generic; using Sentry.Integrations; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal sealed class UnityBeforeSceneLoadIntegration : ISdkIntegration { - internal sealed class UnityBeforeSceneLoadIntegration : ISdkIntegration - { - private readonly IApplication _application; + private readonly IApplication _application; - public UnityBeforeSceneLoadIntegration(IApplication? application = null) - => _application = application ?? ApplicationAdapter.Instance; + public UnityBeforeSceneLoadIntegration(IApplication? application = null) + => _application = application ?? ApplicationAdapter.Instance; - public void Register(IHub hub, SentryOptions options) - { - var data = _application.ActiveSceneName is { } name - ? new Dictionary { { "scene", name } } - : null; + public void Register(IHub hub, SentryOptions options) + { + var data = _application.ActiveSceneName is { } name + ? new Dictionary { { "scene", name } } + : null; - hub.AddBreadcrumb(message: "BeforeSceneLoad", category: "scene.beforeload", data: data); - } + hub.AddBreadcrumb(message: "BeforeSceneLoad", category: "scene.beforeload", data: data); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs index 1e7755482..d34ad03d5 100644 --- a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs @@ -4,185 +4,184 @@ using Sentry.Protocol; using UnityEngine; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal sealed class UnityLogHandlerIntegration : ISdkIntegration, ILogHandler { - internal sealed class UnityLogHandlerIntegration : ISdkIntegration, ILogHandler - { - internal readonly ErrorTimeDebounce ErrorTimeDebounce; - internal readonly LogTimeDebounce LogTimeDebounce; - internal readonly WarningTimeDebounce WarningTimeDebounce; + internal readonly ErrorTimeDebounce ErrorTimeDebounce; + internal readonly LogTimeDebounce LogTimeDebounce; + internal readonly WarningTimeDebounce WarningTimeDebounce; - private readonly IApplication _application; + private readonly IApplication _application; - private IHub? _hub; - private SentryUnityOptions? _sentryOptions; + private IHub? _hub; + private SentryUnityOptions? _sentryOptions; - private ILogHandler _unityLogHandler = null!; // Set during register + private ILogHandler _unityLogHandler = null!; // Set during register - public UnityLogHandlerIntegration(SentryUnityOptions options, IApplication? application = null) - { - _application = application ?? ApplicationAdapter.Instance; + public UnityLogHandlerIntegration(SentryUnityOptions options, IApplication? application = null) + { + _application = application ?? ApplicationAdapter.Instance; + + LogTimeDebounce = new LogTimeDebounce(options.DebounceTimeLog); + WarningTimeDebounce = new WarningTimeDebounce(options.DebounceTimeWarning); + ErrorTimeDebounce = new ErrorTimeDebounce(options.DebounceTimeError); + } - LogTimeDebounce = new LogTimeDebounce(options.DebounceTimeLog); - WarningTimeDebounce = new WarningTimeDebounce(options.DebounceTimeWarning); - ErrorTimeDebounce = new ErrorTimeDebounce(options.DebounceTimeError); + public void Register(IHub hub, SentryOptions sentryOptions) + { + _hub = hub; + _sentryOptions = sentryOptions as SentryUnityOptions; + if (_sentryOptions is null) + { + return; } - public void Register(IHub hub, SentryOptions sentryOptions) + // If called twice (i.e. init with the same options object) the integration will reference itself as the + // original handler loghandler and endlessly forward to itself + if (Debug.unityLogger.logHandler == this) { - _hub = hub; - _sentryOptions = sentryOptions as SentryUnityOptions; - if (_sentryOptions is null) - { - return; - } + _sentryOptions.DiagnosticLogger?.LogWarning("UnityLogHandlerIntegration has already been registered."); + return; + } - // If called twice (i.e. init with the same options object) the integration will reference itself as the - // original handler loghandler and endlessly forward to itself - if (Debug.unityLogger.logHandler == this) - { - _sentryOptions.DiagnosticLogger?.LogWarning("UnityLogHandlerIntegration has already been registered."); - return; - } + _unityLogHandler = Debug.unityLogger.logHandler; + Debug.unityLogger.logHandler = this; - _unityLogHandler = Debug.unityLogger.logHandler; - Debug.unityLogger.logHandler = this; + _application.Quitting += OnQuitting; + } - _application.Quitting += OnQuitting; + public void LogException(Exception exception, UnityEngine.Object context) + { + try + { + CaptureException(exception, context); } - - public void LogException(Exception exception, UnityEngine.Object context) + finally { - try - { - CaptureException(exception, context); - } - finally - { - // Always pass the exception back to Unity - _unityLogHandler.LogException(exception, context); - } + // Always pass the exception back to Unity + _unityLogHandler.LogException(exception, context); } + } - internal void CaptureException(Exception exception, UnityEngine.Object? context) + internal void CaptureException(Exception exception, UnityEngine.Object? context) + { + if (_hub?.IsEnabled is not true) { - if (_hub?.IsEnabled is not true) - { - return; - } + return; + } - // TODO: Capture the context (i.e. grab the name if != null and set it as context) + // TODO: Capture the context (i.e. grab the name if != null and set it as context) - // NOTE: This might not be entirely true, as a user could as well call `Debug.LogException` - // and expect a handled exception but it is not possible for us to differentiate - // https://docs.sentry.io/platforms/unity/troubleshooting/#unhandled-exceptions---debuglogexception - exception.Data[Mechanism.HandledKey] = false; - exception.Data[Mechanism.MechanismKey] = "Unity.LogException"; - _ = _hub.CaptureException(exception); + // NOTE: This might not be entirely true, as a user could as well call `Debug.LogException` + // and expect a handled exception but it is not possible for us to differentiate + // https://docs.sentry.io/platforms/unity/troubleshooting/#unhandled-exceptions---debuglogexception + exception.Data[Mechanism.HandledKey] = false; + exception.Data[Mechanism.MechanismKey] = "Unity.LogException"; + _ = _hub.CaptureException(exception); - if (_sentryOptions?.AddBreadcrumbsForLogType[LogType.Exception] is true) - { - // So the next event includes this error as a breadcrumb - _hub.AddBreadcrumb(message: $"{exception.GetType()}: {exception.Message}", category: "unity.logger", level: BreadcrumbLevel.Error); - } + if (_sentryOptions?.AddBreadcrumbsForLogType[LogType.Exception] is true) + { + // So the next event includes this error as a breadcrumb + _hub.AddBreadcrumb(message: $"{exception.GetType()}: {exception.Message}", category: "unity.logger", level: BreadcrumbLevel.Error); } + } - public void LogFormat(LogType logType, UnityEngine.Object? context, string format, params object[] args) + public void LogFormat(LogType logType, UnityEngine.Object? context, string format, params object[] args) + { + try { - try - { - CaptureLogFormat(logType, context, format, args); - } - finally - { - // Always pass the log back to Unity - _unityLogHandler.LogFormat(logType, context, format, args); - } + CaptureLogFormat(logType, context, format, args); } - - internal void CaptureLogFormat(LogType logType, UnityEngine.Object? context, string format, params object[] args) + finally { - if (_hub?.IsEnabled is not true) - { - return; - } - - // The SDK sets "Sentry" as tag when logging and we're not capturing SDK internal logs. Expected format: "{0}: {1}" - if (args.Length > 1 && "Sentry".Equals(args[0])) // Checking it this way around because `args[0]` could be null - { - return; - } + // Always pass the log back to Unity + _unityLogHandler.LogFormat(logType, context, format, args); + } + } - if (_sentryOptions?.EnableLogDebouncing is true) - { - var debounced = logType switch - { - LogType.Error or LogType.Exception or LogType.Assert => ErrorTimeDebounce.Debounced(), - LogType.Log => LogTimeDebounce.Debounced(), - LogType.Warning => WarningTimeDebounce.Debounced(), - _ => true - }; - - if (!debounced) - { - return; - } - } + internal void CaptureLogFormat(LogType logType, UnityEngine.Object? context, string format, params object[] args) + { + if (_hub?.IsEnabled is not true) + { + return; + } - var logMessage = args.Length == 0 ? format : string.Format(format, args); + // The SDK sets "Sentry" as tag when logging and we're not capturing SDK internal logs. Expected format: "{0}: {1}" + if (args.Length > 1 && "Sentry".Equals(args[0])) // Checking it this way around because `args[0]` could be null + { + return; + } - if (logType is LogType.Error or LogType.Assert) + if (_sentryOptions?.EnableLogDebouncing is true) + { + var debounced = logType switch { - // TODO: Capture the context (i.e. grab the name if != null and set it as context) - _hub.CaptureMessage(logMessage, ToEventTagType(logType)); - } + LogType.Error or LogType.Exception or LogType.Assert => ErrorTimeDebounce.Debounced(), + LogType.Log => LogTimeDebounce.Debounced(), + LogType.Warning => WarningTimeDebounce.Debounced(), + _ => true + }; - if (_sentryOptions?.AddBreadcrumbsForLogType[logType] is true) + if (!debounced) { - // So the next event includes this as a breadcrumb - _hub.AddBreadcrumb(message: logMessage, category: "unity.logger", level: ToBreadcrumbLevel(logType)); + return; } } - private void OnQuitting() - { - _sentryOptions?.DiagnosticLogger?.LogInfo("OnQuitting was invoked. Unhooking log callback and pausing session."); + var logMessage = args.Length == 0 ? format : string.Format(format, args); - // Note: iOS applications are usually suspended and do not quit. You should tick "Exit on Suspend" in Player settings for iOS builds to cause the game to quit and not suspend, otherwise you may not see this call. - // If "Exit on Suspend" is not ticked then you will see calls to OnApplicationPause instead. - // Note: On Windows Store Apps and Windows Phone 8.1 there is no application quit event. Consider using OnApplicationFocus event when focusStatus equals false. - // Note: On WebGL it is not possible to implement OnApplicationQuit due to nature of the browser tabs closing. + if (logType is LogType.Error or LogType.Assert) + { + // TODO: Capture the context (i.e. grab the name if != null and set it as context) + _hub.CaptureMessage(logMessage, ToEventTagType(logType)); + } - // 'OnQuitting' is invoked even when an uncaught exception happens in the ART. To make sure the .NET - // SDK checks with the native layer on restart if the previous run crashed (through the CrashedLastRun callback) - // we'll just pause sessions on shutdown. On restart they can be closed with the right timestamp and as 'exited'. - if (_sentryOptions?.AutoSessionTracking is true) - { - _hub?.PauseSession(); - } - _hub?.FlushAsync(_sentryOptions?.ShutdownTimeout ?? TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); + if (_sentryOptions?.AddBreadcrumbsForLogType[logType] is true) + { + // So the next event includes this as a breadcrumb + _hub.AddBreadcrumb(message: logMessage, category: "unity.logger", level: ToBreadcrumbLevel(logType)); } + } - private static SentryLevel ToEventTagType(LogType logType) - => logType switch - { - LogType.Assert => SentryLevel.Error, - LogType.Error => SentryLevel.Error, - LogType.Exception => SentryLevel.Error, - LogType.Log => SentryLevel.Info, - LogType.Warning => SentryLevel.Warning, - _ => SentryLevel.Fatal - }; + private void OnQuitting() + { + _sentryOptions?.DiagnosticLogger?.LogInfo("OnQuitting was invoked. Unhooking log callback and pausing session."); - private static BreadcrumbLevel ToBreadcrumbLevel(LogType logType) - => logType switch - { - LogType.Assert => BreadcrumbLevel.Error, - LogType.Error => BreadcrumbLevel.Error, - LogType.Exception => BreadcrumbLevel.Error, - LogType.Log => BreadcrumbLevel.Info, - LogType.Warning => BreadcrumbLevel.Warning, - _ => BreadcrumbLevel.Info - }; + // Note: iOS applications are usually suspended and do not quit. You should tick "Exit on Suspend" in Player settings for iOS builds to cause the game to quit and not suspend, otherwise you may not see this call. + // If "Exit on Suspend" is not ticked then you will see calls to OnApplicationPause instead. + // Note: On Windows Store Apps and Windows Phone 8.1 there is no application quit event. Consider using OnApplicationFocus event when focusStatus equals false. + // Note: On WebGL it is not possible to implement OnApplicationQuit due to nature of the browser tabs closing. + + // 'OnQuitting' is invoked even when an uncaught exception happens in the ART. To make sure the .NET + // SDK checks with the native layer on restart if the previous run crashed (through the CrashedLastRun callback) + // we'll just pause sessions on shutdown. On restart they can be closed with the right timestamp and as 'exited'. + if (_sentryOptions?.AutoSessionTracking is true) + { + _hub?.PauseSession(); + } + _hub?.FlushAsync(_sentryOptions?.ShutdownTimeout ?? TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); } -} + + private static SentryLevel ToEventTagType(LogType logType) + => logType switch + { + LogType.Assert => SentryLevel.Error, + LogType.Error => SentryLevel.Error, + LogType.Exception => SentryLevel.Error, + LogType.Log => SentryLevel.Info, + LogType.Warning => SentryLevel.Warning, + _ => SentryLevel.Fatal + }; + + private static BreadcrumbLevel ToBreadcrumbLevel(LogType logType) + => logType switch + { + LogType.Assert => BreadcrumbLevel.Error, + LogType.Error => BreadcrumbLevel.Error, + LogType.Exception => BreadcrumbLevel.Error, + LogType.Log => BreadcrumbLevel.Info, + LogType.Warning => BreadcrumbLevel.Warning, + _ => BreadcrumbLevel.Info + }; +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs index 5a915559e..9164a4cde 100644 --- a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs @@ -5,179 +5,178 @@ using Sentry.Unity.Integrations; using OperatingSystem = Sentry.Protocol.OperatingSystem; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal static class UnitySdkInfo +{ + public static string Version { get; } = typeof(UnitySdkInfo).Assembly.GetNameAndVersion().Version ?? "0.0.0"; + public const string Name = "sentry.dotnet.unity"; + public const string PackageName = "upm:sentry.unity"; +} + +internal class UnityScopeIntegration : ISdkIntegration { - internal static class UnitySdkInfo + private readonly MainThreadData _mainThreadData; + private readonly IApplication _application; + + public UnityScopeIntegration(SentryMonoBehaviour monoBehaviour, IApplication application) { - public static string Version { get; } = typeof(UnitySdkInfo).Assembly.GetNameAndVersion().Version ?? "0.0.0"; - public const string Name = "sentry.dotnet.unity"; - public const string PackageName = "upm:sentry.unity"; + _mainThreadData = monoBehaviour.MainThreadData; + _application = application; } - internal class UnityScopeIntegration : ISdkIntegration + public void Register(IHub hub, SentryOptions options) { - private readonly MainThreadData _mainThreadData; - private readonly IApplication _application; + var scopeUpdater = new UnityScopeUpdater((SentryUnityOptions)options, _mainThreadData, _application); + hub.ConfigureScope(scopeUpdater.ConfigureScope); + } +} - public UnityScopeIntegration(SentryMonoBehaviour monoBehaviour, IApplication application) - { - _mainThreadData = monoBehaviour.MainThreadData; - _application = application; - } +internal class UnityScopeUpdater +{ + private readonly SentryUnityOptions _options; + private readonly MainThreadData _mainThreadData; + private readonly IApplication _application; - public void Register(IHub hub, SentryOptions options) - { - var scopeUpdater = new UnityScopeUpdater((SentryUnityOptions)options, _mainThreadData, _application); - hub.ConfigureScope(scopeUpdater.ConfigureScope); - } + public UnityScopeUpdater(SentryUnityOptions options, MainThreadData mainThreadData, IApplication application) + { + _options = options; + _mainThreadData = mainThreadData; + _application = application; } - internal class UnityScopeUpdater + public void ConfigureScope(Scope scope) { - private readonly SentryUnityOptions _options; - private readonly MainThreadData _mainThreadData; - private readonly IApplication _application; + PopulateSdk(scope.Sdk); + PopulateApp(scope.Contexts.App); + PopulateOperatingSystem(scope.Contexts.OperatingSystem); + PopulateDevice(scope.Contexts.Device); + PopulateGpu(scope.Contexts.Gpu); + + var unity = new Protocol.Unity(); + PopulateUnity(unity); + scope.Contexts.Add(Protocol.Unity.Type, unity); + + PopulateTags(scope.SetTag); + PopulateUser(scope); + } - public UnityScopeUpdater(SentryUnityOptions options, MainThreadData mainThreadData, IApplication application) - { - _options = options; - _mainThreadData = mainThreadData; - _application = application; - } + private static void PopulateSdk(SdkVersion sdk) + { + sdk.AddPackage(UnitySdkInfo.PackageName, UnitySdkInfo.Version); + sdk.Name = UnitySdkInfo.Name; + sdk.Version = UnitySdkInfo.Version; + } - public void ConfigureScope(Scope scope) - { - PopulateSdk(scope.Sdk); - PopulateApp(scope.Contexts.App); - PopulateOperatingSystem(scope.Contexts.OperatingSystem); - PopulateDevice(scope.Contexts.Device); - PopulateGpu(scope.Contexts.Gpu); - - var unity = new Protocol.Unity(); - PopulateUnity(unity); - scope.Contexts.Add(Protocol.Unity.Type, unity); - - PopulateTags(scope.SetTag); - PopulateUser(scope); - } + private void PopulateApp(App app) + { + app.StartTime = _mainThreadData.StartTime; + var isDebugBuild = _mainThreadData.IsDebugBuild; + app.BuildType = isDebugBuild is null ? null : (isDebugBuild.Value ? "debug" : "release"); + } - private static void PopulateSdk(SdkVersion sdk) - { - sdk.AddPackage(UnitySdkInfo.PackageName, UnitySdkInfo.Version); - sdk.Name = UnitySdkInfo.Name; - sdk.Version = UnitySdkInfo.Version; - } + private void PopulateOperatingSystem(OperatingSystem operatingSystem) + { + operatingSystem.RawDescription = _mainThreadData.OperatingSystem; + } - private void PopulateApp(App app) + private void PopulateDevice(Device device) + { + device.ProcessorCount = _mainThreadData.ProcessorCount; + device.CpuDescription = _mainThreadData.CpuDescription; + device.Timezone = TimeZoneInfo.Local; + device.SupportsVibration = _mainThreadData.SupportsVibration; + device.Name = _mainThreadData.DeviceName; + + // The app can be run in an iOS or Android emulator. We can't safely set a value for simulator. + device.Simulator = _application.IsEditor ? true : null; + device.DeviceUniqueIdentifier = _options.SendDefaultPii + ? _mainThreadData.DeviceUniqueIdentifier + : null; + device.DeviceType = _mainThreadData.DeviceType; + device.Model = _mainThreadData.DeviceModel; + + // This is the approximate amount of system memory in megabytes. + // This function is not supported on Windows Store Apps and will always return 0. + if (_mainThreadData.SystemMemorySize > 0) { - app.StartTime = _mainThreadData.StartTime; - var isDebugBuild = _mainThreadData.IsDebugBuild; - app.BuildType = isDebugBuild is null ? null : (isDebugBuild.Value ? "debug" : "release"); + device.MemorySize = _mainThreadData.SystemMemorySize * 1048576L; // Sentry device mem is in Bytes } + } - private void PopulateOperatingSystem(OperatingSystem operatingSystem) + private void PopulateGpu(Gpu gpu) + { + gpu.Id = _mainThreadData.GraphicsDeviceId; + gpu.Name = _mainThreadData.GraphicsDeviceName; + gpu.VendorName = _mainThreadData.GraphicsDeviceVendor; + gpu.MemorySize = _mainThreadData.GraphicsMemorySize; + gpu.NpotSupport = _mainThreadData.NpotSupport; + gpu.Version = _mainThreadData.GraphicsDeviceVersion; + gpu.ApiType = _mainThreadData.GraphicsDeviceType; + gpu.MaxTextureSize = _mainThreadData.MaxTextureSize; + gpu.SupportsDrawCallInstancing = _mainThreadData.SupportsDrawCallInstancing; + gpu.SupportsRayTracing = _mainThreadData.SupportsRayTracing; + gpu.SupportsComputeShaders = _mainThreadData.SupportsComputeShaders; + gpu.SupportsGeometryShaders = _mainThreadData.SupportsGeometryShaders; + gpu.VendorId = _mainThreadData.GraphicsDeviceVendorId; + gpu.MultiThreadedRendering = _mainThreadData.GraphicsMultiThreaded; + gpu.GraphicsShaderLevel = _mainThreadData.GraphicsShaderLevel switch { - operatingSystem.RawDescription = _mainThreadData.OperatingSystem; - } + null => null, + -1 => null, + 20 => "Shader Model 2.0", + 25 => "Shader Model 2.5", + 30 => "Shader Model 3.0", + 35 => "OpenGL ES 3.0", + 40 => "Shader Model 4.0", + 45 => "Metal / OpenGL ES 3.1", + 46 => "OpenGL 4.1", + 50 => "Shader Model 5.0", + _ => _mainThreadData.GraphicsShaderLevel.ToString() + }; + } - private void PopulateDevice(Device device) + private void PopulateUnity(Protocol.Unity unity) + { + unity.EditorVersion = _mainThreadData.EditorVersion; + unity.InstallMode = _mainThreadData.InstallMode; + unity.TargetFrameRate = _mainThreadData.TargetFrameRate; + unity.CopyTextureSupport = _mainThreadData.CopyTextureSupport; + unity.RenderingThreadingMode = _mainThreadData.RenderingThreadingMode; + } + + private void PopulateTags(Action setTag) + { + // TODO revisit which tags we should be adding by default + if (_mainThreadData.InstallMode is { } installMode) { - device.ProcessorCount = _mainThreadData.ProcessorCount; - device.CpuDescription = _mainThreadData.CpuDescription; - device.Timezone = TimeZoneInfo.Local; - device.SupportsVibration = _mainThreadData.SupportsVibration; - device.Name = _mainThreadData.DeviceName; - - // The app can be run in an iOS or Android emulator. We can't safely set a value for simulator. - device.Simulator = _application.IsEditor ? true : null; - device.DeviceUniqueIdentifier = _options.SendDefaultPii - ? _mainThreadData.DeviceUniqueIdentifier - : null; - device.DeviceType = _mainThreadData.DeviceType; - device.Model = _mainThreadData.DeviceModel; - - // This is the approximate amount of system memory in megabytes. - // This function is not supported on Windows Store Apps and will always return 0. - if (_mainThreadData.SystemMemorySize > 0) - { - device.MemorySize = _mainThreadData.SystemMemorySize * 1048576L; // Sentry device mem is in Bytes - } + setTag("unity.install_mode", installMode); } - private void PopulateGpu(Gpu gpu) + if (_mainThreadData.SupportsDrawCallInstancing.HasValue) { - gpu.Id = _mainThreadData.GraphicsDeviceId; - gpu.Name = _mainThreadData.GraphicsDeviceName; - gpu.VendorName = _mainThreadData.GraphicsDeviceVendor; - gpu.MemorySize = _mainThreadData.GraphicsMemorySize; - gpu.NpotSupport = _mainThreadData.NpotSupport; - gpu.Version = _mainThreadData.GraphicsDeviceVersion; - gpu.ApiType = _mainThreadData.GraphicsDeviceType; - gpu.MaxTextureSize = _mainThreadData.MaxTextureSize; - gpu.SupportsDrawCallInstancing = _mainThreadData.SupportsDrawCallInstancing; - gpu.SupportsRayTracing = _mainThreadData.SupportsRayTracing; - gpu.SupportsComputeShaders = _mainThreadData.SupportsComputeShaders; - gpu.SupportsGeometryShaders = _mainThreadData.SupportsGeometryShaders; - gpu.VendorId = _mainThreadData.GraphicsDeviceVendorId; - gpu.MultiThreadedRendering = _mainThreadData.GraphicsMultiThreaded; - gpu.GraphicsShaderLevel = _mainThreadData.GraphicsShaderLevel switch - { - null => null, - -1 => null, - 20 => "Shader Model 2.0", - 25 => "Shader Model 2.5", - 30 => "Shader Model 3.0", - 35 => "OpenGL ES 3.0", - 40 => "Shader Model 4.0", - 45 => "Metal / OpenGL ES 3.1", - 46 => "OpenGL 4.1", - 50 => "Shader Model 5.0", - _ => _mainThreadData.GraphicsShaderLevel.ToString() - }; + setTag("unity.gpu.supports_instancing", _mainThreadData.SupportsDrawCallInstancing.Value.ToTagValue()); } - private void PopulateUnity(Protocol.Unity unity) + if (_mainThreadData.DeviceType is { } deviceType) { - unity.EditorVersion = _mainThreadData.EditorVersion; - unity.InstallMode = _mainThreadData.InstallMode; - unity.TargetFrameRate = _mainThreadData.TargetFrameRate; - unity.CopyTextureSupport = _mainThreadData.CopyTextureSupport; - unity.RenderingThreadingMode = _mainThreadData.RenderingThreadingMode; + setTag("unity.device.device_type", deviceType); } - private void PopulateTags(Action setTag) + if (_options.SendDefaultPii && _mainThreadData.DeviceUniqueIdentifier is { } deviceUniqueIdentifier) { - // TODO revisit which tags we should be adding by default - if (_mainThreadData.InstallMode is { } installMode) - { - setTag("unity.install_mode", installMode); - } - - if (_mainThreadData.SupportsDrawCallInstancing.HasValue) - { - setTag("unity.gpu.supports_instancing", _mainThreadData.SupportsDrawCallInstancing.Value.ToTagValue()); - } - - if (_mainThreadData.DeviceType is { } deviceType) - { - setTag("unity.device.device_type", deviceType); - } - - if (_options.SendDefaultPii && _mainThreadData.DeviceUniqueIdentifier is { } deviceUniqueIdentifier) - { - setTag("unity.device.unique_identifier", deviceUniqueIdentifier); - } + setTag("unity.device.unique_identifier", deviceUniqueIdentifier); } + } - private void PopulateUser(Scope scope) + private void PopulateUser(Scope scope) + { + if (_options.DefaultUserId is not null) { - if (_options.DefaultUserId is not null) + if (scope.User.Id is null) { - if (scope.User.Id is null) - { - scope.User.Id = _options.DefaultUserId; - } + scope.User.Id = _options.DefaultUserId; } } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs b/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs index d1b3a58e9..e650dcb62 100644 --- a/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs +++ b/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs @@ -1,14 +1,13 @@ using System; using Sentry.Extensibility; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal class UnitySocketExceptionFilter : IExceptionFilter { - internal class UnitySocketExceptionFilter : IExceptionFilter - { - internal const string Message = "The requested address is not valid in this context"; + internal const string Message = "The requested address is not valid in this context"; - public bool Filter(Exception ex) => - ex.GetType() == typeof(System.Net.Sockets.SocketException) && - ex.Message.Equals(Message); - } -} + public bool Filter(Exception ex) => + ex.GetType() == typeof(System.Net.Sockets.SocketException) && + ex.Message.Equals(Message); +} \ No newline at end of file diff --git a/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs b/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs index 52b2f7f70..0de74a360 100644 --- a/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs +++ b/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs @@ -1,14 +1,13 @@ using System; using Sentry.Extensibility; -namespace Sentry.Unity.Integrations +namespace Sentry.Unity.Integrations; + +internal class UnityWebExceptionFilter : IExceptionFilter { - internal class UnityWebExceptionFilter : IExceptionFilter - { - internal const string Message = "Error: ConnectFailure (The requested address is not valid in this context)"; + internal const string Message = "Error: ConnectFailure (The requested address is not valid in this context)"; - public bool Filter(Exception ex) => - ex.GetType() == typeof(System.Net.WebException) && - ex.Message.Equals(Message); - } -} + public bool Filter(Exception ex) => + ex.GetType() == typeof(System.Net.WebException) && + ex.Message.Equals(Message); +} \ No newline at end of file diff --git a/src/Sentry.Unity/Json/SafeSerializer.cs b/src/Sentry.Unity/Json/SafeSerializer.cs index 4a81adabd..59f2cf685 100644 --- a/src/Sentry.Unity/Json/SafeSerializer.cs +++ b/src/Sentry.Unity/Json/SafeSerializer.cs @@ -2,32 +2,31 @@ using System.Text.Json; using Sentry.Extensibility; -namespace Sentry.Unity.Json +namespace Sentry.Unity.Json; + +internal static class SafeSerializer { - internal static class SafeSerializer + /// + /// Attempts to serialize + /// + /// + /// + /// + public static string? SerializeSafely(object value, IDiagnosticLogger? logger = null) { - /// - /// Attempts to serialize - /// - /// - /// - /// - public static string? SerializeSafely(object value, IDiagnosticLogger? logger = null) + if (value is string stringValue) + { + // Otherwise it'll return ""value"" + return stringValue; + } + try + { + return JsonSerializer.Serialize(value); + } + catch (Exception e) { - if (value is string stringValue) - { - // Otherwise it'll return ""value"" - return stringValue; - } - try - { - return JsonSerializer.Serialize(value); - } - catch (Exception e) - { - logger?.LogError(exception: e, "Failed to serialize value of type \"{0}\"", value.GetType()); - return null; - } + logger?.LogError(exception: e, "Failed to serialize value of type \"{0}\"", value.GetType()); + return null; } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/MainThreadData.cs b/src/Sentry.Unity/MainThreadData.cs index 03de13ad6..0aaa42339 100644 --- a/src/Sentry.Unity/MainThreadData.cs +++ b/src/Sentry.Unity/MainThreadData.cs @@ -1,74 +1,73 @@ using System; using System.Threading; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal sealed class MainThreadData { - internal sealed class MainThreadData - { - internal int? MainThreadId { get; set; } + internal int? MainThreadId { get; set; } - public string? OperatingSystem { get; set; } + public string? OperatingSystem { get; set; } - public int? ProcessorCount { get; set; } + public int? ProcessorCount { get; set; } - public bool? SupportsVibration { get; set; } + public bool? SupportsVibration { get; set; } - public string? DeviceType { get; set; } + public string? DeviceType { get; set; } - public string? CpuDescription { get; set; } + public string? CpuDescription { get; set; } - public string? DeviceName { get; set; } + public string? DeviceName { get; set; } - public string? DeviceUniqueIdentifier { get; set; } + public string? DeviceUniqueIdentifier { get; set; } - public string? DeviceModel { get; set; } + public string? DeviceModel { get; set; } - public int? SystemMemorySize { get; set; } + public int? SystemMemorySize { get; set; } - public int? GraphicsDeviceId { get; set; } + public int? GraphicsDeviceId { get; set; } - public string? GraphicsDeviceName { get; set; } + public string? GraphicsDeviceName { get; set; } - public string? GraphicsDeviceVendorId { get; set; } + public string? GraphicsDeviceVendorId { get; set; } - public string? GraphicsDeviceVendor { get; set; } + public string? GraphicsDeviceVendor { get; set; } - public int? GraphicsMemorySize { get; set; } + public int? GraphicsMemorySize { get; set; } - public bool? GraphicsMultiThreaded { get; set; } + public bool? GraphicsMultiThreaded { get; set; } - public string? NpotSupport { get; set; } + public string? NpotSupport { get; set; } - public string? GraphicsDeviceVersion { get; set; } + public string? GraphicsDeviceVersion { get; set; } - public string? GraphicsDeviceType { get; set; } + public string? GraphicsDeviceType { get; set; } - public int? MaxTextureSize { get; set; } + public int? MaxTextureSize { get; set; } - public bool? SupportsDrawCallInstancing { get; set; } + public bool? SupportsDrawCallInstancing { get; set; } - public bool? SupportsRayTracing { get; set; } + public bool? SupportsRayTracing { get; set; } - public bool? SupportsComputeShaders { get; set; } + public bool? SupportsComputeShaders { get; set; } - public bool? SupportsGeometryShaders { get; set; } + public bool? SupportsGeometryShaders { get; set; } - public int? GraphicsShaderLevel { get; set; } + public int? GraphicsShaderLevel { get; set; } - public bool? IsDebugBuild { get; set; } + public bool? IsDebugBuild { get; set; } - public string? EditorVersion { get; set; } - public string? InstallMode { get; set; } + public string? EditorVersion { get; set; } + public string? InstallMode { get; set; } - public string? TargetFrameRate { get; set; } + public string? TargetFrameRate { get; set; } - public string? CopyTextureSupport { get; set; } + public string? CopyTextureSupport { get; set; } - public string? RenderingThreadingMode { get; set; } + public string? RenderingThreadingMode { get; set; } - public DateTimeOffset? StartTime { get; set; } + public DateTimeOffset? StartTime { get; set; } - public bool IsMainThread() - => MainThreadId.HasValue && Thread.CurrentThread.ManagedThreadId == MainThreadId; - } + public bool IsMainThread() + => MainThreadId.HasValue && Thread.CurrentThread.ManagedThreadId == MainThreadId; } diff --git a/src/Sentry.Unity/NativeUtils/CFunctions.cs b/src/Sentry.Unity/NativeUtils/CFunctions.cs index 1b413d3f6..75f3db7d6 100644 --- a/src/Sentry.Unity/NativeUtils/CFunctions.cs +++ b/src/Sentry.Unity/NativeUtils/CFunctions.cs @@ -4,216 +4,215 @@ using Sentry.Protocol; using UnityEngine; -namespace Sentry.Unity.NativeUtils +namespace Sentry.Unity.NativeUtils; + +internal static class C { - internal static class C + internal static void SetValueIfNotNull(sentry_value_t obj, string key, string? value) { - internal static void SetValueIfNotNull(sentry_value_t obj, string key, string? value) + if (value is not null) { - if (value is not null) - { - _ = sentry_value_set_by_key(obj, key, sentry_value_new_string(value)); - } + _ = sentry_value_set_by_key(obj, key, sentry_value_new_string(value)); } + } - internal static void SetValueIfNotNull(sentry_value_t obj, string key, int? value) + internal static void SetValueIfNotNull(sentry_value_t obj, string key, int? value) + { + if (value.HasValue) { - if (value.HasValue) - { - _ = sentry_value_set_by_key(obj, key, sentry_value_new_int32(value.Value)); - } + _ = sentry_value_set_by_key(obj, key, sentry_value_new_int32(value.Value)); } + } - internal static void SetValueIfNotNull(sentry_value_t obj, string key, bool? value) + internal static void SetValueIfNotNull(sentry_value_t obj, string key, bool? value) + { + if (value.HasValue) { - if (value.HasValue) - { - _ = sentry_value_set_by_key(obj, key, sentry_value_new_bool(value.Value ? 1 : 0)); - } + _ = sentry_value_set_by_key(obj, key, sentry_value_new_bool(value.Value ? 1 : 0)); } + } - internal static void SetValueIfNotNull(sentry_value_t obj, string key, double? value) + internal static void SetValueIfNotNull(sentry_value_t obj, string key, double? value) + { + if (value.HasValue) { - if (value.HasValue) - { - _ = sentry_value_set_by_key(obj, key, sentry_value_new_double(value.Value)); - } + _ = sentry_value_set_by_key(obj, key, sentry_value_new_double(value.Value)); } + } - internal static sentry_value_t? GetValueOrNul(sentry_value_t obj, string key) - { - var cValue = sentry_value_get_by_key(obj, key); - return sentry_value_is_null(cValue) == 0 ? cValue : null; - } + internal static sentry_value_t? GetValueOrNul(sentry_value_t obj, string key) + { + var cValue = sentry_value_get_by_key(obj, key); + return sentry_value_is_null(cValue) == 0 ? cValue : null; + } - internal static string? GetValueString(sentry_value_t obj, string key) + internal static string? GetValueString(sentry_value_t obj, string key) + { + if (GetValueOrNul(obj, key) is { } cValue) { - if (GetValueOrNul(obj, key) is { } cValue) + var cString = sentry_value_as_string(cValue); + if (cString != IntPtr.Zero) { - var cString = sentry_value_as_string(cValue); - if (cString != IntPtr.Zero) - { - return Marshal.PtrToStringAnsi(cString); - } + return Marshal.PtrToStringAnsi(cString); } - return null; } + return null; + } - internal static int? GetValueInt(sentry_value_t obj, string key) + internal static int? GetValueInt(sentry_value_t obj, string key) + { + if (GetValueOrNul(obj, key) is { } cValue) { - if (GetValueOrNul(obj, key) is { } cValue) - { - return sentry_value_as_int32(cValue); - } - return null; + return sentry_value_as_int32(cValue); } + return null; + } - internal static double? GetValueDouble(sentry_value_t obj, string key) + internal static double? GetValueDouble(sentry_value_t obj, string key) + { + if (GetValueOrNul(obj, key) is { } cValue) { - if (GetValueOrNul(obj, key) is { } cValue) - { - return sentry_value_as_double(cValue); - } - return null; + return sentry_value_as_double(cValue); } + return null; + } - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_object(); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_object(); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_null(); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_null(); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_bool(int value); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_bool(int value); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_double(double value); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_double(double value); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_int32(int value); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_int32(int value); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_string(string value); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_string(string value); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_new_breadcrumb(string? type, string? message); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_new_breadcrumb(string? type, string? message); - [DllImport("sentry")] - internal static extern int sentry_value_set_by_key(sentry_value_t value, string k, sentry_value_t v); + [DllImport("sentry")] + internal static extern int sentry_value_set_by_key(sentry_value_t value, string k, sentry_value_t v); - internal static bool IsNull(sentry_value_t value) => sentry_value_is_null(value) != 0; + internal static bool IsNull(sentry_value_t value) => sentry_value_is_null(value) != 0; - [DllImport("sentry")] - internal static extern int sentry_value_is_null(sentry_value_t value); + [DllImport("sentry")] + internal static extern int sentry_value_is_null(sentry_value_t value); - [DllImport("sentry")] - internal static extern int sentry_value_as_int32(sentry_value_t value); + [DllImport("sentry")] + internal static extern int sentry_value_as_int32(sentry_value_t value); - [DllImport("sentry")] - internal static extern double sentry_value_as_double(sentry_value_t value); + [DllImport("sentry")] + internal static extern double sentry_value_as_double(sentry_value_t value); - [DllImport("sentry")] - internal static extern IntPtr sentry_value_as_string(sentry_value_t value); + [DllImport("sentry")] + internal static extern IntPtr sentry_value_as_string(sentry_value_t value); - [DllImport("sentry")] - internal static extern UIntPtr sentry_value_get_length(sentry_value_t value); + [DllImport("sentry")] + internal static extern UIntPtr sentry_value_get_length(sentry_value_t value); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_get_by_index(sentry_value_t value, UIntPtr index); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_get_by_index(sentry_value_t value, UIntPtr index); - [DllImport("sentry")] - internal static extern sentry_value_t sentry_value_get_by_key(sentry_value_t value, string key); + [DllImport("sentry")] + internal static extern sentry_value_t sentry_value_get_by_key(sentry_value_t value, string key); - [DllImport("sentry")] - internal static extern void sentry_set_context(string key, sentry_value_t value); + [DllImport("sentry")] + internal static extern void sentry_set_context(string key, sentry_value_t value); - [DllImport("sentry")] - internal static extern void sentry_add_breadcrumb(sentry_value_t breadcrumb); + [DllImport("sentry")] + internal static extern void sentry_add_breadcrumb(sentry_value_t breadcrumb); - [DllImport("sentry")] - internal static extern void sentry_set_tag(string key, string value); + [DllImport("sentry")] + internal static extern void sentry_set_tag(string key, string value); - [DllImport("sentry")] - internal static extern void sentry_remove_tag(string key); + [DllImport("sentry")] + internal static extern void sentry_remove_tag(string key); - [DllImport("sentry")] - internal static extern void sentry_set_user(sentry_value_t user); + [DllImport("sentry")] + internal static extern void sentry_set_user(sentry_value_t user); - [DllImport("sentry")] - internal static extern void sentry_remove_user(); + [DllImport("sentry")] + internal static extern void sentry_remove_user(); - [DllImport("sentry")] - internal static extern void sentry_set_extra(string key, sentry_value_t value); + [DllImport("sentry")] + internal static extern void sentry_set_extra(string key, sentry_value_t value); - [DllImport("sentry")] - internal static extern void sentry_remove_extra(string key); + [DllImport("sentry")] + internal static extern void sentry_remove_extra(string key); - internal static readonly Lazy> DebugImages = new(LoadDebugImages); + internal static readonly Lazy> DebugImages = new(LoadDebugImages); - private static IEnumerable LoadDebugImages() + private static IEnumerable LoadDebugImages() + { + var result = new List(); + try { - var result = new List(); + var cList = sentry_get_modules_list(); try { - var cList = sentry_get_modules_list(); - try + if (!IsNull(cList)) { - if (!IsNull(cList)) + var len = sentry_value_get_length(cList).ToUInt32(); + for (uint i = 0; i < len; i++) { - var len = sentry_value_get_length(cList).ToUInt32(); - for (uint i = 0; i < len; i++) + var cItem = sentry_value_get_by_index(cList, (UIntPtr)i); + if (!IsNull(cItem)) { - var cItem = sentry_value_get_by_index(cList, (UIntPtr)i); - if (!IsNull(cItem)) + // See possible values present in `cItem` in the following files (or their latest versions) + // * https://github.com/getsentry/sentry-native/blob/8faa78298da68d68043f0c3bd694f756c0e95dfa/src/modulefinder/sentry_modulefinder_windows.c#L81 + // * https://github.com/getsentry/sentry-native/blob/8faa78298da68d68043f0c3bd694f756c0e95dfa/src/modulefinder/sentry_modulefinder_windows.c#L24 + // * https://github.com/getsentry/sentry-native/blob/c5c31e56d36bed37fa5422750a591f44502edb41/src/modulefinder/sentry_modulefinder_linux.c#L465 + result.Add(new DebugImage() { - // See possible values present in `cItem` in the following files (or their latest versions) - // * https://github.com/getsentry/sentry-native/blob/8faa78298da68d68043f0c3bd694f756c0e95dfa/src/modulefinder/sentry_modulefinder_windows.c#L81 - // * https://github.com/getsentry/sentry-native/blob/8faa78298da68d68043f0c3bd694f756c0e95dfa/src/modulefinder/sentry_modulefinder_windows.c#L24 - // * https://github.com/getsentry/sentry-native/blob/c5c31e56d36bed37fa5422750a591f44502edb41/src/modulefinder/sentry_modulefinder_linux.c#L465 - result.Add(new DebugImage() - { - CodeFile = GetValueString(cItem, "code_file"), - ImageAddress = Convert.ToInt64(GetValueString(cItem, "image_addr"), 16), - ImageSize = GetValueInt(cItem, "image_size"), - DebugFile = GetValueString(cItem, "debug_file"), - DebugId = GetValueString(cItem, "debug_id"), - CodeId = GetValueString(cItem, "code_id"), - Type = GetValueString(cItem, "type"), - }); - } + CodeFile = GetValueString(cItem, "code_file"), + ImageAddress = Convert.ToInt64(GetValueString(cItem, "image_addr"), 16), + ImageSize = GetValueInt(cItem, "image_size"), + DebugFile = GetValueString(cItem, "debug_file"), + DebugId = GetValueString(cItem, "debug_id"), + CodeId = GetValueString(cItem, "code_id"), + Type = GetValueString(cItem, "type"), + }); } } } - finally - { - sentry_value_decref(cList); - } } - catch (Exception e) + finally { - // Adding the Sentry logger tag ensures we don't send this error to Sentry. - Debug.unityLogger.Log(LogType.Error, UnityLogger.LogTag, $"Error loading the list of debug images: {e}"); + sentry_value_decref(cList); } - return result; } - - // Returns a new reference to an immutable, frozen list. - // The reference must be released with `sentry_value_decref`. - [DllImport("sentry")] - private static extern sentry_value_t sentry_get_modules_list(); - - [DllImport("sentry")] - internal static extern void sentry_value_decref(sentry_value_t value); - - // native union sentry_value_u/t - [StructLayout(LayoutKind.Explicit)] - internal struct sentry_value_t + catch (Exception e) { - [FieldOffset(0)] - internal ulong _bits; - [FieldOffset(0)] - internal double _double; + // Adding the Sentry logger tag ensures we don't send this error to Sentry. + Debug.unityLogger.Log(LogType.Error, UnityLogger.LogTag, $"Error loading the list of debug images: {e}"); } + return result; + } + + // Returns a new reference to an immutable, frozen list. + // The reference must be released with `sentry_value_decref`. + [DllImport("sentry")] + private static extern sentry_value_t sentry_get_modules_list(); + + [DllImport("sentry")] + internal static extern void sentry_value_decref(sentry_value_t value); + // native union sentry_value_u/t + [StructLayout(LayoutKind.Explicit)] + internal struct sentry_value_t + { + [FieldOffset(0)] + internal ulong _bits; + [FieldOffset(0)] + internal double _double; } -} + +} \ No newline at end of file diff --git a/src/Sentry.Unity/NativeUtils/ContextWriter.cs b/src/Sentry.Unity/NativeUtils/ContextWriter.cs index c7b8691c5..5971667c9 100644 --- a/src/Sentry.Unity/NativeUtils/ContextWriter.cs +++ b/src/Sentry.Unity/NativeUtils/ContextWriter.cs @@ -1,98 +1,97 @@ -namespace Sentry.Unity.NativeUtils +namespace Sentry.Unity.NativeUtils; + +internal static class ContextWriter { - internal static class ContextWriter + internal static void WriteApp(string? AppStartTime, string? AppBuildType) { - internal static void WriteApp(string? AppStartTime, string? AppBuildType) - { - var obj = C.sentry_value_new_object(); - C.SetValueIfNotNull(obj, "app_start_time", AppStartTime); - C.SetValueIfNotNull(obj, "build_type", AppBuildType); - C.sentry_set_context(Sentry.Protocol.App.Type, obj); - } + var obj = C.sentry_value_new_object(); + C.SetValueIfNotNull(obj, "app_start_time", AppStartTime); + C.SetValueIfNotNull(obj, "build_type", AppBuildType); + C.sentry_set_context(Sentry.Protocol.App.Type, obj); + } - internal static void WriteOS(string? OperatingSystemRawDescription) - { - var obj = C.sentry_value_new_object(); - C.SetValueIfNotNull(obj, "raw_description", OperatingSystemRawDescription); - C.sentry_set_context(Sentry.Protocol.OperatingSystem.Type, obj); - } + internal static void WriteOS(string? OperatingSystemRawDescription) + { + var obj = C.sentry_value_new_object(); + C.SetValueIfNotNull(obj, "raw_description", OperatingSystemRawDescription); + C.sentry_set_context(Sentry.Protocol.OperatingSystem.Type, obj); + } - internal static void WriteDevice( - int? DeviceProcessorCount, - string? DeviceCpuDescription, - string? DeviceTimezone, - bool? DeviceSupportsVibration, - string? DeviceName, - bool? DeviceSimulator, - string? DeviceDeviceUniqueIdentifier, - string? DeviceDeviceType, - string? DeviceModel, - long? DeviceMemorySize) - { - var obj = C.sentry_value_new_object(); - C.SetValueIfNotNull(obj, "processor_count", DeviceProcessorCount); - C.SetValueIfNotNull(obj, "cpu_description", DeviceCpuDescription); - C.SetValueIfNotNull(obj, "timezone", DeviceTimezone); - C.SetValueIfNotNull(obj, "supports_vibration", DeviceSupportsVibration); - C.SetValueIfNotNull(obj, "name", DeviceName); - C.SetValueIfNotNull(obj, "simulator", DeviceSimulator); - C.SetValueIfNotNull(obj, "device_unique_identifier", DeviceDeviceUniqueIdentifier); - C.SetValueIfNotNull(obj, "device_type", DeviceDeviceType); - C.SetValueIfNotNull(obj, "model", DeviceModel); - C.SetValueIfNotNull(obj, "memory_size", DeviceMemorySize); - C.sentry_set_context(Sentry.Protocol.Device.Type, obj); - } + internal static void WriteDevice( + int? DeviceProcessorCount, + string? DeviceCpuDescription, + string? DeviceTimezone, + bool? DeviceSupportsVibration, + string? DeviceName, + bool? DeviceSimulator, + string? DeviceDeviceUniqueIdentifier, + string? DeviceDeviceType, + string? DeviceModel, + long? DeviceMemorySize) + { + var obj = C.sentry_value_new_object(); + C.SetValueIfNotNull(obj, "processor_count", DeviceProcessorCount); + C.SetValueIfNotNull(obj, "cpu_description", DeviceCpuDescription); + C.SetValueIfNotNull(obj, "timezone", DeviceTimezone); + C.SetValueIfNotNull(obj, "supports_vibration", DeviceSupportsVibration); + C.SetValueIfNotNull(obj, "name", DeviceName); + C.SetValueIfNotNull(obj, "simulator", DeviceSimulator); + C.SetValueIfNotNull(obj, "device_unique_identifier", DeviceDeviceUniqueIdentifier); + C.SetValueIfNotNull(obj, "device_type", DeviceDeviceType); + C.SetValueIfNotNull(obj, "model", DeviceModel); + C.SetValueIfNotNull(obj, "memory_size", DeviceMemorySize); + C.sentry_set_context(Sentry.Protocol.Device.Type, obj); + } - internal static void WriteGpu( - int? GpuId, - string? GpuName, - string? GpuVendorName, - int? GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int? GpuMaxTextureSize, - bool? GpuSupportsDrawCallInstancing, - bool? GpuSupportsRayTracing, - bool? GpuSupportsComputeShaders, - bool? GpuSupportsGeometryShaders, - string? GpuVendorId, - bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel) - { - var obj = C.sentry_value_new_object(); - C.SetValueIfNotNull(obj, "id", GpuId); - C.SetValueIfNotNull(obj, "name", GpuName); - C.SetValueIfNotNull(obj, "vendor_name", GpuVendorName); - C.SetValueIfNotNull(obj, "memory_size", GpuMemorySize); - C.SetValueIfNotNull(obj, "npot_support", GpuNpotSupport); - C.SetValueIfNotNull(obj, "version", GpuVersion); - C.SetValueIfNotNull(obj, "api_type", GpuApiType); - C.SetValueIfNotNull(obj, "max_texture_size", GpuMaxTextureSize); - C.SetValueIfNotNull(obj, "supports_draw_call_instancing", GpuSupportsDrawCallInstancing); - C.SetValueIfNotNull(obj, "supports_ray_tracing", GpuSupportsRayTracing); - C.SetValueIfNotNull(obj, "supports_compute_shaders", GpuSupportsComputeShaders); - C.SetValueIfNotNull(obj, "supports_geometry_shaders", GpuSupportsGeometryShaders); - C.SetValueIfNotNull(obj, "vendor_id", GpuVendorId); - C.SetValueIfNotNull(obj, "multi_threaded_rendering", GpuMultiThreadedRendering); - C.SetValueIfNotNull(obj, "graphics_shader_level", GpuGraphicsShaderLevel); - C.sentry_set_context(Sentry.Protocol.Gpu.Type, obj); - } + internal static void WriteGpu( + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel) + { + var obj = C.sentry_value_new_object(); + C.SetValueIfNotNull(obj, "id", GpuId); + C.SetValueIfNotNull(obj, "name", GpuName); + C.SetValueIfNotNull(obj, "vendor_name", GpuVendorName); + C.SetValueIfNotNull(obj, "memory_size", GpuMemorySize); + C.SetValueIfNotNull(obj, "npot_support", GpuNpotSupport); + C.SetValueIfNotNull(obj, "version", GpuVersion); + C.SetValueIfNotNull(obj, "api_type", GpuApiType); + C.SetValueIfNotNull(obj, "max_texture_size", GpuMaxTextureSize); + C.SetValueIfNotNull(obj, "supports_draw_call_instancing", GpuSupportsDrawCallInstancing); + C.SetValueIfNotNull(obj, "supports_ray_tracing", GpuSupportsRayTracing); + C.SetValueIfNotNull(obj, "supports_compute_shaders", GpuSupportsComputeShaders); + C.SetValueIfNotNull(obj, "supports_geometry_shaders", GpuSupportsGeometryShaders); + C.SetValueIfNotNull(obj, "vendor_id", GpuVendorId); + C.SetValueIfNotNull(obj, "multi_threaded_rendering", GpuMultiThreadedRendering); + C.SetValueIfNotNull(obj, "graphics_shader_level", GpuGraphicsShaderLevel); + C.sentry_set_context(Sentry.Protocol.Gpu.Type, obj); + } - internal static void WriteUnity( - string? EditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode) - { - var obj = C.sentry_value_new_object(); - C.SetValueIfNotNull(obj, "editor_version", EditorVersion); - C.SetValueIfNotNull(obj, "install_mode", UnityInstallMode); - C.SetValueIfNotNull(obj, "target_frame_rate", UnityTargetFrameRate); - C.SetValueIfNotNull(obj, "copy_texture_support", UnityCopyTextureSupport); - C.SetValueIfNotNull(obj, "rendering_threading_mode", UnityRenderingThreadingMode); - C.sentry_set_context(Protocol.Unity.Type, obj); - } + internal static void WriteUnity( + string? EditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode) + { + var obj = C.sentry_value_new_object(); + C.SetValueIfNotNull(obj, "editor_version", EditorVersion); + C.SetValueIfNotNull(obj, "install_mode", UnityInstallMode); + C.SetValueIfNotNull(obj, "target_frame_rate", UnityTargetFrameRate); + C.SetValueIfNotNull(obj, "copy_texture_support", UnityCopyTextureSupport); + C.SetValueIfNotNull(obj, "rendering_threading_mode", UnityRenderingThreadingMode); + C.sentry_set_context(Protocol.Unity.Type, obj); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/Protocol/Unity.cs b/src/Sentry.Unity/Protocol/Unity.cs index 2af77c513..a15fc1f21 100644 --- a/src/Sentry.Unity/Protocol/Unity.cs +++ b/src/Sentry.Unity/Protocol/Unity.cs @@ -4,116 +4,115 @@ using Sentry.Extensibility; using Sentry.Unity.Extensions; -namespace Sentry.Unity.Protocol +namespace Sentry.Unity.Protocol; + +public sealed class Unity : ISentryJsonSerializable { - public sealed class Unity : ISentryJsonSerializable - { - /// - /// Tells Sentry which type of context this is. - /// - public const string Type = "unity"; - - /// - /// The Unity editor version - /// - /// - /// 2019.4.40f1 - /// > - public string? EditorVersion { get; set; } - - /// - /// Application install mode. - /// - /// - /// Unknown, Store, DeveloperBuild, Adhoc, Enterprise, Editor - /// - public string? InstallMode { get; set; } - - /// - /// Support for various copy texture cases. - /// - /// - /// None, Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture - /// - public string? CopyTextureSupport { get; set; } - - /// - /// Application's actual rendering threading mode. - /// - /// - /// Direct, SingleThreaded, MultiThreaded, LegacyJobified, NativeGraphicsJobs, NativeGraphicsJobsWithoutRenderThread - /// - public string? RenderingThreadingMode { get; set; } - - /// - /// Instructs the game to try to render at a specified frame rate. - /// The default targetFrameRate is a special value of -1, which indicates that the game should render at the platform's default frame rate. This default rate depends on the platform. - /// Check https://docs.unity3d.com/ScriptReference/Application-targetFrameRate.html for more info. - /// - public string? TargetFrameRate { get; set; } - - internal Unity Clone() - => new() - { - InstallMode = InstallMode, - CopyTextureSupport = CopyTextureSupport, - RenderingThreadingMode = RenderingThreadingMode, - TargetFrameRate = TargetFrameRate - }; - - public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) + /// + /// Tells Sentry which type of context this is. + /// + public const string Type = "unity"; + + /// + /// The Unity editor version + /// + /// + /// 2019.4.40f1 + /// > + public string? EditorVersion { get; set; } + + /// + /// Application install mode. + /// + /// + /// Unknown, Store, DeveloperBuild, Adhoc, Enterprise, Editor + /// + public string? InstallMode { get; set; } + + /// + /// Support for various copy texture cases. + /// + /// + /// None, Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture + /// + public string? CopyTextureSupport { get; set; } + + /// + /// Application's actual rendering threading mode. + /// + /// + /// Direct, SingleThreaded, MultiThreaded, LegacyJobified, NativeGraphicsJobs, NativeGraphicsJobsWithoutRenderThread + /// + public string? RenderingThreadingMode { get; set; } + + /// + /// Instructs the game to try to render at a specified frame rate. + /// The default targetFrameRate is a special value of -1, which indicates that the game should render at the platform's default frame rate. This default rate depends on the platform. + /// Check https://docs.unity3d.com/ScriptReference/Application-targetFrameRate.html for more info. + /// + public string? TargetFrameRate { get; set; } + + internal Unity Clone() + => new() { - writer.WriteStartObject(); + InstallMode = InstallMode, + CopyTextureSupport = CopyTextureSupport, + RenderingThreadingMode = RenderingThreadingMode, + TargetFrameRate = TargetFrameRate + }; - writer.WriteString("type", Type); + public void WriteTo(Utf8JsonWriter writer, IDiagnosticLogger? logger) + { + writer.WriteStartObject(); - if (!string.IsNullOrWhiteSpace(EditorVersion)) - { - writer.WriteString("editor_version", EditorVersion); - } + writer.WriteString("type", Type); - if (!string.IsNullOrWhiteSpace(InstallMode)) - { - writer.WriteString("install_mode", InstallMode); - } + if (!string.IsNullOrWhiteSpace(EditorVersion)) + { + writer.WriteString("editor_version", EditorVersion); + } - if (!string.IsNullOrWhiteSpace(CopyTextureSupport)) - { - writer.WriteString("copy_texture_support", CopyTextureSupport); - } + if (!string.IsNullOrWhiteSpace(InstallMode)) + { + writer.WriteString("install_mode", InstallMode); + } - if (!string.IsNullOrWhiteSpace(RenderingThreadingMode)) - { - writer.WriteString("rendering_threading_mode", RenderingThreadingMode); - } + if (!string.IsNullOrWhiteSpace(CopyTextureSupport)) + { + writer.WriteString("copy_texture_support", CopyTextureSupport); + } - if (!string.IsNullOrWhiteSpace(TargetFrameRate)) - { - writer.WriteString("target_frame_rate", TargetFrameRate); - } + if (!string.IsNullOrWhiteSpace(RenderingThreadingMode)) + { + writer.WriteString("rendering_threading_mode", RenderingThreadingMode); + } - writer.WriteEndObject(); + if (!string.IsNullOrWhiteSpace(TargetFrameRate)) + { + writer.WriteString("target_frame_rate", TargetFrameRate); } - public static Unity FromJson(JsonElement json) - => new() - { - EditorVersion = json.GetPropertyOrNull("editor_version")?.GetString(), - InstallMode = json.GetPropertyOrNull("install_mode")?.GetString(), - CopyTextureSupport = json.GetPropertyOrNull("copy_texture_support")?.GetString(), - RenderingThreadingMode = json.GetPropertyOrNull("rendering_threading_mode")?.GetString(), - TargetFrameRate = json.GetPropertyOrNull("target_frame_rate")?.GetString() - }; - - public string ToJsonString(IDiagnosticLogger? logger = null) + writer.WriteEndObject(); + } + + public static Unity FromJson(JsonElement json) + => new() { - using var stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); + EditorVersion = json.GetPropertyOrNull("editor_version")?.GetString(), + InstallMode = json.GetPropertyOrNull("install_mode")?.GetString(), + CopyTextureSupport = json.GetPropertyOrNull("copy_texture_support")?.GetString(), + RenderingThreadingMode = json.GetPropertyOrNull("rendering_threading_mode")?.GetString(), + TargetFrameRate = json.GetPropertyOrNull("target_frame_rate")?.GetString() + }; + + public string ToJsonString(IDiagnosticLogger? logger = null) + { + using var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); - WriteTo(writer, logger); - writer.Flush(); + WriteTo(writer, logger); + writer.Flush(); - return Encoding.UTF8.GetString(stream.ToArray()); - } + return Encoding.UTF8.GetString(stream.ToArray()); } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/ScopeObserver.cs b/src/Sentry.Unity/ScopeObserver.cs index 68d7e5158..f3cddfddb 100644 --- a/src/Sentry.Unity/ScopeObserver.cs +++ b/src/Sentry.Unity/ScopeObserver.cs @@ -1,88 +1,87 @@ using Sentry.Unity.Json; -namespace Sentry.Unity +namespace Sentry.Unity; + +/// +/// Sentry Unity Scope Observer wrapper for the common behaviour accross platforms. +/// +public abstract class ScopeObserver : IScopeObserver { - /// - /// Sentry Unity Scope Observer wrapper for the common behaviour accross platforms. - /// - public abstract class ScopeObserver : IScopeObserver + private readonly SentryOptions _options; + private readonly string _name; + + public ScopeObserver( + string name, SentryOptions options) { - private readonly SentryOptions _options; - private readonly string _name; + _name = name; + _options = options; + } + + public void AddBreadcrumb(Breadcrumb breadcrumb) + { + _options.DiagnosticLogger?.Log(SentryLevel.Debug, + "{0} Scope Sync - Adding breadcrumb m:\"{1}\" l:\"{2}\"", null, _name, + breadcrumb.Message, breadcrumb.Level); + AddBreadcrumbImpl(breadcrumb); + } + + public abstract void AddBreadcrumbImpl(Breadcrumb breadcrumb); - public ScopeObserver( - string name, SentryOptions options) + public void SetExtra(string key, object? value) + { + var serialized = value is null ? null : SafeSerializer.SerializeSafely(value); + if (value is not null && serialized is null) { - _name = name; - _options = options; + _options.DiagnosticLogger?.Log(SentryLevel.Warning, + "{0} Scope Sync - SetExtra k:\"{1}\" v:\"{2}\" - value was serialized as null", + null, _name, key, value); } - - public void AddBreadcrumb(Breadcrumb breadcrumb) + else { _options.DiagnosticLogger?.Log(SentryLevel.Debug, - "{0} Scope Sync - Adding breadcrumb m:\"{1}\" l:\"{2}\"", null, _name, - breadcrumb.Message, breadcrumb.Level); - AddBreadcrumbImpl(breadcrumb); + "{0} Scope Sync - Setting Extra k:\"{1}\" v:\"{2}\"", null, _name, key, value); } + SetExtraImpl(key, serialized); + } - public abstract void AddBreadcrumbImpl(Breadcrumb breadcrumb); + public abstract void SetExtraImpl(string key, string? value); - public void SetExtra(string key, object? value) - { - var serialized = value is null ? null : SafeSerializer.SerializeSafely(value); - if (value is not null && serialized is null) - { - _options.DiagnosticLogger?.Log(SentryLevel.Warning, - "{0} Scope Sync - SetExtra k:\"{1}\" v:\"{2}\" - value was serialized as null", - null, _name, key, value); - } - else - { - _options.DiagnosticLogger?.Log(SentryLevel.Debug, - "{0} Scope Sync - Setting Extra k:\"{1}\" v:\"{2}\"", null, _name, key, value); - } - SetExtraImpl(key, serialized); - } + public void SetTag(string key, string value) + { + _options.DiagnosticLogger?.Log(SentryLevel.Debug, + "{0} Scope Sync - Setting Tag k:\"{1}\" v:\"{2}\"", null, _name, key, value); + SetTagImpl(key, value); + } - public abstract void SetExtraImpl(string key, string? value); + public abstract void SetTagImpl(string key, string value); - public void SetTag(string key, string value) - { - _options.DiagnosticLogger?.Log(SentryLevel.Debug, - "{0} Scope Sync - Setting Tag k:\"{1}\" v:\"{2}\"", null, _name, key, value); - SetTagImpl(key, value); - } + public void UnsetTag(string key) + { + _options.DiagnosticLogger?.Log( + SentryLevel.Debug, "{0} Scope Sync - Unsetting Tag k:\"{1}\"", null, _name, key); + UnsetTagImpl(key); + } - public abstract void SetTagImpl(string key, string value); + public abstract void UnsetTagImpl(string key); - public void UnsetTag(string key) + public void SetUser(SentryUser? user) + { + if (user is null) { _options.DiagnosticLogger?.Log( - SentryLevel.Debug, "{0} Scope Sync - Unsetting Tag k:\"{1}\"", null, _name, key); - UnsetTagImpl(key); + SentryLevel.Debug, "{0} Scope Sync - Unsetting User", null, _name); + UnsetUserImpl(); } - - public abstract void UnsetTagImpl(string key); - - public void SetUser(SentryUser? user) + else { - if (user is null) - { - _options.DiagnosticLogger?.Log( - SentryLevel.Debug, "{0} Scope Sync - Unsetting User", null, _name); - UnsetUserImpl(); - } - else - { - _options.DiagnosticLogger?.Log(SentryLevel.Debug, - "{0} Scope Sync - Setting User i:\"{1}\" n:\"{2}\"", null, _name, user.Id, - user.Username); - SetUserImpl(user); - } + _options.DiagnosticLogger?.Log(SentryLevel.Debug, + "{0} Scope Sync - Setting User i:\"{1}\" n:\"{2}\"", null, _name, user.Id, + user.Username); + SetUserImpl(user); } + } - public abstract void SetUserImpl(SentryUser user); + public abstract void SetUserImpl(SentryUser user); - public abstract void UnsetUserImpl(); - } -} + public abstract void UnsetUserImpl(); +} \ No newline at end of file diff --git a/src/Sentry.Unity/ScreenshotAttachment.cs b/src/Sentry.Unity/ScreenshotAttachment.cs index c93343e7d..ef6751b61 100644 --- a/src/Sentry.Unity/ScreenshotAttachment.cs +++ b/src/Sentry.Unity/ScreenshotAttachment.cs @@ -2,117 +2,116 @@ using Sentry.Extensibility; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class ScreenshotAttachment : SentryAttachment { - internal class ScreenshotAttachment : SentryAttachment + public ScreenshotAttachment(IAttachmentContent content) + : base(AttachmentType.Default, content, "screenshot.jpg", "image/jpeg") { } +} + +internal class ScreenshotAttachmentContent : IAttachmentContent +{ + private readonly SentryMonoBehaviour _behaviour; + private readonly SentryUnityOptions _options; + + public ScreenshotAttachmentContent(SentryUnityOptions options, SentryMonoBehaviour behaviour) { - public ScreenshotAttachment(IAttachmentContent content) - : base(AttachmentType.Default, content, "screenshot.jpg", "image/jpeg") { } + _behaviour = behaviour; + _options = options; } - internal class ScreenshotAttachmentContent : IAttachmentContent + public Stream GetStream() { - private readonly SentryMonoBehaviour _behaviour; - private readonly SentryUnityOptions _options; - - public ScreenshotAttachmentContent(SentryUnityOptions options, SentryMonoBehaviour behaviour) + // Note: we need to check explicitly that we're on the same thread. While Unity would throw otherwise + // when capturing the screenshot, it would only do so on development builds. On release, it just crashes... + if (!_behaviour.MainThreadData.IsMainThread()) { - _behaviour = behaviour; - _options = options; + _options.DiagnosticLogger?.LogDebug("Can't capture screenshots on other than main (UI) thread."); + return Stream.Null; } - public Stream GetStream() + if (Screen.width == 0 || Screen.height == 0) { - // Note: we need to check explicitly that we're on the same thread. While Unity would throw otherwise - // when capturing the screenshot, it would only do so on development builds. On release, it just crashes... - if (!_behaviour.MainThreadData.IsMainThread()) - { - _options.DiagnosticLogger?.LogDebug("Can't capture screenshots on other than main (UI) thread."); - return Stream.Null; - } - - 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); - // Returning a memory stream with a capacity of 1 so we can smoke-test the attempt to capture a screenshot in CI - return new MemoryStream(new byte[] { 0x00 }); + _options.DiagnosticLogger?.LogWarning("Can't capture screenshots on a screen with a resolution of '{0}x{1}'.", Screen.width, Screen.height); + // Returning a memory stream with a capacity of 1 so we can smoke-test the attempt to capture a screenshot in CI + return new MemoryStream(new byte[] { 0x00 }); - } - - return new MemoryStream(CaptureScreenshot(Screen.width, Screen.height)); } - internal static int GetTargetResolution(ScreenshotQuality quality) + return new MemoryStream(CaptureScreenshot(Screen.width, Screen.height)); + } + + internal static int GetTargetResolution(ScreenshotQuality quality) + { + return quality switch { - return quality switch - { - ScreenshotQuality.High => 1920, // 1080p - ScreenshotQuality.Medium => 1280, // 720p - ScreenshotQuality.Low => 854, // 480p - _ => 854 // Fallback - }; - } + ScreenshotQuality.High => 1920, // 1080p + ScreenshotQuality.Medium => 1280, // 720p + ScreenshotQuality.Low => 854, // 480p + _ => 854 // Fallback + }; + } - internal byte[] CaptureScreenshot(int width, int height) + internal byte[] CaptureScreenshot(int width, int height) + { + // Make sure the screenshot size does not exceed the target size by scaling the image while conserving the + // original ratio based on which, width or height, is the smaller + if (_options.ScreenshotQuality is not ScreenshotQuality.Full) { - // Make sure the screenshot size does not exceed the target size by scaling the image while conserving the - // original ratio based on which, width or height, is the smaller - if (_options.ScreenshotQuality is not ScreenshotQuality.Full) + var targetResolution = GetTargetResolution(_options.ScreenshotQuality); + var ratioW = targetResolution / (float)width; + var ratioH = targetResolution / (float)height; + var ratio = Mathf.Min(ratioH, ratioW); + if (ratio is > 0.0f and < 1.0f) { - var targetResolution = GetTargetResolution(_options.ScreenshotQuality); - var ratioW = targetResolution / (float)width; - var ratioH = targetResolution / (float)height; - var ratio = Mathf.Min(ratioH, ratioW); - if (ratio is > 0.0f and < 1.0f) - { - width = Mathf.FloorToInt(width * ratio); - height = Mathf.FloorToInt(height * ratio); - } + width = Mathf.FloorToInt(width * ratio); + height = Mathf.FloorToInt(height * ratio); } + } - // Captures the current screenshot synchronously. - var screenshot = new Texture2D(width, height, TextureFormat.RGB24, false); - var renderTextureFull = RenderTexture.GetTemporary(Screen.width, Screen.height); - ScreenCapture.CaptureScreenshotIntoRenderTexture(renderTextureFull); - var renderTextureResized = RenderTexture.GetTemporary(width, height); - - // The image may be mirrored on some platforms - mirror it back. - // See https://docs.unity3d.com/2019.4/Documentation/Manual/SL-PlatformDifferences.html for more info. - // Note, we can't use the `UNITY_UV_STARTS_AT_TOP` macro because it's only available in shaders. - // Instead, there's https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SystemInfo-graphicsUVStartsAtTop.html - if (SentrySystemInfoAdapter.Instance.GraphicsUVStartsAtTop ?? true) - { - Graphics.Blit(renderTextureFull, renderTextureResized, new Vector2(1, -1), new Vector2(0, 1)); - } - else - { - Graphics.Blit(renderTextureFull, renderTextureResized); - } - RenderTexture.ReleaseTemporary(renderTextureFull); - // Remember the previous render target and change it to our target texture. - var previousRenderTexture = RenderTexture.active; - RenderTexture.active = renderTextureResized; + // Captures the current screenshot synchronously. + var screenshot = new Texture2D(width, height, TextureFormat.RGB24, false); + var renderTextureFull = RenderTexture.GetTemporary(Screen.width, Screen.height); + ScreenCapture.CaptureScreenshotIntoRenderTexture(renderTextureFull); + var renderTextureResized = RenderTexture.GetTemporary(width, height); - try - { - // actually copy from the current render target a texture & read data from the active RenderTexture - screenshot.ReadPixels(new Rect(0, 0, width, height), 0, 0); - screenshot.Apply(); - } - finally - { - // Restore the render target. - RenderTexture.active = previousRenderTexture; - } + // The image may be mirrored on some platforms - mirror it back. + // See https://docs.unity3d.com/2019.4/Documentation/Manual/SL-PlatformDifferences.html for more info. + // Note, we can't use the `UNITY_UV_STARTS_AT_TOP` macro because it's only available in shaders. + // Instead, there's https://docs.unity3d.com/2019.4/Documentation/ScriptReference/SystemInfo-graphicsUVStartsAtTop.html + if (SentrySystemInfoAdapter.Instance.GraphicsUVStartsAtTop ?? true) + { + Graphics.Blit(renderTextureFull, renderTextureResized, new Vector2(1, -1), new Vector2(0, 1)); + } + else + { + Graphics.Blit(renderTextureFull, renderTextureResized); + } + RenderTexture.ReleaseTemporary(renderTextureFull); + // Remember the previous render target and change it to our target texture. + var previousRenderTexture = RenderTexture.active; + RenderTexture.active = renderTextureResized; - RenderTexture.ReleaseTemporary(renderTextureResized); + try + { + // actually copy from the current render target a texture & read data from the active RenderTexture + screenshot.ReadPixels(new Rect(0, 0, width, height), 0, 0); + screenshot.Apply(); + } + finally + { + // Restore the render target. + RenderTexture.active = previousRenderTexture; + } - var bytes = screenshot.EncodeToJPG(_options.ScreenshotCompression); - Object.Destroy(screenshot); + RenderTexture.ReleaseTemporary(renderTextureResized); - _options.DiagnosticLogger?.Log(SentryLevel.Debug, - "Screenshot captured at {0}x{1}: {2} bytes", null, width, height, bytes.Length); - return bytes; - } + var bytes = screenshot.EncodeToJPG(_options.ScreenshotCompression); + Object.Destroy(screenshot); + + _options.DiagnosticLogger?.Log(SentryLevel.Debug, + "Screenshot captured at {0}x{1}: {2} bytes", null, width, height, bytes.Length); + return bytes; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index 95481a3bc..ca37843f6 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -4,290 +4,289 @@ using Sentry.Unity.Integrations; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +public class ScriptableSentryUnityOptions : ScriptableObject { - public class ScriptableSentryUnityOptions : ScriptableObject + /// + /// Relative to Assets/Resources + /// + internal const string ConfigRootFolder = "Sentry"; + + /// + /// Main Sentry config name for Unity + /// + internal const string ConfigName = "SentryOptions"; + + /// + /// Path for the config for Unity + /// + public static string GetConfigPath(string? notDefaultConfigName = null) + => $"Assets/Resources/{ConfigRootFolder}/{notDefaultConfigName ?? ConfigName}.asset"; + + [field: SerializeField] public bool Enabled { get; set; } = true; + + [field: SerializeField] public string? Dsn { get; set; } + [field: SerializeField] public bool CaptureInEditor { get; set; } = true; + + [field: SerializeField] public bool EnableLogDebouncing { get; set; } = false; + [field: SerializeField] public int DebounceTimeLog { get; set; } = (int)TimeSpan.FromSeconds(1).TotalMilliseconds; + [field: SerializeField] public int DebounceTimeWarning { get; set; } = (int)TimeSpan.FromSeconds(1).TotalMilliseconds; + [field: SerializeField] public int DebounceTimeError { get; set; } = (int)TimeSpan.FromSeconds(1).TotalMilliseconds; + + [field: SerializeField] public double TracesSampleRate { get; set; } = 0; + [field: SerializeField] public bool AutoStartupTraces { get; set; } = true; + [field: SerializeField] public bool AutoSceneLoadTraces { get; set; } = true; + [field: SerializeField] public bool AutoAwakeTraces { get; set; } = false; + + [field: SerializeField] public bool AutoSessionTracking { get; set; } = true; + + /// + /// Interval in milliseconds a session terminates if put in the background. + /// + [field: SerializeField] public int AutoSessionTrackingInterval { get; set; } = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; + + [field: SerializeField] public string ReleaseOverride { get; set; } = string.Empty; + [field: SerializeField] public string EnvironmentOverride { get; set; } = string.Empty; + [field: SerializeField] public bool AttachStacktrace { get; set; } + [field: SerializeField] public bool AttachScreenshot { get; set; } + [field: SerializeField] public ScreenshotQuality ScreenshotQuality { get; set; } = ScreenshotQuality.High; + [field: SerializeField] public int ScreenshotCompression { get; set; } = 75; + + [field: SerializeField] public bool AttachViewHierarchy { get; set; } = false; + [field: SerializeField] public int MaxViewHierarchyRootObjects { get; set; } = 100; + [field: SerializeField] public int MaxViewHierarchyObjectChildCount { get; set; } = 20; + [field: SerializeField] public int MaxViewHierarchyDepth { get; set; } = 10; + + [field: SerializeField] public bool BreadcrumbsForLogs { get; set; } = true; + [field: SerializeField] public bool BreadcrumbsForWarnings { get; set; } = true; + [field: SerializeField] public bool BreadcrumbsForAsserts { get; set; } = true; + [field: SerializeField] public bool BreadcrumbsForErrors { get; set; } = true; + [field: SerializeField] public bool BreadcrumbsForExceptions { get; set; } = true; + + [field: SerializeField] public int MaxBreadcrumbs { get; set; } = SentryConstants.DefaultMaxBreadcrumbs; + + [field: SerializeField] public ReportAssembliesMode ReportAssembliesMode { get; set; } = ReportAssembliesMode.Version; + [field: SerializeField] public bool SendDefaultPii { get; set; } + [field: SerializeField] public bool IsEnvironmentUser { get; set; } + + [field: SerializeField] public bool EnableOfflineCaching { get; set; } = true; + [field: SerializeField] public int MaxCacheItems { get; set; } = 30; + + /// + /// Time in milliseconds for flushing the cache at startup + /// + [field: SerializeField] public int InitCacheFlushTimeout { get; set; } = (int)TimeSpan.Zero.TotalMilliseconds; + [field: SerializeField] public float SampleRate { get; set; } = 1.0f; + [field: SerializeField] public int ShutdownTimeout { get; set; } = 2000; + [field: SerializeField] public int MaxQueueItems { get; set; } = 30; + + [field: SerializeField] public bool AnrDetectionEnabled { get; set; } = true; + [field: SerializeField] public int AnrTimeout { get; set; } = (int)TimeSpan.FromSeconds(5).TotalMilliseconds; + + [field: SerializeField] public bool CaptureFailedRequests { get; set; } = true; + + // We hold the status codes as a list of ints to be able to serialize it in the editor. + [field: SerializeField] public List FailedRequestStatusCodes { get; set; } = new() { 500, 599 }; + + [field: SerializeField] public bool FilterBadGatewayExceptions { get; set; } = true; + [field: SerializeField] public bool FilterWebExceptions { get; set; } = true; + [field: SerializeField] public bool FilterSocketExceptions { get; set; } = true; + + [field: SerializeField] public bool IosNativeSupportEnabled { get; set; } = true; + [field: SerializeField] public bool AndroidNativeSupportEnabled { get; set; } = true; + [field: SerializeField] public bool NdkIntegrationEnabled { get; set; } = true; + [field: SerializeField] public bool NdkScopeSyncEnabled { get; set; } = true; + [field: SerializeField] public int PostGenerateGradleProjectCallbackOrder { get; set; } = 1; + [field: SerializeField] public bool WindowsNativeSupportEnabled { get; set; } = true; + [field: SerializeField] public bool MacosNativeSupportEnabled { get; set; } = true; + [field: SerializeField] public bool LinuxNativeSupportEnabled { get; set; } = true; + + [field: SerializeField] public bool Il2CppLineNumberSupportEnabled { get; set; } = true; + + [field: SerializeField] public SentryRuntimeOptionsConfiguration? RuntimeOptionsConfiguration { get; set; } + [field: SerializeField] public SentryBuildTimeOptionsConfiguration? BuildTimeOptionsConfiguration { get; set; } + + [field: SerializeField] public bool Debug { get; set; } = true; + [field: SerializeField] public bool DebugOnlyInEditor { get; set; } = true; + [field: SerializeField] public SentryLevel DiagnosticLevel { get; set; } = SentryLevel.Warning; + + /// + /// Loads the ScriptableSentryUnityOptions from `Resource`. + /// + /// The SentryUnityOptions generated from the ScriptableSentryUnityOptions + /// + /// Used for loading the SentryUnityOptions from the ScriptableSentryUnityOptions during runtime. + /// + public static SentryUnityOptions? LoadSentryUnityOptions(ISentryUnityInfo unityInfo) { - /// - /// Relative to Assets/Resources - /// - internal const string ConfigRootFolder = "Sentry"; - - /// - /// Main Sentry config name for Unity - /// - internal const string ConfigName = "SentryOptions"; - - /// - /// Path for the config for Unity - /// - public static string GetConfigPath(string? notDefaultConfigName = null) - => $"Assets/Resources/{ConfigRootFolder}/{notDefaultConfigName ?? ConfigName}.asset"; - - [field: SerializeField] public bool Enabled { get; set; } = true; - - [field: SerializeField] public string? Dsn { get; set; } - [field: SerializeField] public bool CaptureInEditor { get; set; } = true; - - [field: SerializeField] public bool EnableLogDebouncing { get; set; } = false; - [field: SerializeField] public int DebounceTimeLog { get; set; } = (int)TimeSpan.FromSeconds(1).TotalMilliseconds; - [field: SerializeField] public int DebounceTimeWarning { get; set; } = (int)TimeSpan.FromSeconds(1).TotalMilliseconds; - [field: SerializeField] public int DebounceTimeError { get; set; } = (int)TimeSpan.FromSeconds(1).TotalMilliseconds; - - [field: SerializeField] public double TracesSampleRate { get; set; } = 0; - [field: SerializeField] public bool AutoStartupTraces { get; set; } = true; - [field: SerializeField] public bool AutoSceneLoadTraces { get; set; } = true; - [field: SerializeField] public bool AutoAwakeTraces { get; set; } = false; - - [field: SerializeField] public bool AutoSessionTracking { get; set; } = true; - - /// - /// Interval in milliseconds a session terminates if put in the background. - /// - [field: SerializeField] public int AutoSessionTrackingInterval { get; set; } = (int)TimeSpan.FromSeconds(30).TotalMilliseconds; - - [field: SerializeField] public string ReleaseOverride { get; set; } = string.Empty; - [field: SerializeField] public string EnvironmentOverride { get; set; } = string.Empty; - [field: SerializeField] public bool AttachStacktrace { get; set; } - [field: SerializeField] public bool AttachScreenshot { get; set; } - [field: SerializeField] public ScreenshotQuality ScreenshotQuality { get; set; } = ScreenshotQuality.High; - [field: SerializeField] public int ScreenshotCompression { get; set; } = 75; - - [field: SerializeField] public bool AttachViewHierarchy { get; set; } = false; - [field: SerializeField] public int MaxViewHierarchyRootObjects { get; set; } = 100; - [field: SerializeField] public int MaxViewHierarchyObjectChildCount { get; set; } = 20; - [field: SerializeField] public int MaxViewHierarchyDepth { get; set; } = 10; - - [field: SerializeField] public bool BreadcrumbsForLogs { get; set; } = true; - [field: SerializeField] public bool BreadcrumbsForWarnings { get; set; } = true; - [field: SerializeField] public bool BreadcrumbsForAsserts { get; set; } = true; - [field: SerializeField] public bool BreadcrumbsForErrors { get; set; } = true; - [field: SerializeField] public bool BreadcrumbsForExceptions { get; set; } = true; - - [field: SerializeField] public int MaxBreadcrumbs { get; set; } = SentryConstants.DefaultMaxBreadcrumbs; - - [field: SerializeField] public ReportAssembliesMode ReportAssembliesMode { get; set; } = ReportAssembliesMode.Version; - [field: SerializeField] public bool SendDefaultPii { get; set; } - [field: SerializeField] public bool IsEnvironmentUser { get; set; } - - [field: SerializeField] public bool EnableOfflineCaching { get; set; } = true; - [field: SerializeField] public int MaxCacheItems { get; set; } = 30; - - /// - /// Time in milliseconds for flushing the cache at startup - /// - [field: SerializeField] public int InitCacheFlushTimeout { get; set; } = (int)TimeSpan.Zero.TotalMilliseconds; - [field: SerializeField] public float SampleRate { get; set; } = 1.0f; - [field: SerializeField] public int ShutdownTimeout { get; set; } = 2000; - [field: SerializeField] public int MaxQueueItems { get; set; } = 30; - - [field: SerializeField] public bool AnrDetectionEnabled { get; set; } = true; - [field: SerializeField] public int AnrTimeout { get; set; } = (int)TimeSpan.FromSeconds(5).TotalMilliseconds; - - [field: SerializeField] public bool CaptureFailedRequests { get; set; } = true; - - // We hold the status codes as a list of ints to be able to serialize it in the editor. - [field: SerializeField] public List FailedRequestStatusCodes { get; set; } = new() { 500, 599 }; - - [field: SerializeField] public bool FilterBadGatewayExceptions { get; set; } = true; - [field: SerializeField] public bool FilterWebExceptions { get; set; } = true; - [field: SerializeField] public bool FilterSocketExceptions { get; set; } = true; - - [field: SerializeField] public bool IosNativeSupportEnabled { get; set; } = true; - [field: SerializeField] public bool AndroidNativeSupportEnabled { get; set; } = true; - [field: SerializeField] public bool NdkIntegrationEnabled { get; set; } = true; - [field: SerializeField] public bool NdkScopeSyncEnabled { get; set; } = true; - [field: SerializeField] public int PostGenerateGradleProjectCallbackOrder { get; set; } = 1; - [field: SerializeField] public bool WindowsNativeSupportEnabled { get; set; } = true; - [field: SerializeField] public bool MacosNativeSupportEnabled { get; set; } = true; - [field: SerializeField] public bool LinuxNativeSupportEnabled { get; set; } = true; - - [field: SerializeField] public bool Il2CppLineNumberSupportEnabled { get; set; } = true; - - [field: SerializeField] public SentryRuntimeOptionsConfiguration? RuntimeOptionsConfiguration { get; set; } - [field: SerializeField] public SentryBuildTimeOptionsConfiguration? BuildTimeOptionsConfiguration { get; set; } - - [field: SerializeField] public bool Debug { get; set; } = true; - [field: SerializeField] public bool DebugOnlyInEditor { get; set; } = true; - [field: SerializeField] public SentryLevel DiagnosticLevel { get; set; } = SentryLevel.Warning; - - /// - /// Loads the ScriptableSentryUnityOptions from `Resource`. - /// - /// The SentryUnityOptions generated from the ScriptableSentryUnityOptions - /// - /// Used for loading the SentryUnityOptions from the ScriptableSentryUnityOptions during runtime. - /// - public static SentryUnityOptions? LoadSentryUnityOptions(ISentryUnityInfo unityInfo) + var scriptableOptions = Resources.Load($"{ConfigRootFolder}/{ConfigName}"); + if (scriptableOptions is not null) { - var scriptableOptions = Resources.Load($"{ConfigRootFolder}/{ConfigName}"); - if (scriptableOptions is not null) - { - return scriptableOptions.ToSentryUnityOptions(false, unityInfo); - } - - return null; + return scriptableOptions.ToSentryUnityOptions(false, unityInfo); } - internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityInfo? unityInfo, IApplication? application = null) - { - application ??= ApplicationAdapter.Instance; - - var options = new SentryUnityOptions(isBuilding, application) - { - Enabled = Enabled, - Dsn = Dsn, - CaptureInEditor = CaptureInEditor, - EnableLogDebouncing = EnableLogDebouncing, - DebounceTimeLog = TimeSpan.FromMilliseconds(DebounceTimeLog), - DebounceTimeWarning = TimeSpan.FromMilliseconds(DebounceTimeWarning), - DebounceTimeError = TimeSpan.FromMilliseconds(DebounceTimeError), - TracesSampleRate = TracesSampleRate, - AutoStartupTraces = AutoStartupTraces, - AutoSceneLoadTraces = AutoSceneLoadTraces, - AutoSessionTracking = AutoSessionTracking, - AutoSessionTrackingInterval = TimeSpan.FromMilliseconds(AutoSessionTrackingInterval), - AttachStacktrace = AttachStacktrace, - AttachScreenshot = AttachScreenshot, - ScreenshotQuality = ScreenshotQuality, - ScreenshotCompression = ScreenshotCompression, - AttachViewHierarchy = AttachViewHierarchy, - MaxViewHierarchyRootObjects = MaxViewHierarchyRootObjects, - MaxViewHierarchyObjectChildCount = MaxViewHierarchyObjectChildCount, - MaxViewHierarchyDepth = MaxViewHierarchyDepth, - MaxBreadcrumbs = MaxBreadcrumbs, - ReportAssembliesMode = ReportAssembliesMode, - SendDefaultPii = SendDefaultPii, - IsEnvironmentUser = IsEnvironmentUser, - MaxCacheItems = MaxCacheItems, - CacheDirectoryPath = EnableOfflineCaching ? application.PersistentDataPath : null, - InitCacheFlushTimeout = TimeSpan.FromMilliseconds(InitCacheFlushTimeout), - SampleRate = SampleRate == 1.0f ? null : SampleRate, // To skip the random check for dropping events - ShutdownTimeout = TimeSpan.FromMilliseconds(ShutdownTimeout), - MaxQueueItems = MaxQueueItems, - // Because SentryOptions.Debug is used inside the .NET SDK to setup the ConsoleLogger we - // need to set it here directly. - Debug = ShouldDebug(application.IsEditor && !isBuilding), - DiagnosticLevel = DiagnosticLevel, - AnrTimeout = TimeSpan.FromMilliseconds(AnrTimeout), - CaptureFailedRequests = CaptureFailedRequests, - FilterBadGatewayExceptions = FilterBadGatewayExceptions, - IosNativeSupportEnabled = IosNativeSupportEnabled, - AndroidNativeSupportEnabled = AndroidNativeSupportEnabled, - NdkIntegrationEnabled = NdkIntegrationEnabled, - WindowsNativeSupportEnabled = WindowsNativeSupportEnabled, - MacosNativeSupportEnabled = MacosNativeSupportEnabled, - LinuxNativeSupportEnabled = LinuxNativeSupportEnabled, - Il2CppLineNumberSupportEnabled = Il2CppLineNumberSupportEnabled, - PerformanceAutoInstrumentationEnabled = AutoAwakeTraces, - }; - - if (!string.IsNullOrWhiteSpace(ReleaseOverride)) - { - options.Release = ReleaseOverride; - } - - if (!string.IsNullOrWhiteSpace(EnvironmentOverride)) - { - options.Environment = EnvironmentOverride; - } + return null; + } - options.AddBreadcrumbsForLogType[LogType.Log] = BreadcrumbsForLogs; - options.AddBreadcrumbsForLogType[LogType.Warning] = BreadcrumbsForWarnings; - options.AddBreadcrumbsForLogType[LogType.Assert] = BreadcrumbsForAsserts; - options.AddBreadcrumbsForLogType[LogType.Error] = BreadcrumbsForErrors; - options.AddBreadcrumbsForLogType[LogType.Exception] = BreadcrumbsForExceptions; + internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityInfo? unityInfo, IApplication? application = null) + { + application ??= ApplicationAdapter.Instance; - options.FailedRequestStatusCodes = new List(); - for (var i = 0; i < FailedRequestStatusCodes.Count; i += 2) - { - options.FailedRequestStatusCodes.Add( - new HttpStatusCodeRange(FailedRequestStatusCodes[i], FailedRequestStatusCodes[i + 1])); - } + var options = new SentryUnityOptions(isBuilding, application) + { + Enabled = Enabled, + Dsn = Dsn, + CaptureInEditor = CaptureInEditor, + EnableLogDebouncing = EnableLogDebouncing, + DebounceTimeLog = TimeSpan.FromMilliseconds(DebounceTimeLog), + DebounceTimeWarning = TimeSpan.FromMilliseconds(DebounceTimeWarning), + DebounceTimeError = TimeSpan.FromMilliseconds(DebounceTimeError), + TracesSampleRate = TracesSampleRate, + AutoStartupTraces = AutoStartupTraces, + AutoSceneLoadTraces = AutoSceneLoadTraces, + AutoSessionTracking = AutoSessionTracking, + AutoSessionTrackingInterval = TimeSpan.FromMilliseconds(AutoSessionTrackingInterval), + AttachStacktrace = AttachStacktrace, + AttachScreenshot = AttachScreenshot, + ScreenshotQuality = ScreenshotQuality, + ScreenshotCompression = ScreenshotCompression, + AttachViewHierarchy = AttachViewHierarchy, + MaxViewHierarchyRootObjects = MaxViewHierarchyRootObjects, + MaxViewHierarchyObjectChildCount = MaxViewHierarchyObjectChildCount, + MaxViewHierarchyDepth = MaxViewHierarchyDepth, + MaxBreadcrumbs = MaxBreadcrumbs, + ReportAssembliesMode = ReportAssembliesMode, + SendDefaultPii = SendDefaultPii, + IsEnvironmentUser = IsEnvironmentUser, + MaxCacheItems = MaxCacheItems, + CacheDirectoryPath = EnableOfflineCaching ? application.PersistentDataPath : null, + InitCacheFlushTimeout = TimeSpan.FromMilliseconds(InitCacheFlushTimeout), + SampleRate = SampleRate == 1.0f ? null : SampleRate, // To skip the random check for dropping events + ShutdownTimeout = TimeSpan.FromMilliseconds(ShutdownTimeout), + MaxQueueItems = MaxQueueItems, + // Because SentryOptions.Debug is used inside the .NET SDK to setup the ConsoleLogger we + // need to set it here directly. + Debug = ShouldDebug(application.IsEditor && !isBuilding), + DiagnosticLevel = DiagnosticLevel, + AnrTimeout = TimeSpan.FromMilliseconds(AnrTimeout), + CaptureFailedRequests = CaptureFailedRequests, + FilterBadGatewayExceptions = FilterBadGatewayExceptions, + IosNativeSupportEnabled = IosNativeSupportEnabled, + AndroidNativeSupportEnabled = AndroidNativeSupportEnabled, + NdkIntegrationEnabled = NdkIntegrationEnabled, + WindowsNativeSupportEnabled = WindowsNativeSupportEnabled, + MacosNativeSupportEnabled = MacosNativeSupportEnabled, + LinuxNativeSupportEnabled = LinuxNativeSupportEnabled, + Il2CppLineNumberSupportEnabled = Il2CppLineNumberSupportEnabled, + PerformanceAutoInstrumentationEnabled = AutoAwakeTraces, + }; + + if (!string.IsNullOrWhiteSpace(ReleaseOverride)) + { + options.Release = ReleaseOverride; + } - options.SetupLogging(); + if (!string.IsNullOrWhiteSpace(EnvironmentOverride)) + { + options.Environment = EnvironmentOverride; + } - // Bail early if we're building the player. - if (isBuilding) - { - return options; - } + options.AddBreadcrumbsForLogType[LogType.Log] = BreadcrumbsForLogs; + options.AddBreadcrumbsForLogType[LogType.Warning] = BreadcrumbsForWarnings; + options.AddBreadcrumbsForLogType[LogType.Assert] = BreadcrumbsForAsserts; + options.AddBreadcrumbsForLogType[LogType.Error] = BreadcrumbsForErrors; + options.AddBreadcrumbsForLogType[LogType.Exception] = BreadcrumbsForExceptions; - if (RuntimeOptionsConfiguration != null) - { - // This has to happen in between options object creation and updating the options based on programmatic changes - RuntimeOptionsConfiguration.Configure(options); - } + options.FailedRequestStatusCodes = new List(); + for (var i = 0; i < FailedRequestStatusCodes.Count; i += 2) + { + options.FailedRequestStatusCodes.Add( + new HttpStatusCodeRange(FailedRequestStatusCodes[i], FailedRequestStatusCodes[i + 1])); + } - if (!application.IsEditor && options.Il2CppLineNumberSupportEnabled && unityInfo is not null) - { - options.AddIl2CppExceptionProcessor(unityInfo); - } + options.SetupLogging(); - HandlePlatformRestrictions(options, application, unityInfo); - HandleExceptionFilter(options); + // Bail early if we're building the player. + if (isBuilding) + { + return options; + } - if (!AnrDetectionEnabled) - { - options.DisableAnrIntegration(); - } + if (RuntimeOptionsConfiguration != null) + { + // This has to happen in between options object creation and updating the options based on programmatic changes + RuntimeOptionsConfiguration.Configure(options); + } - return options; + if (!application.IsEditor && options.Il2CppLineNumberSupportEnabled && unityInfo is not null) + { + options.AddIl2CppExceptionProcessor(unityInfo); } - private void HandlePlatformRestrictions(SentryUnityOptions options, IApplication application, ISentryUnityInfo? unityInfo) + HandlePlatformRestrictions(options, application, unityInfo); + HandleExceptionFilter(options); + + if (!AnrDetectionEnabled) { - if (unityInfo?.IsKnownPlatform() == false) - { - // This is only provided on a best-effort basis for other than the explicitly supported platforms. - if (options.BackgroundWorker is null) - { - options.DiagnosticLogger?.LogDebug("Platform support for background thread execution is unknown: using WebBackgroundWorker."); - options.BackgroundWorker = new WebBackgroundWorker(options, SentryMonoBehaviour.Instance); - } - - // Disable offline caching regardless whether is was enabled or not. - options.CacheDirectoryPath = null; - if (EnableOfflineCaching) - { - options.DiagnosticLogger?.LogDebug("Platform support for offline caching is unknown: disabling."); - } - - // Requires file access, see https://github.com/getsentry/sentry-unity/issues/290#issuecomment-1163608988 - if (options.AutoSessionTracking) - { - options.DiagnosticLogger?.LogDebug("Platform support for automatic session tracking is unknown: disabling."); - options.AutoSessionTracking = false; - } - } + options.DisableAnrIntegration(); } - private void HandleExceptionFilter(SentryUnityOptions options) + return options; + } + + private void HandlePlatformRestrictions(SentryUnityOptions options, IApplication application, ISentryUnityInfo? unityInfo) + { + if (unityInfo?.IsKnownPlatform() == false) { - if (!options.FilterBadGatewayExceptions) + // This is only provided on a best-effort basis for other than the explicitly supported platforms. + if (options.BackgroundWorker is null) { - options.RemoveExceptionFilter(); + options.DiagnosticLogger?.LogDebug("Platform support for background thread execution is unknown: using WebBackgroundWorker."); + options.BackgroundWorker = new WebBackgroundWorker(options, SentryMonoBehaviour.Instance); } - if (!FilterWebExceptions) + // Disable offline caching regardless whether is was enabled or not. + options.CacheDirectoryPath = null; + if (EnableOfflineCaching) { - options.RemoveExceptionFilter(); + options.DiagnosticLogger?.LogDebug("Platform support for offline caching is unknown: disabling."); } - if (!FilterSocketExceptions) + // Requires file access, see https://github.com/getsentry/sentry-unity/issues/290#issuecomment-1163608988 + if (options.AutoSessionTracking) { - options.RemoveExceptionFilter(); + options.DiagnosticLogger?.LogDebug("Platform support for automatic session tracking is unknown: disabling."); + options.AutoSessionTracking = false; } } + } - internal bool ShouldDebug(bool isEditorPlayer) + private void HandleExceptionFilter(SentryUnityOptions options) + { + if (!options.FilterBadGatewayExceptions) { - if (!isEditorPlayer) - { - return !DebugOnlyInEditor && Debug; - } + options.RemoveExceptionFilter(); + } + + if (!FilterWebExceptions) + { + options.RemoveExceptionFilter(); + } - return Debug; + if (!FilterSocketExceptions) + { + options.RemoveExceptionFilter(); } } -} + + internal bool ShouldDebug(bool isEditorPlayer) + { + if (!isEditorPlayer) + { + return !DebugOnlyInEditor && Debug; + } + + return Debug; + } +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs b/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs index 75ed1b38a..d80b4826a 100644 --- a/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs +++ b/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs @@ -1,15 +1,14 @@ using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +public abstract class SentryBuildTimeOptionsConfiguration : ScriptableObject { - public abstract class SentryBuildTimeOptionsConfiguration : ScriptableObject - { - /// - /// Called during app build. Changes made here will affect build-time processing, symbol upload, etc. - /// Additionally, because iOS, macOS and Android native error handling is configured at build time, - /// you can make changes to these options here. - /// - /// - public abstract void Configure(SentryUnityOptions options, SentryCliOptions cliOptions); - } -} + /// + /// Called during app build. Changes made here will affect build-time processing, symbol upload, etc. + /// Additionally, because iOS, macOS and Android native error handling is configured at build time, + /// you can make changes to these options here. + /// + /// + public abstract void Configure(SentryUnityOptions options, SentryCliOptions cliOptions); +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryCliOptions.cs b/src/Sentry.Unity/SentryCliOptions.cs index f0ae5eddd..2398be11f 100644 --- a/src/Sentry.Unity/SentryCliOptions.cs +++ b/src/Sentry.Unity/SentryCliOptions.cs @@ -2,74 +2,73 @@ using Sentry.Extensibility; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +[Serializable] +public sealed class SentryCliOptions : ScriptableObject { - [Serializable] - public sealed class SentryCliOptions : ScriptableObject - { - /// - /// Sentry CLI config name for Unity - /// - internal const string ConfigName = "SentryCliOptions"; + /// + /// Sentry CLI config name for Unity + /// + internal const string ConfigName = "SentryCliOptions"; - public const string EditorMenuPath = "Tools -> Sentry -> Debug Symbols"; + public const string EditorMenuPath = "Tools -> Sentry -> Debug Symbols"; - [field: SerializeField] public bool UploadSymbols { get; set; } = true; - [field: SerializeField] public bool UploadDevelopmentSymbols { get; set; } = false; - [field: SerializeField] public bool UploadSources { get; set; } = false; - [field: SerializeField] public string? UrlOverride { get; set; } - [field: SerializeField] public string? Auth { get; set; } - [field: SerializeField] public string? Organization { get; set; } - [field: SerializeField] public string? Project { get; set; } - [field: SerializeField] public bool IgnoreCliErrors { get; set; } = false; + [field: SerializeField] public bool UploadSymbols { get; set; } = true; + [field: SerializeField] public bool UploadDevelopmentSymbols { get; set; } = false; + [field: SerializeField] public bool UploadSources { get; set; } = false; + [field: SerializeField] public string? UrlOverride { get; set; } + [field: SerializeField] public string? Auth { get; set; } + [field: SerializeField] public string? Organization { get; set; } + [field: SerializeField] public string? Project { get; set; } + [field: SerializeField] public bool IgnoreCliErrors { get; set; } = false; - internal static string GetConfigPath(string? notDefaultConfigName = null) - => $"Assets/Plugins/Sentry/{notDefaultConfigName ?? ConfigName}.asset"; + internal static string GetConfigPath(string? notDefaultConfigName = null) + => $"Assets/Plugins/Sentry/{notDefaultConfigName ?? ConfigName}.asset"; - private static void MissingFieldWarning(IDiagnosticLogger? logger, string name) => - logger?.LogWarning("{0} missing. Please set it under {1}", name, EditorMenuPath); + private static void MissingFieldWarning(IDiagnosticLogger? logger, string name) => + logger?.LogWarning("{0} missing. Please set it under {1}", name, EditorMenuPath); - public bool IsValid(IDiagnosticLogger? logger, bool isDevelopmentBuild) + public bool IsValid(IDiagnosticLogger? logger, bool isDevelopmentBuild) + { + if (!UploadSymbols) { - if (!UploadSymbols) - { - logger?.LogDebug("Automated symbols upload has been disabled."); - return false; - } - - if (isDevelopmentBuild && !UploadDevelopmentSymbols) - { - logger?.LogDebug("Automated symbols upload for development builds has been disabled."); - return false; - } + logger?.LogDebug("Automated symbols upload has been disabled."); + return false; + } - var validated = true; - if (string.IsNullOrWhiteSpace(Auth)) - { - MissingFieldWarning(logger, "Auth Token"); - validated = false; - } + if (isDevelopmentBuild && !UploadDevelopmentSymbols) + { + logger?.LogDebug("Automated symbols upload for development builds has been disabled."); + return false; + } - if (string.IsNullOrWhiteSpace(Organization)) - { - MissingFieldWarning(logger, "Organization"); - validated = false; - } + var validated = true; + if (string.IsNullOrWhiteSpace(Auth)) + { + MissingFieldWarning(logger, "Auth Token"); + validated = false; + } - if (string.IsNullOrWhiteSpace(Project)) - { - MissingFieldWarning(logger, "Project"); - validated = false; - } + if (string.IsNullOrWhiteSpace(Organization)) + { + MissingFieldWarning(logger, "Organization"); + validated = false; + } - if (!validated) - { - logger?.LogWarning("sentry-cli validation failed. Symbols will not be uploaded." + - "\nYou can disable this warning by disabling the automated symbols upload under " + - EditorMenuPath); - } + if (string.IsNullOrWhiteSpace(Project)) + { + MissingFieldWarning(logger, "Project"); + validated = false; + } - return validated; + if (!validated) + { + logger?.LogWarning("sentry-cli validation failed. Symbols will not be uploaded." + + "\nYou can disable this warning by disabling the automated symbols upload under " + + EditorMenuPath); } + + return validated; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryMonoBehaviour.cs b/src/Sentry.Unity/SentryMonoBehaviour.cs index c94836015..2fabf2296 100644 --- a/src/Sentry.Unity/SentryMonoBehaviour.cs +++ b/src/Sentry.Unity/SentryMonoBehaviour.cs @@ -1,182 +1,179 @@ using System; -using System.Collections; -using Sentry.Extensibility; using Sentry.Unity.Integrations; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +/// +/// Singleton and DontDestroyOnLoad setup. +/// +[AddComponentMenu("")] // Hides it from being added as a component in the inspector +public partial class SentryMonoBehaviour : MonoBehaviour { - /// - /// Singleton and DontDestroyOnLoad setup. - /// - [AddComponentMenu("")] // Hides it from being added as a component in the inspector - public partial class SentryMonoBehaviour : MonoBehaviour + private static SentryMonoBehaviour? _instance; + public static SentryMonoBehaviour Instance { - private static SentryMonoBehaviour? _instance; - public static SentryMonoBehaviour Instance + get { - get + // Unity overrides `==` operator in MonoBehaviours + if (_instance == null) { - // Unity overrides `==` operator in MonoBehaviours - if (_instance == null) - { - // HideAndDontSave excludes the gameObject from the scene meaning it does not get destroyed on loading/unloading - var sentryGameObject = new GameObject("SentryMonoBehaviour") { hideFlags = HideFlags.HideAndDontSave }; - _instance = sentryGameObject.AddComponent(); - } - - return _instance; + // HideAndDontSave excludes the gameObject from the scene meaning it does not get destroyed on loading/unloading + var sentryGameObject = new GameObject("SentryMonoBehaviour") { hideFlags = HideFlags.HideAndDontSave }; + _instance = sentryGameObject.AddComponent(); } + + return _instance; } } +} - /// - /// A MonoBehaviour used to provide access to helper methods used during Performance Auto Instrumentation - /// - public partial class SentryMonoBehaviour - { - public void StartAwakeSpan(MonoBehaviour monoBehaviour) => - SentrySdk.GetSpan()?.StartChild("awake", $"{monoBehaviour.gameObject.name}.{monoBehaviour.GetType().Name}"); +/// +/// A MonoBehaviour used to provide access to helper methods used during Performance Auto Instrumentation +/// +public partial class SentryMonoBehaviour +{ + public void StartAwakeSpan(MonoBehaviour monoBehaviour) => + SentrySdk.GetSpan()?.StartChild("awake", $"{monoBehaviour.gameObject.name}.{monoBehaviour.GetType().Name}"); - public void FinishAwakeSpan() => SentrySdk.GetSpan()?.Finish(SpanStatus.Ok); - } + public void FinishAwakeSpan() => SentrySdk.GetSpan()?.Finish(SpanStatus.Ok); +} +/// +/// A MonoBehavior used to forward application focus events to subscribers. +/// +public partial class SentryMonoBehaviour +{ /// - /// A MonoBehavior used to forward application focus events to subscribers. + /// Hook to receive an event when the application gains focus. /// - public partial class SentryMonoBehaviour - { - /// - /// Hook to receive an event when the application gains focus. - /// - public event Action? ApplicationResuming; + public event Action? ApplicationResuming; - /// - /// Hook to receive an event when the application loses focus. - /// - public event Action? ApplicationPausing; + /// + /// Hook to receive an event when the application loses focus. + /// + public event Action? ApplicationPausing; - // Keeping internal track of running state because OnApplicationPause and OnApplicationFocus get called during startup and would fire false resume events - internal bool _isRunning = true; + // Keeping internal track of running state because OnApplicationPause and OnApplicationFocus get called during startup and would fire false resume events + internal bool _isRunning = true; - private IApplication? _application; - internal IApplication Application + private IApplication? _application; + internal IApplication Application + { + get { - get - { - _application ??= ApplicationAdapter.Instance; - return _application; - } - set => _application = value; + _application ??= ApplicationAdapter.Instance; + return _application; } + set => _application = value; + } - /// - /// Updates the SDK's internal pause status - /// - public void UpdatePauseStatus(bool paused) + /// + /// Updates the SDK's internal pause status + /// + public void UpdatePauseStatus(bool paused) + { + if (paused && _isRunning) { - if (paused && _isRunning) - { - _isRunning = false; - ApplicationPausing?.Invoke(); - } - else if (!paused && !_isRunning) - { - _isRunning = true; - ApplicationResuming?.Invoke(); - } + _isRunning = false; + ApplicationPausing?.Invoke(); + } + else if (!paused && !_isRunning) + { + _isRunning = true; + ApplicationResuming?.Invoke(); } - - /// - /// To receive Pause events. - /// - internal void OnApplicationPause(bool pauseStatus) => UpdatePauseStatus(pauseStatus); - - /// - /// To receive Focus events. - /// - /// - internal void OnApplicationFocus(bool hasFocus) => UpdatePauseStatus(!hasFocus); - - // The GameObject has to destroy itself since it was created with HideFlags.HideAndDontSave - private void OnApplicationQuit() => Destroy(gameObject); } /// - /// Main thread data collector. + /// To receive Pause events. /// - public partial class SentryMonoBehaviour - { - internal readonly MainThreadData MainThreadData = new(); + internal void OnApplicationPause(bool pauseStatus) => UpdatePauseStatus(pauseStatus); + + /// + /// To receive Focus events. + /// + /// + internal void OnApplicationFocus(bool hasFocus) => UpdatePauseStatus(!hasFocus); + + // The GameObject has to destroy itself since it was created with HideFlags.HideAndDontSave + private void OnApplicationQuit() => Destroy(gameObject); +} + +/// +/// Main thread data collector. +/// +public partial class SentryMonoBehaviour +{ + internal readonly MainThreadData MainThreadData = new(); - private ISentrySystemInfo? _sentrySystemInfo; - internal ISentrySystemInfo SentrySystemInfo + private ISentrySystemInfo? _sentrySystemInfo; + internal ISentrySystemInfo SentrySystemInfo + { + get { - get - { - _sentrySystemInfo ??= SentrySystemInfoAdapter.Instance; - return _sentrySystemInfo; - } - set => _sentrySystemInfo = value; + _sentrySystemInfo ??= SentrySystemInfoAdapter.Instance; + return _sentrySystemInfo; } + set => _sentrySystemInfo = value; + } - // Note: Awake is called only once and synchronously while the object is built. - // We want to do it this way instead of a StartCoroutine() so that we have the context info ASAP. - private void Awake() => CollectData(); + // Note: Awake is called only once and synchronously while the object is built. + // We want to do it this way instead of a StartCoroutine() so that we have the context info ASAP. + private void Awake() => CollectData(); - internal void CollectData() + internal void CollectData() + { + // Note: Awake() runs on the main thread. The following code just reads a couple of variables so there's no + // delay on the UI and we're safe to do it on the main thread. + MainThreadData.MainThreadId = SentrySystemInfo.MainThreadId; + MainThreadData.ProcessorCount = SentrySystemInfo.ProcessorCount; + MainThreadData.OperatingSystem = SentrySystemInfo.OperatingSystem; + MainThreadData.CpuDescription = SentrySystemInfo.CpuDescription; + MainThreadData.SupportsVibration = SentrySystemInfo.SupportsVibration; + MainThreadData.DeviceName = SentrySystemInfo.DeviceName; + MainThreadData.SystemMemorySize = SentrySystemInfo.SystemMemorySize; + MainThreadData.GraphicsDeviceId = SentrySystemInfo.GraphicsDeviceId; + MainThreadData.GraphicsDeviceName = SentrySystemInfo.GraphicsDeviceName; + MainThreadData.GraphicsDeviceVendor = SentrySystemInfo.GraphicsDeviceVendor; + MainThreadData.GraphicsMemorySize = SentrySystemInfo.GraphicsMemorySize; + MainThreadData.NpotSupport = SentrySystemInfo.NpotSupport; + MainThreadData.GraphicsDeviceVersion = SentrySystemInfo.GraphicsDeviceVersion; + MainThreadData.GraphicsDeviceType = SentrySystemInfo.GraphicsDeviceType; + MainThreadData.MaxTextureSize = SentrySystemInfo.MaxTextureSize; + MainThreadData.SupportsDrawCallInstancing = SentrySystemInfo.SupportsDrawCallInstancing; + MainThreadData.SupportsRayTracing = SentrySystemInfo.SupportsRayTracing; + MainThreadData.SupportsComputeShaders = SentrySystemInfo.SupportsComputeShaders; + MainThreadData.SupportsGeometryShaders = SentrySystemInfo.SupportsGeometryShaders; + MainThreadData.GraphicsShaderLevel = SentrySystemInfo.GraphicsShaderLevel; + MainThreadData.EditorVersion = SentrySystemInfo.EditorVersion; + MainThreadData.InstallMode = SentrySystemInfo.InstallMode; + if (MainThreadData.IsMainThread()) { - // Note: Awake() runs on the main thread. The following code just reads a couple of variables so there's no - // delay on the UI and we're safe to do it on the main thread. - MainThreadData.MainThreadId = SentrySystemInfo.MainThreadId; - MainThreadData.ProcessorCount = SentrySystemInfo.ProcessorCount; - MainThreadData.OperatingSystem = SentrySystemInfo.OperatingSystem; - MainThreadData.CpuDescription = SentrySystemInfo.CpuDescription; - MainThreadData.SupportsVibration = SentrySystemInfo.SupportsVibration; - MainThreadData.DeviceName = SentrySystemInfo.DeviceName; - MainThreadData.SystemMemorySize = SentrySystemInfo.SystemMemorySize; - MainThreadData.GraphicsDeviceId = SentrySystemInfo.GraphicsDeviceId; - MainThreadData.GraphicsDeviceName = SentrySystemInfo.GraphicsDeviceName; - MainThreadData.GraphicsDeviceVendor = SentrySystemInfo.GraphicsDeviceVendor; - MainThreadData.GraphicsMemorySize = SentrySystemInfo.GraphicsMemorySize; - MainThreadData.NpotSupport = SentrySystemInfo.NpotSupport; - MainThreadData.GraphicsDeviceVersion = SentrySystemInfo.GraphicsDeviceVersion; - MainThreadData.GraphicsDeviceType = SentrySystemInfo.GraphicsDeviceType; - MainThreadData.MaxTextureSize = SentrySystemInfo.MaxTextureSize; - MainThreadData.SupportsDrawCallInstancing = SentrySystemInfo.SupportsDrawCallInstancing; - MainThreadData.SupportsRayTracing = SentrySystemInfo.SupportsRayTracing; - MainThreadData.SupportsComputeShaders = SentrySystemInfo.SupportsComputeShaders; - MainThreadData.SupportsGeometryShaders = SentrySystemInfo.SupportsGeometryShaders; - MainThreadData.GraphicsShaderLevel = SentrySystemInfo.GraphicsShaderLevel; - MainThreadData.EditorVersion = SentrySystemInfo.EditorVersion; - MainThreadData.InstallMode = SentrySystemInfo.InstallMode; - if (MainThreadData.IsMainThread()) - { - MainThreadData.DeviceType = SentrySystemInfo.DeviceType?.Value; - MainThreadData.DeviceUniqueIdentifier = SentrySystemInfo.DeviceUniqueIdentifier?.Value; - MainThreadData.DeviceModel = SentrySystemInfo.DeviceModel?.Value; - MainThreadData.GraphicsDeviceVendorId = SentrySystemInfo.GraphicsDeviceVendorId?.Value; - MainThreadData.GraphicsMultiThreaded = SentrySystemInfo.GraphicsMultiThreaded?.Value; - MainThreadData.IsDebugBuild = SentrySystemInfo.IsDebugBuild?.Value; - MainThreadData.TargetFrameRate = SentrySystemInfo.TargetFrameRate?.Value; - MainThreadData.CopyTextureSupport = SentrySystemInfo.CopyTextureSupport?.Value; - MainThreadData.RenderingThreadingMode = SentrySystemInfo.RenderingThreadingMode?.Value; - MainThreadData.StartTime = SentrySystemInfo.StartTime?.Value; - } - else - { - // Note: while this shouldn't ever occur, we want to make sure there are some values instead of UB. - MainThreadData.DeviceType = null; - MainThreadData.DeviceUniqueIdentifier = null; - MainThreadData.DeviceModel = null; - MainThreadData.GraphicsDeviceVendorId = null; - MainThreadData.GraphicsMultiThreaded = null; - MainThreadData.IsDebugBuild = null; - MainThreadData.TargetFrameRate = null; - MainThreadData.CopyTextureSupport = null; - MainThreadData.RenderingThreadingMode = null; - MainThreadData.StartTime = null; - } + MainThreadData.DeviceType = SentrySystemInfo.DeviceType?.Value; + MainThreadData.DeviceUniqueIdentifier = SentrySystemInfo.DeviceUniqueIdentifier?.Value; + MainThreadData.DeviceModel = SentrySystemInfo.DeviceModel?.Value; + MainThreadData.GraphicsDeviceVendorId = SentrySystemInfo.GraphicsDeviceVendorId?.Value; + MainThreadData.GraphicsMultiThreaded = SentrySystemInfo.GraphicsMultiThreaded?.Value; + MainThreadData.IsDebugBuild = SentrySystemInfo.IsDebugBuild?.Value; + MainThreadData.TargetFrameRate = SentrySystemInfo.TargetFrameRate?.Value; + MainThreadData.CopyTextureSupport = SentrySystemInfo.CopyTextureSupport?.Value; + MainThreadData.RenderingThreadingMode = SentrySystemInfo.RenderingThreadingMode?.Value; + MainThreadData.StartTime = SentrySystemInfo.StartTime?.Value; + } + else + { + // Note: while this shouldn't ever occur, we want to make sure there are some values instead of UB. + MainThreadData.DeviceType = null; + MainThreadData.DeviceUniqueIdentifier = null; + MainThreadData.DeviceModel = null; + MainThreadData.GraphicsDeviceVendorId = null; + MainThreadData.GraphicsMultiThreaded = null; + MainThreadData.IsDebugBuild = null; + MainThreadData.TargetFrameRate = null; + MainThreadData.CopyTextureSupport = null; + MainThreadData.RenderingThreadingMode = null; + MainThreadData.StartTime = null; } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs b/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs index 4e981d820..f5b96da39 100644 --- a/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs +++ b/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs @@ -1,15 +1,14 @@ using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +public abstract class SentryRuntimeOptionsConfiguration : ScriptableObject { - public abstract class SentryRuntimeOptionsConfiguration : ScriptableObject - { - /// - /// Called at the player startup by SentryInitialization. - /// You can alter configuration for the C# error handling and also - /// native error handling in platforms **other** than iOS, macOS and Android. - /// - /// - public abstract void Configure(SentryUnityOptions options); - } -} + /// + /// Called at the player startup by SentryInitialization. + /// You can alter configuration for the C# error handling and also + /// native error handling in platforms **other** than iOS, macOS and Android. + /// + /// + public abstract void Configure(SentryUnityOptions options); +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryUnity.cs b/src/Sentry.Unity/SentryUnity.cs index 1947e3f3c..b8996d659 100644 --- a/src/Sentry.Unity/SentryUnity.cs +++ b/src/Sentry.Unity/SentryUnity.cs @@ -2,50 +2,49 @@ using System.ComponentModel; using Sentry.Extensibility; -namespace Sentry.Unity +namespace Sentry.Unity; + +/// +/// Sentry Unity initialization class. +/// +public static class SentryUnity { + private static SentryUnitySdk? UnitySdk; + /// - /// Sentry Unity initialization class. + /// Initializes Sentry Unity SDK while configuring the options. /// - public static class SentryUnity + /// Callback to configure the options. + public static void Init(Action sentryUnityOptionsConfigure) { - private static SentryUnitySdk? UnitySdk; + var options = new SentryUnityOptions(); + sentryUnityOptionsConfigure.Invoke(options); - /// - /// Initializes Sentry Unity SDK while configuring the options. - /// - /// Callback to configure the options. - public static void Init(Action sentryUnityOptionsConfigure) - { - var options = new SentryUnityOptions(); - sentryUnityOptionsConfigure.Invoke(options); - - Init(options); - } + Init(options); + } - /// - /// Initializes Sentry Unity SDK while providing an options object. - /// - /// The options object. - [EditorBrowsable(EditorBrowsableState.Never)] - public static void Init(SentryUnityOptions options) + /// + /// Initializes Sentry Unity SDK while providing an options object. + /// + /// The options object. + [EditorBrowsable(EditorBrowsableState.Never)] + public static void Init(SentryUnityOptions options) + { + if (UnitySdk is not null) { - if (UnitySdk is not null) - { - options.DiagnosticLogger?.LogWarning("The SDK has already been initialized."); - } - - UnitySdk = SentryUnitySdk.Init(options); + options.DiagnosticLogger?.LogWarning("The SDK has already been initialized."); } - /// - /// Closes the Sentry Unity SDK - /// - [EditorBrowsable(EditorBrowsableState.Never)] - public static void Close() - { - UnitySdk?.Close(); - UnitySdk = null; - } + UnitySdk = SentryUnitySdk.Init(options); + } + + /// + /// Closes the Sentry Unity SDK + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static void Close() + { + UnitySdk?.Close(); + UnitySdk = null; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 058cec11b..59bb4b2df 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -7,351 +7,350 @@ using UnityEngine; using CompressionLevel = System.IO.Compression.CompressionLevel; -namespace Sentry.Unity +namespace Sentry.Unity; + +/// +/// Sentry Unity Options. +/// +/// +/// Options to configure Unity while extending the Sentry .NET SDK functionality. +/// +public sealed class SentryUnityOptions : SentryOptions { /// - /// Sentry Unity Options. + /// UPM name of Sentry Unity SDK (package.json) + /// + public const string PackageName = "io.sentry.unity"; + + /// + /// Whether the SDK should automatically enable or not. /// /// - /// Options to configure Unity while extending the Sentry .NET SDK functionality. + /// At a minimum, the need to be provided. /// - public sealed class SentryUnityOptions : SentryOptions + public bool Enabled { get; set; } = true; + + /// + /// "Whether the SDK should automatically create traces during startup." + /// + public bool AutoStartupTraces { get; set; } = true; + + /// + /// "Whether the SDK should automatically create traces when loading scenes." + /// + public bool AutoSceneLoadTraces { get; set; } = true; + + /// + /// Whether Sentry events should be captured while in the Unity Editor. + /// + // Lower entry barrier, likely set to false after initial setup. + public bool CaptureInEditor { get; set; } = true; + + /// + /// Whether Sentry events should be debounced it too frequent. + /// + public bool EnableLogDebouncing { get; set; } = false; + + /// + /// Timespan between sending events of LogType.Log + /// + public TimeSpan DebounceTimeLog { get; set; } = TimeSpan.FromSeconds(1); + + /// + /// Timespan between sending events of LogType.Warning + /// + public TimeSpan DebounceTimeWarning { get; set; } = TimeSpan.FromSeconds(1); + + /// + /// Timespan between sending events of LogType.Assert, LogType.Exception and LogType.Error + /// + public TimeSpan DebounceTimeError { get; set; } = TimeSpan.FromSeconds(1); + + + private CompressionLevelWithAuto _requestBodyCompressionLevel = CompressionLevelWithAuto.Auto; + + /// + /// The level which to compress the request body sent to Sentry. + /// + public new CompressionLevelWithAuto RequestBodyCompressionLevel { - /// - /// UPM name of Sentry Unity SDK (package.json) - /// - public const string PackageName = "io.sentry.unity"; - - /// - /// Whether the SDK should automatically enable or not. - /// - /// - /// At a minimum, the need to be provided. - /// - public bool Enabled { get; set; } = true; - - /// - /// "Whether the SDK should automatically create traces during startup." - /// - public bool AutoStartupTraces { get; set; } = true; - - /// - /// "Whether the SDK should automatically create traces when loading scenes." - /// - public bool AutoSceneLoadTraces { get; set; } = true; - - /// - /// Whether Sentry events should be captured while in the Unity Editor. - /// - // Lower entry barrier, likely set to false after initial setup. - public bool CaptureInEditor { get; set; } = true; - - /// - /// Whether Sentry events should be debounced it too frequent. - /// - public bool EnableLogDebouncing { get; set; } = false; - - /// - /// Timespan between sending events of LogType.Log - /// - public TimeSpan DebounceTimeLog { get; set; } = TimeSpan.FromSeconds(1); - - /// - /// Timespan between sending events of LogType.Warning - /// - public TimeSpan DebounceTimeWarning { get; set; } = TimeSpan.FromSeconds(1); - - /// - /// Timespan between sending events of LogType.Assert, LogType.Exception and LogType.Error - /// - public TimeSpan DebounceTimeError { get; set; } = TimeSpan.FromSeconds(1); - - - private CompressionLevelWithAuto _requestBodyCompressionLevel = CompressionLevelWithAuto.Auto; - - /// - /// The level which to compress the request body sent to Sentry. - /// - public new CompressionLevelWithAuto RequestBodyCompressionLevel + get => _requestBodyCompressionLevel; + set { - get => _requestBodyCompressionLevel; - set + _requestBodyCompressionLevel = value; + if (value == CompressionLevelWithAuto.Auto) { - _requestBodyCompressionLevel = value; - if (value == CompressionLevelWithAuto.Auto) - { - // TODO: If WebGL, then NoCompression, else .. optimize (e.g: adapt to platform) - // The target platform is known when building the player, so 'auto' should resolve there(here). - // Since some platforms don't support GZipping fallback: no compression. - base.RequestBodyCompressionLevel = CompressionLevel.NoCompression; - } - else - { - // Auto would result in -1 set if not treated before providing the options to the Sentry .NET SDK - // DeflateStream would throw System.ArgumentOutOfRangeException - base.RequestBodyCompressionLevel = (CompressionLevel)value; - } + // TODO: If WebGL, then NoCompression, else .. optimize (e.g: adapt to platform) + // The target platform is known when building the player, so 'auto' should resolve there(here). + // Since some platforms don't support GZipping fallback: no compression. + base.RequestBodyCompressionLevel = CompressionLevel.NoCompression; } - } - - /// - /// Try to attach a current screen capture on error events. - /// - public bool AttachScreenshot { get; set; } = false; - - /// - /// Try to attach the current scene's hierarchy. - /// - public bool AttachViewHierarchy { get; set; } = false; - - /// - /// Maximum number of captured GameObjects in a scene root. - /// - public int MaxViewHierarchyRootObjects { get; set; } = 100; - - /// - /// Maximum number of child objects captured for each GameObject. - /// - public int MaxViewHierarchyObjectChildCount { get; set; } = 20; - - /// - /// Maximum depth of the hierarchy to capture. For example, setting 1 will only capture root GameObjects. - /// - public int MaxViewHierarchyDepth { get; set; } = 10; - - /// - /// The quality of the attached screenshot - /// - public ScreenshotQuality ScreenshotQuality { get; set; } = ScreenshotQuality.High; - - /// - /// The JPG compression quality of the attached screenshot - /// - public int ScreenshotCompression { get; set; } = 75; - - /// - /// Whether the SDK should automatically add breadcrumbs per LogType - /// - public Dictionary AddBreadcrumbsForLogType { get; set; } - - /// - /// The duration in [ms] for how long the game has to be unresponsive before an ANR event is reported. - /// - public TimeSpan AnrTimeout { get; set; } = TimeSpan.FromSeconds(5); - - /// - /// Whether the SDK should automatically filter `Bad Gateway Exceptions` caused by Unity. - /// - public bool FilterBadGatewayExceptions { get; set; } = true; - - /// - /// Whether the SDK should add native support for iOS - /// - public bool IosNativeSupportEnabled { get; set; } = true; - - /// - /// Whether the SDK should add native support for Android - /// - public bool AndroidNativeSupportEnabled { get; set; } = true; - - /// - /// Whether the SDK should add the NDK integration for Android - /// - public bool NdkIntegrationEnabled { get; set; } = true; - - /// - /// Whether the SDK should sync the scope to the NDK layer for Android - /// - public bool NdkScopeSyncEnabled { get; set; } = true; - - /// - /// Whether the SDK should add native support for Windows - /// - public bool WindowsNativeSupportEnabled { get; set; } = true; - - /// - /// Whether the SDK should add native support for MacOS - /// - public bool MacosNativeSupportEnabled { get; set; } = true; - - /// - /// Whether the SDK should add native support for Linux - /// - public bool LinuxNativeSupportEnabled { get; set; } = true; - - /// - /// Whether the SDK should add IL2CPP line number support - /// - /// - /// To give line numbers, Sentry requires the debug symbols Unity generates during build - /// For that reason, uploading debug information files must be enabled. - /// For that, Org Slut, Project Slug and Auth token are required. - /// - public bool Il2CppLineNumberSupportEnabled { get; set; } = true; - - /// - /// Enable automatic performance transaction tracking. - /// - public bool PerformanceAutoInstrumentationEnabled { get; set; } = false; - - // This option is hidden due to incompatibility between IL2CPP and Enhanced mode. - private new StackTraceMode StackTraceMode { get; set; } - - // Initialized by native SDK binding code to set the User.ID in .NET (UnityEventProcessor). - internal string? _defaultUserId; - internal string? DefaultUserId - { - get => _defaultUserId; - set + else { - _defaultUserId = value; - if (_defaultUserId is null) - { - DiagnosticLogger?.LogWarning("Couldn't set the default user ID - the value is NULL."); - } - else - { - DiagnosticLogger?.LogDebug("Setting '{0}' as the default user ID.", _defaultUserId); - } + // Auto would result in -1 set if not treated before providing the options to the Sentry .NET SDK + // DeflateStream would throw System.ArgumentOutOfRangeException + base.RequestBodyCompressionLevel = (CompressionLevel)value; } } + } - // Whether components & integrations can use multi-threading. - internal bool MultiThreading = true; + /// + /// Try to attach a current screen capture on error events. + /// + public bool AttachScreenshot { get; set; } = false; - /// - /// Used to synchronize context from .NET to the native SDK - /// - internal ContextWriter? NativeContextWriter { get; set; } = null; + /// + /// Try to attach the current scene's hierarchy. + /// + public bool AttachViewHierarchy { get; set; } = false; - /// - /// Used to close down the native SDK - /// - internal Action? NativeSupportCloseCallback { get; set; } = null; + /// + /// Maximum number of captured GameObjects in a scene root. + /// + public int MaxViewHierarchyRootObjects { get; set; } = 100; - internal List SdkIntegrationNames { get; set; } = new(); + /// + /// Maximum number of child objects captured for each GameObject. + /// + public int MaxViewHierarchyObjectChildCount { get; set; } = 20; - public SentryUnityOptions() : this(false, ApplicationAdapter.Instance) { } + /// + /// Maximum depth of the hierarchy to capture. For example, setting 1 will only capture root GameObjects. + /// + public int MaxViewHierarchyDepth { get; set; } = 10; + + /// + /// The quality of the attached screenshot + /// + public ScreenshotQuality ScreenshotQuality { get; set; } = ScreenshotQuality.High; - internal SentryUnityOptions(bool isBuilding, IApplication application) : - this(SentryMonoBehaviour.Instance, application, isBuilding) - { } + /// + /// The JPG compression quality of the attached screenshot + /// + public int ScreenshotCompression { get; set; } = 75; - internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication application, bool isBuilding) + /// + /// Whether the SDK should automatically add breadcrumbs per LogType + /// + public Dictionary AddBreadcrumbsForLogType { get; set; } + + /// + /// The duration in [ms] for how long the game has to be unresponsive before an ANR event is reported. + /// + public TimeSpan AnrTimeout { get; set; } = TimeSpan.FromSeconds(5); + + /// + /// Whether the SDK should automatically filter `Bad Gateway Exceptions` caused by Unity. + /// + public bool FilterBadGatewayExceptions { get; set; } = true; + + /// + /// Whether the SDK should add native support for iOS + /// + public bool IosNativeSupportEnabled { get; set; } = true; + + /// + /// Whether the SDK should add native support for Android + /// + public bool AndroidNativeSupportEnabled { get; set; } = true; + + /// + /// Whether the SDK should add the NDK integration for Android + /// + public bool NdkIntegrationEnabled { get; set; } = true; + + /// + /// Whether the SDK should sync the scope to the NDK layer for Android + /// + public bool NdkScopeSyncEnabled { get; set; } = true; + + /// + /// Whether the SDK should add native support for Windows + /// + public bool WindowsNativeSupportEnabled { get; set; } = true; + + /// + /// Whether the SDK should add native support for MacOS + /// + public bool MacosNativeSupportEnabled { get; set; } = true; + + /// + /// Whether the SDK should add native support for Linux + /// + public bool LinuxNativeSupportEnabled { get; set; } = true; + + /// + /// Whether the SDK should add IL2CPP line number support + /// + /// + /// To give line numbers, Sentry requires the debug symbols Unity generates during build + /// For that reason, uploading debug information files must be enabled. + /// For that, Org Slut, Project Slug and Auth token are required. + /// + public bool Il2CppLineNumberSupportEnabled { get; set; } = true; + + /// + /// Enable automatic performance transaction tracking. + /// + public bool PerformanceAutoInstrumentationEnabled { get; set; } = false; + + // This option is hidden due to incompatibility between IL2CPP and Enhanced mode. + private new StackTraceMode StackTraceMode { get; set; } + + // Initialized by native SDK binding code to set the User.ID in .NET (UnityEventProcessor). + internal string? _defaultUserId; + internal string? DefaultUserId + { + get => _defaultUserId; + set { - // IL2CPP doesn't support Process.GetCurrentProcess().StartupTime - DetectStartupTime = StartupTimeDetectionMode.Fast; - - this.AddInAppExclude("UnityEngine"); - this.AddInAppExclude("UnityEditor"); - var processor = new UnityEventProcessor(this, behaviour); - this.AddEventProcessor(processor); - this.AddTransactionProcessor(processor); - - this.AddIntegration(new UnityLogHandlerIntegration(this)); - this.AddIntegration(new AnrIntegration(behaviour)); - this.AddIntegration(new UnityScopeIntegration(behaviour, application)); - this.AddIntegration(new UnityBeforeSceneLoadIntegration()); - this.AddIntegration(new SceneManagerIntegration()); - this.AddIntegration(new SessionIntegration(behaviour)); - - this.AddExceptionFilter(new UnityBadGatewayExceptionFilter()); - this.AddExceptionFilter(new UnityWebExceptionFilter()); - this.AddExceptionFilter(new UnitySocketExceptionFilter()); - - IsGlobalModeEnabled = true; - - AutoSessionTracking = true; - RequestBodyCompressionLevel = CompressionLevelWithAuto.NoCompression; - InitCacheFlushTimeout = System.TimeSpan.Zero; - - // Ben.Demystifer not compatible with IL2CPP. We could allow Enhanced in the future for Mono. - // See https://github.com/getsentry/sentry-unity/issues/675 - base.StackTraceMode = StackTraceMode.Original; - IsEnvironmentUser = false; - - if (application.ProductName is string productName - && !string.IsNullOrWhiteSpace(productName) - && productName.Any(c => c != '.')) // productName consisting solely of '.' + _defaultUserId = value; + if (_defaultUserId is null) { - productName = Regex.Replace(productName, @"\n|\r|\t|\/|\\|\.{2}|@", "_"); - Release = $"{productName}@{application.Version}"; + DiagnosticLogger?.LogWarning("Couldn't set the default user ID - the value is NULL."); } else { - Release = application.Version; + DiagnosticLogger?.LogDebug("Setting '{0}' as the default user ID.", _defaultUserId); } + } + } - Environment = application.IsEditor && !isBuilding - ? "editor" - : "production"; + // Whether components & integrations can use multi-threading. + internal bool MultiThreading = true; - AddBreadcrumbsForLogType = new Dictionary - { - { LogType.Log, true}, - { LogType.Warning, true}, - { LogType.Assert, true}, - { LogType.Error, true}, - { LogType.Exception, true}, - }; + /// + /// Used to synchronize context from .NET to the native SDK + /// + internal ContextWriter? NativeContextWriter { get; set; } = null; + + /// + /// Used to close down the native SDK + /// + internal Action? NativeSupportCloseCallback { get; set; } = null; + + internal List SdkIntegrationNames { get; set; } = new(); + + public SentryUnityOptions() : this(false, ApplicationAdapter.Instance) { } + + internal SentryUnityOptions(bool isBuilding, IApplication application) : + this(SentryMonoBehaviour.Instance, application, isBuilding) + { } + + internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication application, bool isBuilding) + { + // IL2CPP doesn't support Process.GetCurrentProcess().StartupTime + DetectStartupTime = StartupTimeDetectionMode.Fast; + + this.AddInAppExclude("UnityEngine"); + this.AddInAppExclude("UnityEditor"); + var processor = new UnityEventProcessor(this, behaviour); + this.AddEventProcessor(processor); + this.AddTransactionProcessor(processor); + + this.AddIntegration(new UnityLogHandlerIntegration(this)); + this.AddIntegration(new AnrIntegration(behaviour)); + this.AddIntegration(new UnityScopeIntegration(behaviour, application)); + this.AddIntegration(new UnityBeforeSceneLoadIntegration()); + this.AddIntegration(new SceneManagerIntegration()); + this.AddIntegration(new SessionIntegration(behaviour)); + + this.AddExceptionFilter(new UnityBadGatewayExceptionFilter()); + this.AddExceptionFilter(new UnityWebExceptionFilter()); + this.AddExceptionFilter(new UnitySocketExceptionFilter()); + + IsGlobalModeEnabled = true; + + AutoSessionTracking = true; + RequestBodyCompressionLevel = CompressionLevelWithAuto.NoCompression; + InitCacheFlushTimeout = System.TimeSpan.Zero; + + // Ben.Demystifer not compatible with IL2CPP. We could allow Enhanced in the future for Mono. + // See https://github.com/getsentry/sentry-unity/issues/675 + base.StackTraceMode = StackTraceMode.Original; + IsEnvironmentUser = false; + + if (application.ProductName is string productName + && !string.IsNullOrWhiteSpace(productName) + && productName.Any(c => c != '.')) // productName consisting solely of '.' + { + productName = Regex.Replace(productName, @"\n|\r|\t|\/|\\|\.{2}|@", "_"); + Release = $"{productName}@{application.Version}"; + } + else + { + Release = application.Version; } - public override string ToString() + Environment = application.IsEditor && !isBuilding + ? "editor" + : "production"; + + AddBreadcrumbsForLogType = new Dictionary { - return $@"Sentry SDK Options: + { LogType.Log, true}, + { LogType.Warning, true}, + { LogType.Assert, true}, + { LogType.Error, true}, + { LogType.Exception, true}, + }; + } + + public override string ToString() + { + return $@"Sentry SDK Options: Capture In Editor: {CaptureInEditor} Release: {Release} Environment: {Environment} Offline Caching: {(CacheDirectoryPath is null ? "disabled" : "enabled")} "; - } } +} +/// +/// with an additional value for Automatic +/// +public enum CompressionLevelWithAuto +{ /// - /// with an additional value for Automatic + /// The Unity SDK will attempt to choose the best option for the target player. /// - public enum CompressionLevelWithAuto - { - /// - /// The Unity SDK will attempt to choose the best option for the target player. - /// - Auto = -1, - /// - /// The compression operation should be optimally compressed, even if the operation takes a longer time (and CPU) to complete. - /// Not supported on IL2CPP. - /// - Optimal = CompressionLevel.Optimal, - /// - /// The compression operation should complete as quickly as possible, even if the resulting data is not optimally compressed. - /// Not supported on IL2CPP. - /// - Fastest = CompressionLevel.Fastest, - /// - /// No compression should be performed. - /// - NoCompression = CompressionLevel.NoCompression, - } - + Auto = -1, /// - /// Controls for the JPEG compression quality of the attached screenshot + /// The compression operation should be optimally compressed, even if the operation takes a longer time (and CPU) to complete. + /// Not supported on IL2CPP. /// - public enum ScreenshotQuality - { - /// - /// Full quality - /// - Full, - /// - /// High quality - /// - High, - /// - /// Medium quality - /// - Medium, - /// - /// Low quality - /// - Low - } + Optimal = CompressionLevel.Optimal, + /// + /// The compression operation should complete as quickly as possible, even if the resulting data is not optimally compressed. + /// Not supported on IL2CPP. + /// + Fastest = CompressionLevel.Fastest, + /// + /// No compression should be performed. + /// + NoCompression = CompressionLevel.NoCompression, } + +/// +/// Controls for the JPEG compression quality of the attached screenshot +/// +public enum ScreenshotQuality +{ + /// + /// Full quality + /// + Full, + /// + /// High quality + /// + High, + /// + /// Medium quality + /// + Medium, + /// + /// Low quality + /// + Low +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs index e8d5b13ee..b9d6fcb97 100644 --- a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs +++ b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs @@ -1,109 +1,108 @@ using Sentry.Extensibility; using Sentry.Unity.Integrations; -namespace Sentry.Unity +namespace Sentry.Unity; + +public static class SentryUnityOptionsExtensions { - public static class SentryUnityOptionsExtensions - { - public static bool ShouldInitializeSdk(this SentryUnityOptions? options) => ShouldInitializeSdk(options, null); + public static bool ShouldInitializeSdk(this SentryUnityOptions? options) => ShouldInitializeSdk(options, null); - internal static bool ShouldInitializeSdk(this SentryUnityOptions? options, IApplication? application = null) + internal static bool ShouldInitializeSdk(this SentryUnityOptions? options, IApplication? application = null) + { + if (options is null) { - if (options is null) - { - return false; - } - - if (!IsValid(options)) - { - return false; - } - - application ??= ApplicationAdapter.Instance; - if (!options!.CaptureInEditor && application.IsEditor) - { - options.DiagnosticLogger?.LogInfo("Disabled while in the Editor."); - return false; - } + return false; + } - return true; + if (!IsValid(options)) + { + return false; } - internal static bool IsValid(this SentryUnityOptions options) + application ??= ApplicationAdapter.Instance; + if (!options!.CaptureInEditor && application.IsEditor) { - if (!options.Enabled) - { - options.DiagnosticLogger?.LogDebug("Sentry SDK has been disabled." + - "\nYou can disable this log by raising the debug verbosity level above 'Debug'."); - return false; - } + options.DiagnosticLogger?.LogInfo("Disabled while in the Editor."); + return false; + } - if (string.IsNullOrWhiteSpace(options.Dsn)) - { - options.DiagnosticLogger?.LogWarning("No Sentry DSN configured. Sentry will be disabled."); - return false; - } + return true; + } - return true; + internal static bool IsValid(this SentryUnityOptions options) + { + if (!options.Enabled) + { + options.DiagnosticLogger?.LogDebug("Sentry SDK has been disabled." + + "\nYou can disable this log by raising the debug verbosity level above 'Debug'."); + return false; } - internal static void SetupLogging(this SentryUnityOptions options) + if (string.IsNullOrWhiteSpace(options.Dsn)) { - if (options.Debug) - { - if (options.DiagnosticLogger is null) - { - options.DiagnosticLogger = new UnityLogger(options); - options.DiagnosticLogger.LogDebug("Logging enabled with 'UnityLogger' min level: {0}", options.DiagnosticLevel); - } - } - else - { - options.DiagnosticLogger = null; - } + options.DiagnosticLogger?.LogWarning("No Sentry DSN configured. Sentry will be disabled."); + return false; } - internal static void AddIl2CppExceptionProcessor(this SentryUnityOptions options, ISentryUnityInfo unityInfo) + return true; + } + + internal static void SetupLogging(this SentryUnityOptions options) + { + if (options.Debug) { - if (unityInfo.Il2CppMethods is not null) + if (options.DiagnosticLogger is null) { - options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options, unityInfo)); - } - else - { - options.DiagnosticLogger?.LogWarning("Failed to find required IL2CPP methods - Skipping line number support"); + options.DiagnosticLogger = new UnityLogger(options); + options.DiagnosticLogger.LogDebug("Logging enabled with 'UnityLogger' min level: {0}", options.DiagnosticLevel); } } + else + { + options.DiagnosticLogger = null; + } + } - /// - /// Disables the capture of errors through . - /// - /// The SentryUnityOptions to remove the integration from. - public static void DisableUnityApplicationLoggingIntegration(this SentryUnityOptions options) => - options.RemoveIntegration(); - - /// - /// Disables the application-not-responding detection. - /// - public static void DisableAnrIntegration(this SentryUnityOptions options) => - options.RemoveIntegration(); - - /// - /// Disables the automatic filtering of Bad Gateway exception of type Exception. - /// - public static void DisableBadGatewayExceptionFilter(this SentryUnityOptions options) => - options.RemoveExceptionFilter(); - - /// - /// Disables the automatic filtering of System.Net.WebException. - /// - public static void DisableWebExceptionFilter(this SentryUnityOptions options) => - options.RemoveExceptionFilter(); - - /// - /// Disables the automatic filtering of System.Net.Sockets.SocketException with error code 10049. - /// - public static void DisableSocketExceptionFilter(this SentryUnityOptions options) => - options.RemoveExceptionFilter(); + internal static void AddIl2CppExceptionProcessor(this SentryUnityOptions options, ISentryUnityInfo unityInfo) + { + if (unityInfo.Il2CppMethods is not null) + { + options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options, unityInfo)); + } + else + { + options.DiagnosticLogger?.LogWarning("Failed to find required IL2CPP methods - Skipping line number support"); + } } -} + + /// + /// Disables the capture of errors through . + /// + /// The SentryUnityOptions to remove the integration from. + public static void DisableUnityApplicationLoggingIntegration(this SentryUnityOptions options) => + options.RemoveIntegration(); + + /// + /// Disables the application-not-responding detection. + /// + public static void DisableAnrIntegration(this SentryUnityOptions options) => + options.RemoveIntegration(); + + /// + /// Disables the automatic filtering of Bad Gateway exception of type Exception. + /// + public static void DisableBadGatewayExceptionFilter(this SentryUnityOptions options) => + options.RemoveExceptionFilter(); + + /// + /// Disables the automatic filtering of System.Net.WebException. + /// + public static void DisableWebExceptionFilter(this SentryUnityOptions options) => + options.RemoveExceptionFilter(); + + /// + /// Disables the automatic filtering of System.Net.Sockets.SocketException with error code 10049. + /// + public static void DisableSocketExceptionFilter(this SentryUnityOptions options) => + options.RemoveExceptionFilter(); +} \ No newline at end of file diff --git a/src/Sentry.Unity/SentryUnitySDK.cs b/src/Sentry.Unity/SentryUnitySDK.cs index 729bac69d..35a77fd03 100644 --- a/src/Sentry.Unity/SentryUnitySDK.cs +++ b/src/Sentry.Unity/SentryUnitySDK.cs @@ -5,111 +5,110 @@ using Sentry.Unity.Integrations; using UnityEngine; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class SentryUnitySdk { - internal class SentryUnitySdk + private readonly SentryUnityOptions _options; + private IDisposable _dotnetSdk = null!; + private FileStream? _lockFile; + + private SentryUnitySdk(SentryUnityOptions options) + { + _options = options; + } + + internal static SentryUnitySdk? Init(SentryUnityOptions options) { - private readonly SentryUnityOptions _options; - private IDisposable _dotnetSdk = null!; - private FileStream? _lockFile; + var unitySdk = new SentryUnitySdk(options); - private SentryUnitySdk(SentryUnityOptions options) + options.SetupLogging(); + if (!options.ShouldInitializeSdk()) { - _options = options; + return null; } - internal static SentryUnitySdk? Init(SentryUnityOptions options) + // On Standalone, we disable cache dir in case multiple app instances run over the same path. + // Note: we cannot use a named Mutex, because Unit doesn't support it. Instead, we create a file with `FileShare.None`. + // https://forum.unity.com/threads/unsupported-internal-call-for-il2cpp-mutex-createmutex_internal-named-mutexes-are-not-supported.387334/ + if (ApplicationAdapter.Instance.Platform is RuntimePlatform.WindowsPlayer && options.CacheDirectoryPath is not null) { - var unitySdk = new SentryUnitySdk(options); - - options.SetupLogging(); - if (!options.ShouldInitializeSdk()) + try { - return null; + unitySdk._lockFile = new FileStream(Path.Combine(options.CacheDirectoryPath, "sentry-unity.lock"), FileMode.OpenOrCreate, + FileAccess.ReadWrite, FileShare.None); } - - // On Standalone, we disable cache dir in case multiple app instances run over the same path. - // Note: we cannot use a named Mutex, because Unit doesn't support it. Instead, we create a file with `FileShare.None`. - // https://forum.unity.com/threads/unsupported-internal-call-for-il2cpp-mutex-createmutex_internal-named-mutexes-are-not-supported.387334/ - if (ApplicationAdapter.Instance.Platform is RuntimePlatform.WindowsPlayer && options.CacheDirectoryPath is not null) + catch (Exception ex) { - try - { - unitySdk._lockFile = new FileStream(Path.Combine(options.CacheDirectoryPath, "sentry-unity.lock"), FileMode.OpenOrCreate, - FileAccess.ReadWrite, FileShare.None); - } - catch (Exception ex) - { - options.DiagnosticLogger?.LogWarning("An exception was thrown while trying to " + - "acquire a lockfile on the config directory: .NET event cache will be disabled.", ex); - options.CacheDirectoryPath = null; - options.AutoSessionTracking = false; - } + options.DiagnosticLogger?.LogWarning("An exception was thrown while trying to " + + "acquire a lockfile on the config directory: .NET event cache will be disabled.", ex); + options.CacheDirectoryPath = null; + options.AutoSessionTracking = false; } + } - unitySdk._dotnetSdk = SentrySdk.Init(options); + unitySdk._dotnetSdk = SentrySdk.Init(options); - if (options.AttachScreenshot) - { - SentrySdk.ConfigureScope(s => - s.AddAttachment(new ScreenshotAttachment( - new ScreenshotAttachmentContent(options, SentryMonoBehaviour.Instance)))); - } + if (options.AttachScreenshot) + { + SentrySdk.ConfigureScope(s => + s.AddAttachment(new ScreenshotAttachment( + new ScreenshotAttachmentContent(options, SentryMonoBehaviour.Instance)))); + } - if (options.AttachViewHierarchy) - { - SentrySdk.ConfigureScope(s => - s.AddAttachment(new ViewHierarchyAttachment( - new UnityViewHierarchyAttachmentContent(options, SentryMonoBehaviour.Instance)))); - } + if (options.AttachViewHierarchy) + { + SentrySdk.ConfigureScope(s => + s.AddAttachment(new ViewHierarchyAttachment( + new UnityViewHierarchyAttachmentContent(options, SentryMonoBehaviour.Instance)))); + } - if (options.NativeContextWriter is { } contextWriter) + if (options.NativeContextWriter is { } contextWriter) + { + SentrySdk.ConfigureScope((scope) => { - SentrySdk.ConfigureScope((scope) => + var task = Task.Run(() => contextWriter.Write(scope)).ContinueWith(t => { - var task = Task.Run(() => contextWriter.Write(scope)).ContinueWith(t => + if (t.Exception is not null) { - if (t.Exception is not null) - { - options.DiagnosticLogger?.LogWarning( - "Failed to synchronize scope to the native SDK: {0}", t.Exception); - } - }); + options.DiagnosticLogger?.LogWarning( + "Failed to synchronize scope to the native SDK: {0}", t.Exception); + } }); - } + }); + } - ApplicationAdapter.Instance.Quitting += unitySdk.Close; + ApplicationAdapter.Instance.Quitting += unitySdk.Close; - return unitySdk; - } + return unitySdk; + } - public void Close() + public void Close() + { + _options.DiagnosticLogger?.LogDebug("Closing the sentry-dotnet SDK"); + try { - _options.DiagnosticLogger?.LogDebug("Closing the sentry-dotnet SDK"); - try - { - ApplicationAdapter.Instance.Quitting -= Close; - _options.NativeSupportCloseCallback?.Invoke(); - _options.NativeSupportCloseCallback = null; + ApplicationAdapter.Instance.Quitting -= Close; + _options.NativeSupportCloseCallback?.Invoke(); + _options.NativeSupportCloseCallback = null; - _dotnetSdk.Dispose(); - } - catch (Exception ex) - { - _options.DiagnosticLogger?.Log(SentryLevel.Warning, - "Exception while closing the .NET SDK.", ex); - } + _dotnetSdk.Dispose(); + } + catch (Exception ex) + { + _options.DiagnosticLogger?.Log(SentryLevel.Warning, + "Exception while closing the .NET SDK.", ex); + } - try - { - // We don't really need to close, Windows would release the lock anyway, but let's be nice. - _lockFile?.Close(); - } - catch (Exception ex) - { - _options.DiagnosticLogger?.Log(SentryLevel.Warning, - "Exception while releasing the lockfile on the config directory.", ex); - } + try + { + // We don't really need to close, Windows would release the lock anyway, but let's be nice. + _lockFile?.Close(); + } + catch (Exception ex) + { + _options.DiagnosticLogger?.Log(SentryLevel.Warning, + "Exception while releasing the lockfile on the config directory.", ex); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/SystemInfoAdapter.cs b/src/Sentry.Unity/SystemInfoAdapter.cs index f7aa61c8a..cbc5fb2c9 100644 --- a/src/Sentry.Unity/SystemInfoAdapter.cs +++ b/src/Sentry.Unity/SystemInfoAdapter.cs @@ -1,117 +1,115 @@ using System; using System.Threading; using UnityEngine; -using UnityEngine.UIElements; -namespace Sentry.Unity +namespace Sentry.Unity; + +// Warning: The `Lazy<>` properties may only be evaluated on the main (UI) thread. +internal interface ISentrySystemInfo +{ + int? MainThreadId { get; } + string? OperatingSystem { get; } + int? ProcessorCount { get; } + bool? SupportsVibration { get; } + Lazy? DeviceType { get; } + string? CpuDescription { get; } + string? DeviceName { get; } + Lazy? DeviceUniqueIdentifier { get; } + Lazy? DeviceModel { get; } + int? SystemMemorySize { get; } + int? GraphicsDeviceId { get; } + string? GraphicsDeviceName { get; } + Lazy? GraphicsDeviceVendorId { get; } + string? GraphicsDeviceVendor { get; } + int? GraphicsMemorySize { get; } + Lazy? GraphicsMultiThreaded { get; } + string? NpotSupport { get; } + string? GraphicsDeviceVersion { get; } + string? GraphicsDeviceType { get; } + int? MaxTextureSize { get; } + bool? SupportsDrawCallInstancing { get; } + bool? SupportsRayTracing { get; } + bool? SupportsComputeShaders { get; } + bool? SupportsGeometryShaders { get; } + int? GraphicsShaderLevel { get; } + bool? GraphicsUVStartsAtTop { get; } + Lazy? IsDebugBuild { get; } + string? EditorVersion { get; } + string? InstallMode { get; } + Lazy? TargetFrameRate { get; } + Lazy? CopyTextureSupport { get; } + Lazy? RenderingThreadingMode { get; } + Lazy? StartTime { get; } +} + +internal sealed class SentrySystemInfoAdapter : ISentrySystemInfo { - // Warning: The `Lazy<>` properties may only be evaluated on the main (UI) thread. - internal interface ISentrySystemInfo + public static readonly SentrySystemInfoAdapter Instance = new(); + + private SentrySystemInfoAdapter() { - int? MainThreadId { get; } - string? OperatingSystem { get; } - int? ProcessorCount { get; } - bool? SupportsVibration { get; } - Lazy? DeviceType { get; } - string? CpuDescription { get; } - string? DeviceName { get; } - Lazy? DeviceUniqueIdentifier { get; } - Lazy? DeviceModel { get; } - int? SystemMemorySize { get; } - int? GraphicsDeviceId { get; } - string? GraphicsDeviceName { get; } - Lazy? GraphicsDeviceVendorId { get; } - string? GraphicsDeviceVendor { get; } - int? GraphicsMemorySize { get; } - Lazy? GraphicsMultiThreaded { get; } - string? NpotSupport { get; } - string? GraphicsDeviceVersion { get; } - string? GraphicsDeviceType { get; } - int? MaxTextureSize { get; } - bool? SupportsDrawCallInstancing { get; } - bool? SupportsRayTracing { get; } - bool? SupportsComputeShaders { get; } - bool? SupportsGeometryShaders { get; } - int? GraphicsShaderLevel { get; } - bool? GraphicsUVStartsAtTop { get; } - Lazy? IsDebugBuild { get; } - string? EditorVersion { get; } - string? InstallMode { get; } - Lazy? TargetFrameRate { get; } - Lazy? CopyTextureSupport { get; } - Lazy? RenderingThreadingMode { get; } - Lazy? StartTime { get; } } - internal sealed class SentrySystemInfoAdapter : ISentrySystemInfo + public int? MainThreadId => Thread.CurrentThread.ManagedThreadId; + public string? OperatingSystem => SystemInfo.operatingSystem; + public int? ProcessorCount => SystemInfo.processorCount; + public bool? SupportsVibration => SystemInfo.supportsVibration; + public Lazy? DeviceType => new(() => SystemInfo.deviceType.ToString()); + public string? CpuDescription => SystemInfo.processorType; + public string? DeviceName { - public static readonly SentrySystemInfoAdapter Instance = new(); - - private SentrySystemInfoAdapter() - { - } - - public int? MainThreadId => Thread.CurrentThread.ManagedThreadId; - public string? OperatingSystem => SystemInfo.operatingSystem; - public int? ProcessorCount => SystemInfo.processorCount; - public bool? SupportsVibration => SystemInfo.supportsVibration; - public Lazy? DeviceType => new(() => SystemInfo.deviceType.ToString()); - public string? CpuDescription => SystemInfo.processorType; - public string? DeviceName + get { - get + // Workaround for https://github.com/getsentry/sentry-unity/issues/1322 - + if (Application.platform == RuntimePlatform.Android) { - // Workaround for https://github.com/getsentry/sentry-unity/issues/1322 - - if (Application.platform == RuntimePlatform.Android) + using var version = new AndroidJavaClass("android.os.Build$VERSION"); + var sdkVersion = version.GetStatic("SDK_INT"); + if (sdkVersion >= 32) { - using var version = new AndroidJavaClass("android.os.Build$VERSION"); - var sdkVersion = version.GetStatic("SDK_INT"); - if (sdkVersion >= 32) + try + { + return SystemInfo.deviceName; + } + catch { - try - { - return SystemInfo.deviceName; - } - catch - { - // Unity's built-in fallback - return ""; - } + // Unity's built-in fallback + return ""; } } - - return SystemInfo.deviceName; } - } - public Lazy DeviceUniqueIdentifier => new(() => SystemInfo.deviceUniqueIdentifier); - public Lazy DeviceModel => new(() => SystemInfo.deviceModel); - /// - /// System memory size in megabytes. - /// - public int? SystemMemorySize => SystemInfo.systemMemorySize; - public int? GraphicsDeviceId => SystemInfo.graphicsDeviceID; - public string? GraphicsDeviceName => SystemInfo.graphicsDeviceName; - public Lazy? GraphicsDeviceVendorId => new(() => SystemInfo.graphicsDeviceVendorID.ToString()); - public string? GraphicsDeviceVendor => SystemInfo.graphicsDeviceVendor; - public int? GraphicsMemorySize => SystemInfo.graphicsMemorySize; - public Lazy? GraphicsMultiThreaded => new(() => SystemInfo.graphicsMultiThreaded); - public string? NpotSupport => SystemInfo.npotSupport.ToString(); - public string? GraphicsDeviceVersion => SystemInfo.graphicsDeviceVersion; - public string? GraphicsDeviceType => SystemInfo.graphicsDeviceType.ToString(); - public int? MaxTextureSize => SystemInfo.maxTextureSize; - public bool? SupportsDrawCallInstancing => SystemInfo.supportsInstancing; - public bool? SupportsRayTracing => SystemInfo.supportsRayTracing; - public bool? SupportsComputeShaders => SystemInfo.supportsComputeShaders; - public bool? SupportsGeometryShaders => SystemInfo.supportsGeometryShaders; - public int? GraphicsShaderLevel => SystemInfo.graphicsShaderLevel; - public bool? GraphicsUVStartsAtTop => SystemInfo.graphicsUVStartsAtTop; - public Lazy IsDebugBuild => new(() => Debug.isDebugBuild); - public string? EditorVersion => Application.unityVersion; - public string? InstallMode => Application.installMode.ToString(); - public Lazy TargetFrameRate => new(() => Application.targetFrameRate.ToString()); - public Lazy CopyTextureSupport => new(() => SystemInfo.copyTextureSupport.ToString()); - public Lazy RenderingThreadingMode => new(() => SystemInfo.renderingThreadingMode.ToString()); - public Lazy? StartTime => new(() => DateTimeOffset.UtcNow.AddSeconds(-Time.realtimeSinceStartup)); + return SystemInfo.deviceName; + } } -} + + public Lazy DeviceUniqueIdentifier => new(() => SystemInfo.deviceUniqueIdentifier); + public Lazy DeviceModel => new(() => SystemInfo.deviceModel); + /// + /// System memory size in megabytes. + /// + public int? SystemMemorySize => SystemInfo.systemMemorySize; + public int? GraphicsDeviceId => SystemInfo.graphicsDeviceID; + public string? GraphicsDeviceName => SystemInfo.graphicsDeviceName; + public Lazy? GraphicsDeviceVendorId => new(() => SystemInfo.graphicsDeviceVendorID.ToString()); + public string? GraphicsDeviceVendor => SystemInfo.graphicsDeviceVendor; + public int? GraphicsMemorySize => SystemInfo.graphicsMemorySize; + public Lazy? GraphicsMultiThreaded => new(() => SystemInfo.graphicsMultiThreaded); + public string? NpotSupport => SystemInfo.npotSupport.ToString(); + public string? GraphicsDeviceVersion => SystemInfo.graphicsDeviceVersion; + public string? GraphicsDeviceType => SystemInfo.graphicsDeviceType.ToString(); + public int? MaxTextureSize => SystemInfo.maxTextureSize; + public bool? SupportsDrawCallInstancing => SystemInfo.supportsInstancing; + public bool? SupportsRayTracing => SystemInfo.supportsRayTracing; + public bool? SupportsComputeShaders => SystemInfo.supportsComputeShaders; + public bool? SupportsGeometryShaders => SystemInfo.supportsGeometryShaders; + public int? GraphicsShaderLevel => SystemInfo.graphicsShaderLevel; + public bool? GraphicsUVStartsAtTop => SystemInfo.graphicsUVStartsAtTop; + public Lazy IsDebugBuild => new(() => Debug.isDebugBuild); + public string? EditorVersion => Application.unityVersion; + public string? InstallMode => Application.installMode.ToString(); + public Lazy TargetFrameRate => new(() => Application.targetFrameRate.ToString()); + public Lazy CopyTextureSupport => new(() => SystemInfo.copyTextureSupport.ToString()); + public Lazy RenderingThreadingMode => new(() => SystemInfo.renderingThreadingMode.ToString()); + public Lazy? StartTime => new(() => DateTimeOffset.UtcNow.AddSeconds(-Time.realtimeSinceStartup)); +} \ No newline at end of file diff --git a/src/Sentry.Unity/TimeDebounceBase.cs b/src/Sentry.Unity/TimeDebounceBase.cs index a9668753c..6e0756c13 100644 --- a/src/Sentry.Unity/TimeDebounceBase.cs +++ b/src/Sentry.Unity/TimeDebounceBase.cs @@ -1,56 +1,55 @@ using System; -namespace Sentry.Unity +namespace Sentry.Unity; + +public interface IUnityLogMessageDebounce { - public interface IUnityLogMessageDebounce - { - bool Debounced(); - } + bool Debounced(); +} - /// - /// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback - /// - internal class TimeDebounceBase : IUnityLogMessageDebounce - { - private static DateTimeOffset Now => DateTimeOffset.UtcNow; +/// +/// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback +/// +internal class TimeDebounceBase : IUnityLogMessageDebounce +{ + private static DateTimeOffset Now => DateTimeOffset.UtcNow; - protected TimeSpan DebounceOffset; + protected TimeSpan DebounceOffset; - private DateTimeOffset? _barrierOffset; + private DateTimeOffset? _barrierOffset; - public bool Debounced() + public bool Debounced() + { + if (_barrierOffset != null && Now < _barrierOffset) { - if (_barrierOffset != null && Now < _barrierOffset) - { - return false; - } - - _barrierOffset = Now.Add(DebounceOffset); - return true; + return false; } - } - /// - /// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback - /// - internal sealed class LogTimeDebounce : TimeDebounceBase - { - public LogTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; + _barrierOffset = Now.Add(DebounceOffset); + return true; } +} - /// - /// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback - /// - internal sealed class ErrorTimeDebounce : TimeDebounceBase - { - public ErrorTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; - } +/// +/// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback +/// +internal sealed class LogTimeDebounce : TimeDebounceBase +{ + public LogTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; +} - /// - /// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback - /// - internal sealed class WarningTimeDebounce : TimeDebounceBase - { - public WarningTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; - } +/// +/// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback +/// +internal sealed class ErrorTimeDebounce : TimeDebounceBase +{ + public ErrorTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; } + +/// +/// This class is not thread-safe and is designed to be called by Unity non-threaded logger callback +/// +internal sealed class WarningTimeDebounce : TimeDebounceBase +{ + public WarningTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; +} \ No newline at end of file diff --git a/src/Sentry.Unity/UnityEventProcessor.cs b/src/Sentry.Unity/UnityEventProcessor.cs index b86144b0c..d4fc83f46 100644 --- a/src/Sentry.Unity/UnityEventProcessor.cs +++ b/src/Sentry.Unity/UnityEventProcessor.cs @@ -4,96 +4,95 @@ using UnityEngine; using DeviceOrientation = Sentry.Protocol.DeviceOrientation; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class UnityEventProcessor : + ISentryEventProcessor, + ISentryTransactionProcessor { - internal class UnityEventProcessor : - ISentryEventProcessor, - ISentryTransactionProcessor + private readonly SentryUnityOptions _sentryOptions; + private readonly MainThreadData _mainThreadData; + + public UnityEventProcessor(SentryUnityOptions sentryOptions, SentryMonoBehaviour sentryMonoBehaviour) { - private readonly SentryUnityOptions _sentryOptions; - private readonly MainThreadData _mainThreadData; + _sentryOptions = sentryOptions; + _mainThreadData = sentryMonoBehaviour.MainThreadData; + } - public UnityEventProcessor(SentryUnityOptions sentryOptions, SentryMonoBehaviour sentryMonoBehaviour) - { - _sentryOptions = sentryOptions; - _mainThreadData = sentryMonoBehaviour.MainThreadData; - } + public SentryTransaction? Process(SentryTransaction transaction) + { + SetEventContext(transaction); + return transaction; + } - public SentryTransaction? Process(SentryTransaction transaction) - { - SetEventContext(transaction); - return transaction; - } + public SentryEvent Process(SentryEvent @event) + { + SetEventContext(@event); - public SentryEvent Process(SentryEvent @event) - { - SetEventContext(@event); + @event.ServerName = null; - @event.ServerName = null; + return @event; + } - return @event; + private void SetEventContext(IEventLike sentryEvent) + { + try + { + PopulateDevice(sentryEvent.Contexts.Device); + // Populating the SDK Integrations here (for now) instead of UnityScopeIntegration because it cannot be guaranteed + // that it got added last or that there was not an integration added at a later point + PopulateSdkIntegrations(sentryEvent.Sdk); + // TODO revisit which tags we should be adding by default + sentryEvent.SetTag("unity.is_main_thread", _mainThreadData.IsMainThread().ToTagValue()); } - - private void SetEventContext(IEventLike sentryEvent) + catch (Exception exception) { - try - { - PopulateDevice(sentryEvent.Contexts.Device); - // Populating the SDK Integrations here (for now) instead of UnityScopeIntegration because it cannot be guaranteed - // that it got added last or that there was not an integration added at a later point - PopulateSdkIntegrations(sentryEvent.Sdk); - // TODO revisit which tags we should be adding by default - sentryEvent.SetTag("unity.is_main_thread", _mainThreadData.IsMainThread().ToTagValue()); - } - catch (Exception exception) - { - _sentryOptions.DiagnosticLogger?.LogError(exception: exception, "{0} processing failed.", nameof(SentryEvent)); - } + _sentryOptions.DiagnosticLogger?.LogError(exception: exception, "{0} processing failed.", nameof(SentryEvent)); } + } - private void PopulateDevice(Device device) + private void PopulateDevice(Device device) + { + if (!_mainThreadData.IsMainThread()) { - if (!_mainThreadData.IsMainThread()) - { - return; - } - - device.BatteryStatus = SystemInfo.batteryStatus.ToString(); + return; + } - var batteryLevel = SystemInfo.batteryLevel; - if (batteryLevel > 0.0) - { - device.BatteryLevel = (short?)(batteryLevel * 100); - } + device.BatteryStatus = SystemInfo.batteryStatus.ToString(); - switch (Input.deviceOrientation) - { - case UnityEngine.DeviceOrientation.Portrait: - case UnityEngine.DeviceOrientation.PortraitUpsideDown: - device.Orientation = DeviceOrientation.Portrait; - break; - case UnityEngine.DeviceOrientation.LandscapeLeft: - case UnityEngine.DeviceOrientation.LandscapeRight: - device.Orientation = DeviceOrientation.Landscape; - break; - case UnityEngine.DeviceOrientation.FaceUp: - case UnityEngine.DeviceOrientation.FaceDown: - // TODO: Add to protocol? - break; - } + var batteryLevel = SystemInfo.batteryLevel; + if (batteryLevel > 0.0) + { + device.BatteryLevel = (short?)(batteryLevel * 100); } - private void PopulateSdkIntegrations(SdkVersion sdkVersion) + switch (Input.deviceOrientation) { - foreach (var integrationName in _sentryOptions.SdkIntegrationNames) - { - sdkVersion.AddIntegration(integrationName); - } + case UnityEngine.DeviceOrientation.Portrait: + case UnityEngine.DeviceOrientation.PortraitUpsideDown: + device.Orientation = DeviceOrientation.Portrait; + break; + case UnityEngine.DeviceOrientation.LandscapeLeft: + case UnityEngine.DeviceOrientation.LandscapeRight: + device.Orientation = DeviceOrientation.Landscape; + break; + case UnityEngine.DeviceOrientation.FaceUp: + case UnityEngine.DeviceOrientation.FaceDown: + // TODO: Add to protocol? + break; } } - internal static class TagValueNormalizer + private void PopulateSdkIntegrations(SdkVersion sdkVersion) { - internal static string ToTagValue(this bool value) => value ? "true" : "false"; + foreach (var integrationName in _sentryOptions.SdkIntegrationNames) + { + sdkVersion.AddIntegration(integrationName); + } } } + +internal static class TagValueNormalizer +{ + internal static string ToTagValue(this bool value) => value ? "true" : "false"; +} \ No newline at end of file diff --git a/src/Sentry.Unity/UnityLogger.cs b/src/Sentry.Unity/UnityLogger.cs index edffb5a92..7c2936cbe 100644 --- a/src/Sentry.Unity/UnityLogger.cs +++ b/src/Sentry.Unity/UnityLogger.cs @@ -3,47 +3,46 @@ using UnityEngine; using static System.String; -namespace Sentry.Unity +namespace Sentry.Unity; + +public class UnityLogger : IDiagnosticLogger { - public class UnityLogger : IDiagnosticLogger - { - public const string LogTag = "Sentry"; + public const string LogTag = "Sentry"; - private readonly SentryOptions _sentryOptions; - private readonly ILogger _logger; + private readonly SentryOptions _sentryOptions; + private readonly ILogger _logger; - public bool IsEnabled(SentryLevel level) => level >= _sentryOptions.DiagnosticLevel; + public bool IsEnabled(SentryLevel level) => level >= _sentryOptions.DiagnosticLevel; - public UnityLogger(SentryUnityOptions sentryUnityOptions) : this(sentryUnityOptions, null) - { } + public UnityLogger(SentryUnityOptions sentryUnityOptions) : this(sentryUnityOptions, null) + { } - internal UnityLogger(SentryOptions sentryOptions, ILogger? logger = null) - { - _sentryOptions = sentryOptions; - _logger = logger ?? Debug.unityLogger; - } + internal UnityLogger(SentryOptions sentryOptions, ILogger? logger = null) + { + _sentryOptions = sentryOptions; + _logger = logger ?? Debug.unityLogger; + } - public void Log(SentryLevel logLevel, string? message, Exception? exception = null, params object?[] args) + public void Log(SentryLevel logLevel, string? message, Exception? exception = null, params object?[] args) + { + if (!IsEnabled(logLevel)) { - if (!IsEnabled(logLevel)) - { - return; - } - - _logger.Log(GetUnityLogType(logLevel), LogTag, $"({logLevel.ToString()}) {Format(message, args)} {exception}"); + return; } - internal static LogType GetUnityLogType(SentryLevel logLevel) - { - return logLevel switch - { - SentryLevel.Debug or SentryLevel.Info => LogType.Log, - SentryLevel.Warning => LogType.Warning, - SentryLevel.Error or SentryLevel.Fatal => LogType.Error, - _ => LogType.Log - }; - } + _logger.Log(GetUnityLogType(logLevel), LogTag, $"({logLevel.ToString()}) {Format(message, args)} {exception}"); + } - public override string ToString() => nameof(UnityLogger); + internal static LogType GetUnityLogType(SentryLevel logLevel) + { + return logLevel switch + { + SentryLevel.Debug or SentryLevel.Info => LogType.Log, + SentryLevel.Warning => LogType.Warning, + SentryLevel.Error or SentryLevel.Fatal => LogType.Error, + _ => LogType.Log + }; } -} + + public override string ToString() => nameof(UnityLogger); +} \ No newline at end of file diff --git a/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs b/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs index e1cf3b359..3024df30f 100644 --- a/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs +++ b/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs @@ -7,96 +7,95 @@ using UnityEngine; using UnityEngine.SceneManagement; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class UnityViewHierarchyAttachmentContent : IAttachmentContent { - internal class UnityViewHierarchyAttachmentContent : IAttachmentContent + private readonly SentryMonoBehaviour _behaviour; + private readonly SentryUnityOptions _options; + + public UnityViewHierarchyAttachmentContent(SentryUnityOptions options, SentryMonoBehaviour behaviour) { - private readonly SentryMonoBehaviour _behaviour; - private readonly SentryUnityOptions _options; + _behaviour = behaviour; + _options = options; + } - public UnityViewHierarchyAttachmentContent(SentryUnityOptions options, SentryMonoBehaviour behaviour) + public Stream GetStream() + { + // Note: we need to check explicitly that we're on the same thread. While Unity would throw otherwise + // when capturing the screenshot, it would only do so on development builds. On release, it just crashes... + if (!_behaviour.MainThreadData.IsMainThread()) { - _behaviour = behaviour; - _options = options; + _options.DiagnosticLogger?.LogDebug("Can't capture screenshots on other than main (UI) thread."); + return Stream.Null; } - public Stream GetStream() - { - // Note: we need to check explicitly that we're on the same thread. While Unity would throw otherwise - // when capturing the screenshot, it would only do so on development builds. On release, it just crashes... - if (!_behaviour.MainThreadData.IsMainThread()) - { - _options.DiagnosticLogger?.LogDebug("Can't capture screenshots on other than main (UI) thread."); - return Stream.Null; - } - - return CaptureViewHierarchy(); - } + return CaptureViewHierarchy(); + } - internal Stream CaptureViewHierarchy() - { - var stream = new MemoryStream(); - using var writer = new Utf8JsonWriter(stream); + internal Stream CaptureViewHierarchy() + { + var stream = new MemoryStream(); + using var writer = new Utf8JsonWriter(stream); - var viewHierarchy = CreateViewHierarchy( - _options.MaxViewHierarchyRootObjects, - _options.MaxViewHierarchyObjectChildCount, - _options.MaxViewHierarchyDepth); - viewHierarchy.WriteTo(writer, _options.DiagnosticLogger); + var viewHierarchy = CreateViewHierarchy( + _options.MaxViewHierarchyRootObjects, + _options.MaxViewHierarchyObjectChildCount, + _options.MaxViewHierarchyDepth); + viewHierarchy.WriteTo(writer, _options.DiagnosticLogger); - writer.Flush(); - stream.Seek(0, SeekOrigin.Begin); + writer.Flush(); + stream.Seek(0, SeekOrigin.Begin); - return stream; + return stream; + } + + internal ViewHierarchy CreateViewHierarchy(int maxRootGameObjectCount, int maxChildCount, int maxDepth) + { + var rootGameObjects = new List(); + var scene = SceneManager.GetActiveScene(); + scene.GetRootGameObjects(rootGameObjects); + + // Consider the root a 'scene'. + var root = new UnityViewHierarchyNode(scene.name); + var viewHierarchy = new ViewHierarchy("Unity"); + viewHierarchy.Windows.Add(root); + + var rootElementCount = Math.Min(rootGameObjects.Count, maxRootGameObjectCount); + for (var i = 0; i < rootElementCount; i++) + { + CreateNode(maxDepth, maxChildCount, root, rootGameObjects[i].transform); } - internal ViewHierarchy CreateViewHierarchy(int maxRootGameObjectCount, int maxChildCount, int maxDepth) + return viewHierarchy; + } + + internal void CreateNode(int remainingDepth, int maxChildCount, ViewHierarchyNode parentNode, Transform transform) + { + var components = new List(); + transform.GetComponents(components); + var node = new UnityViewHierarchyNode(transform.name) + { + Tag = transform.tag, + Position = transform.position.ToString(), + Rotation = transform.rotation.eulerAngles.ToString(), + Scale = transform.localScale.ToString(), + Active = transform.gameObject.activeSelf, + Extras = components.Select(e => e.GetType().ToString()).ToList() + }; + + parentNode.Children.Add(node); + + remainingDepth--; + if (remainingDepth <= 0) { - var rootGameObjects = new List(); - var scene = SceneManager.GetActiveScene(); - scene.GetRootGameObjects(rootGameObjects); - - // Consider the root a 'scene'. - var root = new UnityViewHierarchyNode(scene.name); - var viewHierarchy = new ViewHierarchy("Unity"); - viewHierarchy.Windows.Add(root); - - var rootElementCount = Math.Min(rootGameObjects.Count, maxRootGameObjectCount); - for (var i = 0; i < rootElementCount; i++) - { - CreateNode(maxDepth, maxChildCount, root, rootGameObjects[i].transform); - } - - return viewHierarchy; + return; } - internal void CreateNode(int remainingDepth, int maxChildCount, ViewHierarchyNode parentNode, Transform transform) + var childCount = Math.Min(transform.childCount, maxChildCount); + for (var i = 0; i < childCount; i++) { - var components = new List(); - transform.GetComponents(components); - var node = new UnityViewHierarchyNode(transform.name) - { - Tag = transform.tag, - Position = transform.position.ToString(), - Rotation = transform.rotation.eulerAngles.ToString(), - Scale = transform.localScale.ToString(), - Active = transform.gameObject.activeSelf, - Extras = components.Select(e => e.GetType().ToString()).ToList() - }; - - parentNode.Children.Add(node); - - remainingDepth--; - if (remainingDepth <= 0) - { - return; - } - - var childCount = Math.Min(transform.childCount, maxChildCount); - for (var i = 0; i < childCount; i++) - { - CreateNode(remainingDepth, maxChildCount, node, transform.GetChild(i)); - } + CreateNode(remainingDepth, maxChildCount, node, transform.GetChild(i)); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/UnityViewHierarchyNode.cs b/src/Sentry.Unity/UnityViewHierarchyNode.cs index 85a3bf298..1c14bd097 100644 --- a/src/Sentry.Unity/UnityViewHierarchyNode.cs +++ b/src/Sentry.Unity/UnityViewHierarchyNode.cs @@ -2,54 +2,53 @@ using System.Text.Json; using Sentry.Extensibility; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class UnityViewHierarchyNode : ViewHierarchyNode { - internal class UnityViewHierarchyNode : ViewHierarchyNode - { - public string? Tag { get; set; } - public string? Position { get; set; } - public string? Rotation { get; set; } - public string? Scale { get; set; } - public bool? Active { get; set; } + public string? Tag { get; set; } + public string? Position { get; set; } + public string? Rotation { get; set; } + public string? Scale { get; set; } + public bool? Active { get; set; } - public List? Extras { get; set; } + public List? Extras { get; set; } - public UnityViewHierarchyNode(string name) : base(name) { } + public UnityViewHierarchyNode(string name) : base(name) { } - protected override void WriteAdditionalProperties(Utf8JsonWriter writer, IDiagnosticLogger? logger) + protected override void WriteAdditionalProperties(Utf8JsonWriter writer, IDiagnosticLogger? logger) + { + if (!string.IsNullOrWhiteSpace(Tag)) { - if (!string.IsNullOrWhiteSpace(Tag)) - { - writer.WriteString("tag", Tag); - } + writer.WriteString("tag", Tag); + } - if (!string.IsNullOrWhiteSpace(Position)) - { - writer.WriteString("position", Position); - } - if (!string.IsNullOrWhiteSpace(Rotation)) - { - writer.WriteString("rotation", Rotation); - } - if (!string.IsNullOrWhiteSpace(Scale)) - { - writer.WriteString("scale", Scale); - } + if (!string.IsNullOrWhiteSpace(Position)) + { + writer.WriteString("position", Position); + } + if (!string.IsNullOrWhiteSpace(Rotation)) + { + writer.WriteString("rotation", Rotation); + } + if (!string.IsNullOrWhiteSpace(Scale)) + { + writer.WriteString("scale", Scale); + } - if (Active is { } active) - { - writer.WriteString("active", active.ToString()); - } + if (Active is { } active) + { + writer.WriteString("active", active.ToString()); + } - if (Extras is { } extras) + if (Extras is { } extras) + { + writer.WriteStartArray("extras"); + foreach (var extra in extras) { - writer.WriteStartArray("extras"); - foreach (var extra in extras) - { - writer.WriteStringValue(extra); - } - writer.WriteEndArray(); + writer.WriteStringValue(extra); } + writer.WriteEndArray(); } } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/UnityWebRequestTransport.cs b/src/Sentry.Unity/UnityWebRequestTransport.cs index f4bf565cd..816c27556 100644 --- a/src/Sentry.Unity/UnityWebRequestTransport.cs +++ b/src/Sentry.Unity/UnityWebRequestTransport.cs @@ -7,118 +7,116 @@ using Sentry.Extensibility; using Sentry.Http; using Sentry.Protocol.Envelopes; -using UnityEngine; using UnityEngine.Networking; -namespace Sentry.Unity +namespace Sentry.Unity; + +internal class WebBackgroundWorker : IBackgroundWorker { - internal class WebBackgroundWorker : IBackgroundWorker - { - private readonly SentryMonoBehaviour _behaviour; - private readonly UnityWebRequestTransport _transport; + private readonly SentryMonoBehaviour _behaviour; + private readonly UnityWebRequestTransport _transport; - public WebBackgroundWorker(SentryUnityOptions options, SentryMonoBehaviour behaviour) - { - _behaviour = behaviour; - _transport = new UnityWebRequestTransport(options); - } + public WebBackgroundWorker(SentryUnityOptions options, SentryMonoBehaviour behaviour) + { + _behaviour = behaviour; + _transport = new UnityWebRequestTransport(options); + } - public bool EnqueueEnvelope(Envelope envelope) - { - _ = _behaviour.StartCoroutine(_transport.SendEnvelopeAsync(envelope)); - return true; - } + public bool EnqueueEnvelope(Envelope envelope) + { + _ = _behaviour.StartCoroutine(_transport.SendEnvelopeAsync(envelope)); + return true; + } - public Task FlushAsync(TimeSpan timeout) => Task.CompletedTask; + public Task FlushAsync(TimeSpan timeout) => Task.CompletedTask; - public int QueuedItems { get; } - } + public int QueuedItems { get; } +} - internal class UnityWebRequestTransport : HttpTransportBase - { - private readonly SentryUnityOptions _options; +internal class UnityWebRequestTransport : HttpTransportBase +{ + private readonly SentryUnityOptions _options; - public UnityWebRequestTransport(SentryUnityOptions options) - : base(options) + public UnityWebRequestTransport(SentryUnityOptions options) + : base(options) => _options = options; - // adapted HttpTransport.SendEnvelopeAsync() - internal IEnumerator SendEnvelopeAsync(Envelope envelope) + // adapted HttpTransport.SendEnvelopeAsync() + internal IEnumerator SendEnvelopeAsync(Envelope envelope) + { + using var processedEnvelope = ProcessEnvelope(envelope); + if (processedEnvelope.Items.Count > 0) { - using var processedEnvelope = ProcessEnvelope(envelope); - if (processedEnvelope.Items.Count > 0) - { - // Send envelope to ingress - var httpRequest = CreateRequest(processedEnvelope); - var www = CreateWebRequest(httpRequest); - yield return www.SendWebRequest(); + // Send envelope to ingress + var httpRequest = CreateRequest(processedEnvelope); + var www = CreateWebRequest(httpRequest); + yield return www.SendWebRequest(); - var response = GetResponse(www); - if (response is not null) - { - HandleResponse(response, processedEnvelope); - } + var response = GetResponse(www); + if (response is not null) + { + HandleResponse(response, processedEnvelope); } } + } - private UnityWebRequest CreateWebRequest(HttpRequestMessage message) + private UnityWebRequest CreateWebRequest(HttpRequestMessage message) + { + using var contentStream = ReadStreamFromHttpContent(message.Content); + var contentMemoryStream = contentStream as MemoryStream; + if (contentMemoryStream is null) { - using var contentStream = ReadStreamFromHttpContent(message.Content); - var contentMemoryStream = contentStream as MemoryStream; - if (contentMemoryStream is null) - { - contentMemoryStream = new MemoryStream(); - contentStream.CopyTo(contentMemoryStream); - contentMemoryStream.Flush(); - } - - var www = new UnityWebRequest - { - url = message.RequestUri.ToString(), - method = message.Method.Method.ToUpperInvariant(), - uploadHandler = new UploadHandlerRaw(contentMemoryStream.ToArray()), - downloadHandler = new DownloadHandlerBuffer() - }; + contentMemoryStream = new MemoryStream(); + contentStream.CopyTo(contentMemoryStream); + contentMemoryStream.Flush(); + } - foreach (var header in message.Headers) - { - www.SetRequestHeader(header.Key, string.Join(",", header.Value)); - } + var www = new UnityWebRequest + { + url = message.RequestUri.ToString(), + method = message.Method.Method.ToUpperInvariant(), + uploadHandler = new UploadHandlerRaw(contentMemoryStream.ToArray()), + downloadHandler = new DownloadHandlerBuffer() + }; - return www; + foreach (var header in message.Headers) + { + www.SetRequestHeader(header.Key, string.Join(",", header.Value)); } - private HttpResponseMessage? GetResponse(UnityWebRequest www) - { - // Let's disable treating "warning:obsolete" as an error here because the alternative of putting a static - // function to user code (to be able to use #if UNITY_2019) is just ugly. + return www; + } + + private HttpResponseMessage? GetResponse(UnityWebRequest www) + { + // Let's disable treating "warning:obsolete" as an error here because the alternative of putting a static + // function to user code (to be able to use #if UNITY_2019) is just ugly. #pragma warning disable 618 - // if (www.result == UnityWebRequest.Result.ConnectionError) // Unity 2020.1+; `.result` not present on 2019 - if (www.isNetworkError) // Unity 2019; obsolete (error) on later versions + // if (www.result == UnityWebRequest.Result.ConnectionError) // Unity 2020.1+; `.result` not present on 2019 + if (www.isNetworkError) // Unity 2019; obsolete (error) on later versions #pragma warning restore 618 - { - _options.DiagnosticLogger?.LogWarning("Failed to send request: {0}", www.error); - return null; - } + { + _options.DiagnosticLogger?.LogWarning("Failed to send request: {0}", www.error); + return null; + } - var response = new HttpResponseMessage((HttpStatusCode)www.responseCode); - foreach (var header in www.GetResponseHeaders()) + var response = new HttpResponseMessage((HttpStatusCode)www.responseCode); + foreach (var header in www.GetResponseHeaders()) + { + try { - try + // Unity would throw if we tried to set content-type, content-length, or content-encoding + if (!header.Key.StartsWith("content-", StringComparison.InvariantCultureIgnoreCase)) { - // Unity would throw if we tried to set content-type, content-length, or content-encoding - if (!header.Key.StartsWith("content-", StringComparison.InvariantCultureIgnoreCase)) - { - response.Headers.Add(header.Key, header.Value); - } - } - catch (InvalidOperationException e) - { - _options.DiagnosticLogger?.LogError(e, "Failed to extract response header: {0}", header.Key); + response.Headers.Add(header.Key, header.Value); } } - response.Content = new StringContent(www.downloadHandler.text); - return response; + catch (InvalidOperationException e) + { + _options.DiagnosticLogger?.LogError(e, "Failed to extract response header: {0}", header.Key); + } } + response.Content = new StringContent(www.downloadHandler.text); + return response; } -} +} \ No newline at end of file diff --git a/src/Sentry.Unity/WebGL/SentryWebGL.cs b/src/Sentry.Unity/WebGL/SentryWebGL.cs index 0f6795332..7a33f68ec 100644 --- a/src/Sentry.Unity/WebGL/SentryWebGL.cs +++ b/src/Sentry.Unity/WebGL/SentryWebGL.cs @@ -1,48 +1,47 @@ using Sentry.Extensibility; using UnityEngine.Analytics; -namespace Sentry.Unity.WebGL +namespace Sentry.Unity.WebGL; + +/// +/// Configure Sentry for WebGL +/// +public static class SentryWebGL { /// - /// Configure Sentry for WebGL + /// Configures the WebGL support. /// - public static class SentryWebGL + /// The Sentry Unity options to use. + public static void Configure(SentryUnityOptions options) { - /// - /// Configures the WebGL support. - /// - /// The Sentry Unity options to use. - public static void Configure(SentryUnityOptions options) + options.DiagnosticLogger?.LogDebug("Updating configuration for Unity WebGL."); + + // Note: we need to use a custom background worker which actually doesn't work in the background + // because Unity doesn't support async (multithreading) yet. This may change in the future so let's watch + // https://docs.unity3d.com/2019.4/Documentation/ScriptReference/PlayerSettings.WebGL-threadsSupport.html + options.BackgroundWorker = new WebBackgroundWorker(options, SentryMonoBehaviour.Instance); + + // No way to recognize crashes in WebGL yet. We may be able to do so after implementing the JS support. + // Additionally, we could recognize the situation when the unity gets stuck due to an error in JS/native: + // "An abnormal situation has occurred: the PlayerLoop internal function has been called recursively. + // Please contact Customer Support with a sample project so that we can reproduce the problem and troubleshoot it." + // Maybe we could write a file when this error occurs and recognize it on the next start. Like unity-native. + options.CrashedLastRun = () => false; + + // Disable async when accessing files (e.g. FileStream(useAsync: true)) because it throws on WebGL. + options.UseAsyncFileIO = false; + + if (options.AttachScreenshot) { - options.DiagnosticLogger?.LogDebug("Updating configuration for Unity WebGL."); - - // Note: we need to use a custom background worker which actually doesn't work in the background - // because Unity doesn't support async (multithreading) yet. This may change in the future so let's watch - // https://docs.unity3d.com/2019.4/Documentation/ScriptReference/PlayerSettings.WebGL-threadsSupport.html - options.BackgroundWorker = new WebBackgroundWorker(options, SentryMonoBehaviour.Instance); - - // No way to recognize crashes in WebGL yet. We may be able to do so after implementing the JS support. - // Additionally, we could recognize the situation when the unity gets stuck due to an error in JS/native: - // "An abnormal situation has occurred: the PlayerLoop internal function has been called recursively. - // Please contact Customer Support with a sample project so that we can reproduce the problem and troubleshoot it." - // Maybe we could write a file when this error occurs and recognize it on the next start. Like unity-native. - options.CrashedLastRun = () => false; - - // Disable async when accessing files (e.g. FileStream(useAsync: true)) because it throws on WebGL. - options.UseAsyncFileIO = false; - - if (options.AttachScreenshot) - { - options.AttachScreenshot = false; - options.DiagnosticLogger?.LogWarning("Attaching screenshots on WebGL is disabled - " + - "it currently produces blank screenshots mid-frame."); - } - - // Use AnalyticsSessionInfo.userId as the default UserID in native & dotnet - options.DefaultUserId = AnalyticsSessionInfo.userId; - - // Indicate that this platform doesn't support running background threads. - options.MultiThreading = false; + options.AttachScreenshot = false; + options.DiagnosticLogger?.LogWarning("Attaching screenshots on WebGL is disabled - " + + "it currently produces blank screenshots mid-frame."); } + + // Use AnalyticsSessionInfo.userId as the default UserID in native & dotnet + options.DefaultUserId = AnalyticsSessionInfo.userId; + + // Indicate that this platform doesn't support running background threads. + options.MultiThreading = false; } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs index a8c7061fb..a36de49de 100644 --- a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs +++ b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs @@ -2,21 +2,21 @@ using System.Threading; using NUnit.Framework; -namespace Sentry.Unity.Android.Tests +namespace Sentry.Unity.Android.Tests; + +public class SentryNativeAndroidTests { - public class SentryNativeAndroidTests - { - private bool _reinstallCalled; - private Action? _originalReinstallSentryNativeBackendStrategy; - private Action _fakeReinstallSentryNativeBackendStrategy; - private TestUnityInfo _sentryUnityInfo = null!; + private bool _reinstallCalled; + private Action? _originalReinstallSentryNativeBackendStrategy; + private Action _fakeReinstallSentryNativeBackendStrategy; + private TestUnityInfo _sentryUnityInfo = null!; - public SentryNativeAndroidTests() - => _fakeReinstallSentryNativeBackendStrategy = () => _reinstallCalled = true; + public SentryNativeAndroidTests() + => _fakeReinstallSentryNativeBackendStrategy = () => _reinstallCalled = true; - [SetUp] - public void SetUp() - { + [SetUp] + public void SetUp() + { _originalReinstallSentryNativeBackendStrategy = Interlocked.Exchange(ref SentryNative.ReinstallSentryNativeBackendStrategy, _fakeReinstallSentryNativeBackendStrategy); @@ -27,57 +27,57 @@ public void SetUp() SentryNativeAndroid.SentryJava = new TestSentryJava(); } - [TearDown] - public void TearDown() => - _fakeReinstallSentryNativeBackendStrategy = - Interlocked.Exchange(ref SentryNative.ReinstallSentryNativeBackendStrategy!, - _originalReinstallSentryNativeBackendStrategy)!; + [TearDown] + public void TearDown() => + _fakeReinstallSentryNativeBackendStrategy = + Interlocked.Exchange(ref SentryNative.ReinstallSentryNativeBackendStrategy!, + _originalReinstallSentryNativeBackendStrategy)!; - [Test] - public void Configure_DefaultConfiguration_SetsScopeObserver() - { + [Test] + public void Configure_DefaultConfiguration_SetsScopeObserver() + { var options = new SentryUnityOptions(); SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.IsAssignableFrom(options.ScopeObserver); } - [Test] - public void Configure_DefaultConfiguration_SetsCrashedLastRun() - { + [Test] + public void Configure_DefaultConfiguration_SetsCrashedLastRun() + { var options = new SentryUnityOptions(); SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.IsNotNull(options.CrashedLastRun); } - [Test] - public void Configure_NativeAndroidSupportDisabled_ObserverIsNull() - { + [Test] + public void Configure_NativeAndroidSupportDisabled_ObserverIsNull() + { var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.Null(options.ScopeObserver); } - [Test] - public void Configure_DefaultConfiguration_EnablesScopeSync() - { + [Test] + public void Configure_DefaultConfiguration_EnablesScopeSync() + { var options = new SentryUnityOptions(); SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.True(options.EnableScopeSync); } - [Test] - public void Configure_NativeAndroidSupportDisabled_DisabledScopeSync() - { + [Test] + public void Configure_NativeAndroidSupportDisabled_DisabledScopeSync() + { var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.False(options.EnableScopeSync); } - [Test] - [TestCase(true, true)] - [TestCase(false, true)] - public void Configure_IL2CPP_ReInitializesNativeBackend(bool il2cpp, bool expectedReinstall) - { + [Test] + [TestCase(true, true)] + [TestCase(false, true)] + public void Configure_IL2CPP_ReInitializesNativeBackend(bool il2cpp, bool expectedReinstall) + { _sentryUnityInfo.IL2CPP = il2cpp; Assert.False(_reinstallCalled); // Sanity check @@ -86,17 +86,17 @@ public void Configure_IL2CPP_ReInitializesNativeBackend(bool il2cpp, bool expect Assert.AreEqual(expectedReinstall, _reinstallCalled); } - [Test] - public void Configure_NativeAndroidSupportDisabled_DoesNotReInitializeNativeBackend() - { + [Test] + public void Configure_NativeAndroidSupportDisabled_DoesNotReInitializeNativeBackend() + { var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.False(_reinstallCalled); } - [Test] - public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() - { + [Test] + public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() + { var options = new SentryUnityOptions(); var sentryJava = SentryNativeAndroid.SentryJava as TestSentryJava; Assert.NotNull(sentryJava); @@ -105,5 +105,4 @@ public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() SentryNativeAndroid.Configure(options, _sentryUnityInfo); Assert.False(string.IsNullOrEmpty(options.DefaultUserId)); } - } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs b/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs index 744a9b50c..b6d1ab839 100644 --- a/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs +++ b/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs @@ -1,21 +1,20 @@ using System; -namespace Sentry.Unity.Android.Tests +namespace Sentry.Unity.Android.Tests; + +public class TestJniExecutor : IJniExecutor { - public class TestJniExecutor : IJniExecutor + public TResult? Run(Func jniOperation) { - public TResult? Run(Func jniOperation) - { - return default; - } + return default; + } - public void Run(Action jniOperation) - { - } + public void Run(Action jniOperation) + { + } - public void Dispose() - { - // TODO release managed resources here - } + public void Dispose() + { + // TODO release managed resources here } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs b/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs index fd0ab0796..a47d7f5c8 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Linq; using System.Reflection; using NUnit.Framework; using Sentry.Unity.Editor.Android; @@ -8,424 +7,423 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.Tests.Android +namespace Sentry.Unity.Editor.Tests.Android; + +public class AndroidManifestTests { - public class AndroidManifestTests + private class Fixture { - private class Fixture - { - public SentryUnityOptions? SentryUnityOptions { get; set; } - public SentryCliOptions? SentryCliOptions { get; set; } - public UnityTestLogger UnityTestLogger { get; set; } - public bool IsDevelopmentBuild { get; set; } - public ScriptingImplementation ScriptingImplementation { get; set; } = ScriptingImplementation.IL2CPP; + public SentryUnityOptions? SentryUnityOptions { get; set; } + public SentryCliOptions? SentryCliOptions { get; set; } + public UnityTestLogger UnityTestLogger { get; set; } + public bool IsDevelopmentBuild { get; set; } + public ScriptingImplementation ScriptingImplementation { get; set; } = ScriptingImplementation.IL2CPP; - public Fixture() + public Fixture() + { + UnityTestLogger = new(); + // Options configured to initialize the Android SDK, tests will change from there: + SentryUnityOptions = new() { - UnityTestLogger = new(); - // Options configured to initialize the Android SDK, tests will change from there: - SentryUnityOptions = new() - { - Enabled = true, - Dsn = "https://k@h/p", - AndroidNativeSupportEnabled = true, - Debug = true - }; - SentryUnityOptions.DiagnosticLogger = new UnityLogger(SentryUnityOptions, UnityTestLogger); - - SentryCliOptions = ScriptableObject.CreateInstance(); - SentryCliOptions.Auth = "test_auth_token"; - SentryCliOptions.Organization = "test_organization"; - SentryCliOptions.Project = "test_project"; - } - - public AndroidManifestConfiguration GetSut() => - new(() => (SentryUnityOptions, SentryCliOptions), - IsDevelopmentBuild, - ScriptingImplementation, - UnityTestLogger); + Enabled = true, + Dsn = "https://k@h/p", + AndroidNativeSupportEnabled = true, + Debug = true + }; + SentryUnityOptions.DiagnosticLogger = new UnityLogger(SentryUnityOptions, UnityTestLogger); + + SentryCliOptions = ScriptableObject.CreateInstance(); + SentryCliOptions.Auth = "test_auth_token"; + SentryCliOptions.Organization = "test_organization"; + SentryCliOptions.Project = "test_project"; } - [SetUp] - public void SetUp() => _fixture = new Fixture(); - private Fixture _fixture = null!; + public AndroidManifestConfiguration GetSut() => + new(() => (SentryUnityOptions, SentryCliOptions), + IsDevelopmentBuild, + ScriptingImplementation, + UnityTestLogger); + } - [Test] - public void ModifyManifest_BrokenPath_ThrowsFileNotFound() - { - var sut = _fixture.GetSut(); - const string brokenBasePath = "broken-path"; - var ex = Assert.Throws(() => sut.ModifyManifest(brokenBasePath)); + [SetUp] + public void SetUp() => _fixture = new Fixture(); + private Fixture _fixture = null!; - Assert.AreEqual( - Path.Combine(brokenBasePath, "src", "main", "AndroidManifest.xml"), - ex.FileName); + [Test] + public void ModifyManifest_BrokenPath_ThrowsFileNotFound() + { + var sut = _fixture.GetSut(); + const string brokenBasePath = "broken-path"; + var ex = Assert.Throws(() => sut.ModifyManifest(brokenBasePath)); - Assert.AreEqual( - "Can't configure native Android SDK nor set auto-init:false.", - ex.Message); - } + Assert.AreEqual( + Path.Combine(brokenBasePath, "src", "main", "AndroidManifest.xml"), + ex.FileName); - [Test] - public void ModifyManifest_LoadSentryUnityOptions_NullOptions_LogWarningAndDoesNotAddSentry() - { - _fixture.SentryUnityOptions = null; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.AreEqual( + "Can't configure native Android SDK nor set auto-init:false.", + ex.Message); + } - _fixture.UnityTestLogger.AssertLogContains( - SentryLevel.Warning, - "Android native support disabled because Sentry has not been configured. " + - "You can do that through the editor: Tools -> Sentry"); + [Test] + public void ModifyManifest_LoadSentryUnityOptions_NullOptions_LogWarningAndDoesNotAddSentry() + { + _fixture.SentryUnityOptions = null; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Debug.Log($"Manifest:\n{manifest}"); - Assert.False(manifest.Contains("io.sentry.dsn")); - } + _fixture.UnityTestLogger.AssertLogContains( + SentryLevel.Warning, + "Android native support disabled because Sentry has not been configured. " + + "You can do that through the editor: Tools -> Sentry"); - [Test] - public void ModifyManifest_UnityOptions_EnabledFalse_LogDebugAndDoesNotAddSentry() - { - _fixture.SentryUnityOptions!.Enabled = false; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Debug.Log($"Manifest:\n{manifest}"); + Assert.False(manifest.Contains("io.sentry.dsn")); + } - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Sentry SDK has been disabled.\nYou can disable this log by raising the debug verbosity level above 'Debug'."); + [Test] + public void ModifyManifest_UnityOptions_EnabledFalse_LogDebugAndDoesNotAddSentry() + { + _fixture.SentryUnityOptions!.Enabled = false; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Debug.Log($"Manifest:\n{manifest}"); - Assert.False(manifest.Contains("io.sentry.dsn")); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Sentry SDK has been disabled.\nYou can disable this log by raising the debug verbosity level above 'Debug'."); - [Test] - [TestCase(null)] - [TestCase("")] - [TestCase(" ")] - public void ModifyManifest_UnityOptions_EnabledWithoutDsn_LogWarningAndDoesNotAddSentry(string? dsn) - { - _fixture.SentryUnityOptions!.Dsn = dsn; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Debug.Log($"Manifest:\n{manifest}"); + Assert.False(manifest.Contains("io.sentry.dsn")); + } - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Warning, "No Sentry DSN configured. Sentry will be disabled."); + [Test] + [TestCase(null)] + [TestCase("")] + [TestCase(" ")] + public void ModifyManifest_UnityOptions_EnabledWithoutDsn_LogWarningAndDoesNotAddSentry(string? dsn) + { + _fixture.SentryUnityOptions!.Dsn = dsn; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Debug.Log($"Manifest:\n{manifest}"); - Assert.False(manifest.Contains("io.sentry.dsn")); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Warning, "No Sentry DSN configured. Sentry will be disabled."); - [Test] - public void ModifyManifest_UnityOptions_AndroidNativeSupportEnabledFalse_LogDebugAndDoesNotAddSentry() - { - _fixture.SentryUnityOptions!.AndroidNativeSupportEnabled = false; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Debug.Log($"Manifest:\n{manifest}"); + Assert.False(manifest.Contains("io.sentry.dsn")); + } - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Android native support disabled through the options."); + [Test] + public void ModifyManifest_UnityOptions_AndroidNativeSupportEnabledFalse_LogDebugAndDoesNotAddSentry() + { + _fixture.SentryUnityOptions!.AndroidNativeSupportEnabled = false; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Debug.Log($"Manifest:\n{manifest}"); - Assert.False(manifest.Contains("io.sentry.dsn")); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Android native support disabled through the options."); - [Test] - public void ModifyManifest_ManifestHasDsn() - { - var expected = _fixture.SentryUnityOptions!.Dsn; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Debug.Log($"Manifest:\n{manifest}"); + Assert.False(manifest.Contains("io.sentry.dsn")); + } - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting DSN: {expected}"); + [Test] + public void ModifyManifest_ManifestHasDsn() + { + var expected = _fixture.SentryUnityOptions!.Dsn; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Assert.True(manifest.Contains( - $""), - $"Expected 'DSN' in Manifest:\n{manifest}"); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting DSN: {expected}"); - [Test] - public void ModifyManifest_ReleaseIsNull_ReleaseNotSet() - { - _fixture.SentryUnityOptions!.Release = null; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.True(manifest.Contains( + $""), + $"Expected 'DSN' in Manifest:\n{manifest}"); + } - Assert.False(manifest.Contains( - " sut.ModifyManifest(basePath)); - [Test] - public void ModifyManifest_ReleaseIsNotNull_SetRelease() - { - const string? expected = "expected release"; - _fixture.SentryUnityOptions!.Release = expected; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.False(manifest.Contains( + " sut.ModifyManifest(basePath)); - Assert.True(manifest.Contains( - $""), - $"Expected 'release' in Manifest:\n{manifest}"); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting Release: {expected}"); - [Test] - public void ModifyManifest_EnvironmentIsNull_EnvironmentNotSet() - { - _fixture.SentryUnityOptions!.Environment = null; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.True(manifest.Contains( + $""), + $"Expected 'release' in Manifest:\n{manifest}"); + } - Assert.False(manifest.Contains( - " sut.ModifyManifest(basePath)); - [Test] - public void ModifyManifest_EnvironmentIsNotNull_SetEnvironment() - { - const string? expected = "expected env"; - _fixture.SentryUnityOptions!.Environment = expected; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.False(manifest.Contains( + " sut.ModifyManifest(basePath)); - Assert.True(manifest.Contains( - $""), - $"Expected 'environment' in Manifest:\n{manifest}"); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting Environment: {expected}"); - // options.setDiagnosticLevel(SentryLevel.valueOf(level.toUpperCase(Locale.ROOT))); - // modules/sentry-java/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java - // modules/sentry-java/sentry/src/main/java/io/sentry/SentryLevel.java - private static readonly SentryJavaLevel[] SentryJavaLevels = - { - new () { SentryLevel = SentryLevel.Debug, JavaLevel = "debug" }, - new () { SentryLevel = SentryLevel.Error, JavaLevel = "error" }, - new () { SentryLevel = SentryLevel.Fatal, JavaLevel = "fatal" }, - new () { SentryLevel = SentryLevel.Info, JavaLevel = "info" }, - new () { SentryLevel = SentryLevel.Warning, JavaLevel = "warning" } - }; - - [Test] - public void ModifyManifest_DiagnosticLevel_TestCases( - [ValueSource(nameof(SentryJavaLevels))] SentryJavaLevel levels) - { - _fixture.SentryUnityOptions!.DiagnosticLevel = levels.SentryLevel; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.True(manifest.Contains( + $""), + $"Expected 'environment' in Manifest:\n{manifest}"); + } - // Debug message is only logged if level is Debug: - if (levels.SentryLevel == SentryLevel.Debug) - { - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting DiagnosticLevel: {levels.SentryLevel}"); - } + // options.setDiagnosticLevel(SentryLevel.valueOf(level.toUpperCase(Locale.ROOT))); + // modules/sentry-java/sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java + // modules/sentry-java/sentry/src/main/java/io/sentry/SentryLevel.java + private static readonly SentryJavaLevel[] SentryJavaLevels = + { + new () { SentryLevel = SentryLevel.Debug, JavaLevel = "debug" }, + new () { SentryLevel = SentryLevel.Error, JavaLevel = "error" }, + new () { SentryLevel = SentryLevel.Fatal, JavaLevel = "fatal" }, + new () { SentryLevel = SentryLevel.Info, JavaLevel = "info" }, + new () { SentryLevel = SentryLevel.Warning, JavaLevel = "warning" } + }; + + [Test] + public void ModifyManifest_DiagnosticLevel_TestCases( + [ValueSource(nameof(SentryJavaLevels))] SentryJavaLevel levels) + { + _fixture.SentryUnityOptions!.DiagnosticLevel = levels.SentryLevel; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Assert.True(manifest.Contains( - $""), - $"Expected 'io.sentry.debug.level' in Manifest:\n{manifest}"); + // Debug message is only logged if level is Debug: + if (levels.SentryLevel == SentryLevel.Debug) + { + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting DiagnosticLevel: {levels.SentryLevel}"); } - [Test] - public void ModifyManifest_SampleRate_SetIfNotNull() - { - const float expected = 0.6f; - _fixture.SentryUnityOptions!.SampleRate = expected; - var sut = _fixture.GetSut(); - var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + Assert.True(manifest.Contains( + $""), + $"Expected 'io.sentry.debug.level' in Manifest:\n{manifest}"); + } - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting SampleRate: {expected}"); + [Test] + public void ModifyManifest_SampleRate_SetIfNotNull() + { + const float expected = 0.6f; + _fixture.SentryUnityOptions!.SampleRate = expected; + var sut = _fixture.GetSut(); + var manifest = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - Assert.True(manifest.Contains( - $""), - $"Expected 'io.sentry.sample-rate' in Manifest:\n{manifest}"); - } + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, $"Setting SampleRate: {expected}"); + + Assert.True(manifest.Contains( + $""), + $"Expected 'io.sentry.sample-rate' in Manifest:\n{manifest}"); + } - [Test] - public void ModifyManifest_RepeatedRunProducesSameResult() + [Test] + public void ModifyManifest_RepeatedRunProducesSameResult() + { + var sut = _fixture.GetSut(); + var manifest1 = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); + var manifest2 = WithAndroidManifest((basePath) => { - var sut = _fixture.GetSut(); - var manifest1 = WithAndroidManifest(basePath => sut.ModifyManifest(basePath)); - var manifest2 = WithAndroidManifest((basePath) => - { - sut.ModifyManifest(basePath); - sut.ModifyManifest(basePath); - }); + sut.ModifyManifest(basePath); + sut.ModifyManifest(basePath); + }); - Debug.Log(manifest2); - Assert.True(manifest1.Contains("sentry.dsn")); - Assert.AreEqual(manifest1, manifest2); - } + Debug.Log(manifest2); + Assert.True(manifest1.Contains("sentry.dsn")); + Assert.AreEqual(manifest1, manifest2); + } - [Test] - public void ModifyManifest_RepeatedRunOverwritesConfigs() + [Test] + public void ModifyManifest_RepeatedRunOverwritesConfigs() + { + var fixture2 = new Fixture(); + fixture2.SentryUnityOptions!.Dsn = "fixture_2_dsn"; + var manifest1 = WithAndroidManifest(basePath => _fixture.GetSut().ModifyManifest(basePath)); + var manifest2 = WithAndroidManifest((basePath) => { - var fixture2 = new Fixture(); - fixture2.SentryUnityOptions!.Dsn = "fixture_2_dsn"; - var manifest1 = WithAndroidManifest(basePath => _fixture.GetSut().ModifyManifest(basePath)); - var manifest2 = WithAndroidManifest((basePath) => - { - _fixture.GetSut().ModifyManifest(basePath); - fixture2.GetSut().ModifyManifest(basePath); - }); - - Debug.Log($"Manifest 1 (before):\n{manifest1}"); - Debug.Log($"Manifest 2 (after):\n{manifest2}"); - Assert.True(manifest1.Contains($"io.sentry.dsn\" android:value=\"{_fixture.SentryUnityOptions!.Dsn}")); - Assert.False(manifest2.Contains($"io.sentry.dsn\" android:value=\"{_fixture.SentryUnityOptions!.Dsn}")); // Sanity check - Assert.True(manifest2.Contains($"io.sentry.dsn\" android:value=\"{fixture2.SentryUnityOptions!.Dsn}")); - } + _fixture.GetSut().ModifyManifest(basePath); + fixture2.GetSut().ModifyManifest(basePath); + }); + + Debug.Log($"Manifest 1 (before):\n{manifest1}"); + Debug.Log($"Manifest 2 (after):\n{manifest2}"); + Assert.True(manifest1.Contains($"io.sentry.dsn\" android:value=\"{_fixture.SentryUnityOptions!.Dsn}")); + Assert.False(manifest2.Contains($"io.sentry.dsn\" android:value=\"{_fixture.SentryUnityOptions!.Dsn}")); // Sanity check + Assert.True(manifest2.Contains($"io.sentry.dsn\" android:value=\"{fixture2.SentryUnityOptions!.Dsn}")); + } - [Test] - public void SetupSymbolsUpload_SentryCliOptionsNull_LogsWarningAndReturns() - { - var dsuFixture = new DebugSymbolUploadTests.Fixture(); - DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); + [Test] + public void SetupSymbolsUpload_SentryCliOptionsNull_LogsWarningAndReturns() + { + var dsuFixture = new DebugSymbolUploadTests.Fixture(); + DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); - _fixture.SentryCliOptions = null; - var sut = _fixture.GetSut(); + _fixture.SentryCliOptions = null; + var sut = _fixture.GetSut(); - sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); + sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Warning, "Failed to load sentry-cli options."); + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Warning, "Failed to load sentry-cli options."); - Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); - } + Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); + } - [Test] - public void SetupSymbolsUpload_SymbolsUploadDisabled_LogsAndReturns() - { - var dsuFixture = new DebugSymbolUploadTests.Fixture(); - DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); + [Test] + public void SetupSymbolsUpload_SymbolsUploadDisabled_LogsAndReturns() + { + var dsuFixture = new DebugSymbolUploadTests.Fixture(); + DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); - _fixture.SentryCliOptions!.UploadSymbols = false; - var sut = _fixture.GetSut(); + _fixture.SentryCliOptions!.UploadSymbols = false; + var sut = _fixture.GetSut(); - sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); + sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Automated symbols upload has been disabled."); + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Automated symbols upload has been disabled."); - Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); - } + Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); + } - [Test] - public void SetupSymbolsUpload_DevelopmentBuildDevUploadDisabled_LogsAndReturns() - { - var dsuFixture = new DebugSymbolUploadTests.Fixture(); - DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); + [Test] + public void SetupSymbolsUpload_DevelopmentBuildDevUploadDisabled_LogsAndReturns() + { + var dsuFixture = new DebugSymbolUploadTests.Fixture(); + DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); - _fixture.IsDevelopmentBuild = true; - var sut = _fixture.GetSut(); + _fixture.IsDevelopmentBuild = true; + var sut = _fixture.GetSut(); - sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); + sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Automated symbols upload for development builds has been disabled."); + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "Automated symbols upload for development builds has been disabled."); - Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); - } + Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); + } - [Test] - public void SetupSymbolsUpload_SentryCliOptionsInvalid_LogsAndReturns() - { - var dsuFixture = new DebugSymbolUploadTests.Fixture(); - DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); + [Test] + public void SetupSymbolsUpload_SentryCliOptionsInvalid_LogsAndReturns() + { + var dsuFixture = new DebugSymbolUploadTests.Fixture(); + DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); - _fixture.SentryCliOptions!.Auth = string.Empty; - var sut = _fixture.GetSut(); + _fixture.SentryCliOptions!.Auth = string.Empty; + var sut = _fixture.GetSut(); - sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); + sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Warning, "sentry-cli validation failed. Symbols will not be uploaded." + - "\nYou can disable this warning by disabling the automated symbols upload under " + - SentryCliOptions.EditorMenuPath); + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Warning, "sentry-cli validation failed. Symbols will not be uploaded." + + "\nYou can disable this warning by disabling the automated symbols upload under " + + SentryCliOptions.EditorMenuPath); - Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); - } + Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); + } - [Test] - [TestCase(ScriptingImplementation.IL2CPP)] - [TestCase(ScriptingImplementation.Mono2x)] - public void SetupSymbolsUpload_ValidConfiguration_AppendsUploadTaskToGradleAndCreatesSentryProperties(ScriptingImplementation scriptingImplementation) - { - var dsuFixture = new DebugSymbolUploadTests.Fixture(); - DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); + [Test] + [TestCase(ScriptingImplementation.IL2CPP)] + [TestCase(ScriptingImplementation.Mono2x)] + public void SetupSymbolsUpload_ValidConfiguration_AppendsUploadTaskToGradleAndCreatesSentryProperties(ScriptingImplementation scriptingImplementation) + { + var dsuFixture = new DebugSymbolUploadTests.Fixture(); + DebugSymbolUploadTests.SetupFakeProject(dsuFixture.FakeProjectPath); - _fixture.ScriptingImplementation = scriptingImplementation; - var sut = _fixture.GetSut(); + _fixture.ScriptingImplementation = scriptingImplementation; + var sut = _fixture.GetSut(); - sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); + sut.SetupSymbolsUpload(dsuFixture.UnityProjectPath, dsuFixture.GradleProjectPath); - StringAssert.Contains("println 'Uploading symbols to Sentry", - File.ReadAllText(Path.Combine(dsuFixture.GradleProjectPath, "launcher/build.gradle"))); - Assert.True(File.Exists(Path.Combine(dsuFixture.GradleProjectPath, "launcher/sentry.properties"))); + StringAssert.Contains("println 'Uploading symbols to Sentry", + File.ReadAllText(Path.Combine(dsuFixture.GradleProjectPath, "launcher/build.gradle"))); + Assert.True(File.Exists(Path.Combine(dsuFixture.GradleProjectPath, "launcher/sentry.properties"))); - Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); - } + Directory.Delete(Path.GetFullPath(dsuFixture.FakeProjectPath), true); + } - [Test] - public void CopyAndroidSdkToGradleProject_AndroidNativeSupportEnabled_CopiesAndroidSdkToGradleProject() - { - var fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var unityProjectPath = Path.Combine(fakeProjectPath, "UnityProject"); - var gradleProjectPath = Path.Combine(fakeProjectPath, "GradleProject"); - DebugSymbolUploadTests.SetupFakeProject(fakeProjectPath); - var sut = _fixture.GetSut(); + [Test] + public void CopyAndroidSdkToGradleProject_AndroidNativeSupportEnabled_CopiesAndroidSdkToGradleProject() + { + var fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var unityProjectPath = Path.Combine(fakeProjectPath, "UnityProject"); + var gradleProjectPath = Path.Combine(fakeProjectPath, "GradleProject"); + DebugSymbolUploadTests.SetupFakeProject(fakeProjectPath); + var sut = _fixture.GetSut(); - sut.CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath); + sut.CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath); - Assert.IsTrue(File.Exists(Path.Combine(gradleProjectPath, "unityLibrary", "libs", "androidSdk.jar"))); + Assert.IsTrue(File.Exists(Path.Combine(gradleProjectPath, "unityLibrary", "libs", "androidSdk.jar"))); - Directory.Delete(fakeProjectPath, true); - } + Directory.Delete(fakeProjectPath, true); + } - [Test] - public void CopyAndroidSdkToGradleProject_AndroidNativeSupportDisabledButSdkAlreadyCopied_RemovesAndroidSdkFromGradleProject() - { - var fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var unityProjectPath = Path.Combine(fakeProjectPath, "UnityProject"); - var gradleProjectPath = Path.Combine(fakeProjectPath, "GradleProject"); - DebugSymbolUploadTests.SetupFakeProject(fakeProjectPath); - var androidSdk = Path.Combine(gradleProjectPath, "unityLibrary", "libs", "androidSdk.jar"); - File.Create(androidSdk); + [Test] + public void CopyAndroidSdkToGradleProject_AndroidNativeSupportDisabledButSdkAlreadyCopied_RemovesAndroidSdkFromGradleProject() + { + var fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var unityProjectPath = Path.Combine(fakeProjectPath, "UnityProject"); + var gradleProjectPath = Path.Combine(fakeProjectPath, "GradleProject"); + DebugSymbolUploadTests.SetupFakeProject(fakeProjectPath); + var androidSdk = Path.Combine(gradleProjectPath, "unityLibrary", "libs", "androidSdk.jar"); + File.Create(androidSdk); - _fixture.SentryUnityOptions!.AndroidNativeSupportEnabled = false; - var sut = _fixture.GetSut(); + _fixture.SentryUnityOptions!.AndroidNativeSupportEnabled = false; + var sut = _fixture.GetSut(); - sut.CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath); + sut.CopyAndroidSdkToGradleProject(unityProjectPath, gradleProjectPath); - Assert.IsFalse(File.Exists(androidSdk)); + Assert.IsFalse(File.Exists(androidSdk)); - Directory.Delete(fakeProjectPath, true); - } + Directory.Delete(fakeProjectPath, true); + } - private string WithAndroidManifest(Action callback) + private string WithAndroidManifest(Action callback) + { + var basePath = GetFakeManifestFileBasePath(); + var androidManifest = AndroidManifestConfiguration.GetManifestPath(basePath); + try { - var basePath = GetFakeManifestFileBasePath(); - var androidManifest = AndroidManifestConfiguration.GetManifestPath(basePath); - try - { - callback(basePath); - return File.ReadAllText(androidManifest); - } - finally - { - Directory.Delete(basePath, true); - } + callback(basePath); + return File.ReadAllText(androidManifest); } - - private string GetFakeManifestFileBasePath() + finally { - var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var newAndroidManifest = Path.Combine(assemblyPath, "TestFiles", "Android", "Empty-AndroidManifest.xml"); - - var basePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - var destination = Path.Combine(basePath, "src", "main"); - Directory.CreateDirectory(destination); - File.Copy(newAndroidManifest, Path.Combine(destination, "AndroidManifest.xml"), false); - return basePath; + Directory.Delete(basePath, true); } + } - public struct SentryJavaLevel - { - public SentryLevel SentryLevel; - public string JavaLevel; - } + private string GetFakeManifestFileBasePath() + { + var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var newAndroidManifest = Path.Combine(assemblyPath, "TestFiles", "Android", "Empty-AndroidManifest.xml"); + + var basePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + var destination = Path.Combine(basePath, "src", "main"); + Directory.CreateDirectory(destination); + File.Copy(newAndroidManifest, Path.Combine(destination, "AndroidManifest.xml"), false); + return basePath; + } + + public struct SentryJavaLevel + { + public SentryLevel SentryLevel; + public string JavaLevel; } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs b/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs index dc6c149d9..b64b16617 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs @@ -8,252 +8,251 @@ using Sentry.Unity.Tests.SharedClasses; using Sentry.Unity.Tests.Stubs; -namespace Sentry.Unity.Editor.Tests.Android +namespace Sentry.Unity.Editor.Tests.Android; + +public class DebugSymbolUploadTests { - public class DebugSymbolUploadTests + public class Fixture { - public class Fixture + internal UnityTestLogger UnityTestLogger { get; set; } + public string FakeProjectPath { get; set; } + public string UnityProjectPath { get; set; } + public string GradleProjectPath { get; set; } + public string SentryCliPath { get; set; } + + public bool IsExporting { get; set; } + public bool IsMinifyEnabled { get; set; } + public bool IgnoreCliErrors { get; set; } + public TestApplication Application { get; set; } + + public Fixture() { - internal UnityTestLogger UnityTestLogger { get; set; } - public string FakeProjectPath { get; set; } - public string UnityProjectPath { get; set; } - public string GradleProjectPath { get; set; } - public string SentryCliPath { get; set; } - - public bool IsExporting { get; set; } - public bool IsMinifyEnabled { get; set; } - public bool IgnoreCliErrors { get; set; } - public TestApplication Application { get; set; } - - public Fixture() - { - UnityTestLogger = new(); + UnityTestLogger = new(); - FakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + FakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - UnityProjectPath = Path.Combine(FakeProjectPath, "UnityProject"); - GradleProjectPath = Path.Combine(FakeProjectPath, "GradleProject"); - SentryCliPath = Path.Combine(FakeProjectPath, "fake-sentry-cli"); + UnityProjectPath = Path.Combine(FakeProjectPath, "UnityProject"); + GradleProjectPath = Path.Combine(FakeProjectPath, "GradleProject"); + SentryCliPath = Path.Combine(FakeProjectPath, "fake-sentry-cli"); - Application = new TestApplication(unityVersion: "2019.4"); - } - - internal DebugSymbolUpload GetSut() => new( - new UnityLogger(new SentryOptions(), UnityTestLogger), - new SentryCliOptions() { UploadSources = true, IgnoreCliErrors = IgnoreCliErrors }, - UnityProjectPath, - GradleProjectPath, - IsExporting, - IsMinifyEnabled, - Application - ); + Application = new TestApplication(unityVersion: "2019.4"); } - [SetUp] - public void SetUp() - { - _fixture = new Fixture(); - SetupFakeProject(_fixture.FakeProjectPath); - } + internal DebugSymbolUpload GetSut() => new( + new UnityLogger(new SentryOptions(), UnityTestLogger), + new SentryCliOptions() { UploadSources = true, IgnoreCliErrors = IgnoreCliErrors }, + UnityProjectPath, + GradleProjectPath, + IsExporting, + IsMinifyEnabled, + Application + ); + } - private Fixture _fixture = null!; // created through SetUp + [SetUp] + public void SetUp() + { + _fixture = new Fixture(); + SetupFakeProject(_fixture.FakeProjectPath); + } - [TearDown] - public void TearDown() => Directory.Delete(Path.GetFullPath(_fixture.FakeProjectPath), true); + private Fixture _fixture = null!; // created through SetUp - [Test] - [TestCase("2019.4", false)] - [TestCase("2020.3", false)] - [TestCase("2021.1", false)] - [TestCase("2021.2", true)] - [TestCase("2022.1", true)] - public void IsNewBuildingBackend(string unityVersion, bool expectedIsNewBuildingBackend) - { - _fixture.Application = new TestApplication(unityVersion: unityVersion); + [TearDown] + public void TearDown() => Directory.Delete(Path.GetFullPath(_fixture.FakeProjectPath), true); - var actualIsNewBuildingBackend = DebugSymbolUpload.IsNewBuildingBackend(_fixture.Application); + [Test] + [TestCase("2019.4", false)] + [TestCase("2020.3", false)] + [TestCase("2021.1", false)] + [TestCase("2021.2", true)] + [TestCase("2022.1", true)] + public void IsNewBuildingBackend(string unityVersion, bool expectedIsNewBuildingBackend) + { + _fixture.Application = new TestApplication(unityVersion: unityVersion); - Assert.AreEqual(expectedIsNewBuildingBackend, actualIsNewBuildingBackend); - } + var actualIsNewBuildingBackend = DebugSymbolUpload.IsNewBuildingBackend(_fixture.Application); - [Test] - [TestCase("2019.4", DebugSymbolUpload.RelativeBuildOutputPathOld, DebugSymbolUpload.RelativeGradlePathOld)] - [TestCase("2021.2", DebugSymbolUpload.RelativeBuildOutputPathNew, DebugSymbolUpload.RelativeAndroidPathNew)] - public void GetSymbolUploadPaths_IsExportingFalse_ReturnsCorrectPathForVersion(string unityVersion, - string relativeBuildPath, string gradlePath) - { - _fixture.IsExporting = false; - var sut = _fixture.GetSut(); - - var actualSymbolsPaths = sut.GetSymbolUploadPaths(); + Assert.AreEqual(expectedIsNewBuildingBackend, actualIsNewBuildingBackend); + } - Assert.NotNull(actualSymbolsPaths.Any(path => path.Contains(relativeBuildPath))); - Assert.NotNull(actualSymbolsPaths.Any(path => path.Contains(gradlePath))); - } + [Test] + [TestCase("2019.4", DebugSymbolUpload.RelativeBuildOutputPathOld, DebugSymbolUpload.RelativeGradlePathOld)] + [TestCase("2021.2", DebugSymbolUpload.RelativeBuildOutputPathNew, DebugSymbolUpload.RelativeAndroidPathNew)] + public void GetSymbolUploadPaths_IsExportingFalse_ReturnsCorrectPathForVersion(string unityVersion, + string relativeBuildPath, string gradlePath) + { + _fixture.IsExporting = false; + var sut = _fixture.GetSut(); - [Test] - public void GetSymbolUploadPaths_IsExportingTrue_ReturnsPathToExportedProject() - { - _fixture.IsExporting = true; - var sut = _fixture.GetSut(); + var actualSymbolsPaths = sut.GetSymbolUploadPaths(); - var actualSymbolPaths = sut.GetSymbolUploadPaths(); + Assert.NotNull(actualSymbolsPaths.Any(path => path.Contains(relativeBuildPath))); + Assert.NotNull(actualSymbolsPaths.Any(path => path.Contains(gradlePath))); + } - Assert.AreEqual(2, actualSymbolPaths.Count); - } + [Test] + public void GetSymbolUploadPaths_IsExportingTrue_ReturnsPathToExportedProject() + { + _fixture.IsExporting = true; + var sut = _fixture.GetSut(); - [Test] - public void AppendUploadToGradleFile_SentryCliFileDoesNotExist_ThrowsFileNotFoundException() - { - var invalidSentryCliPath = Path.GetRandomFileName(); - var sut = _fixture.GetSut(); + var actualSymbolPaths = sut.GetSymbolUploadPaths(); - var ex = Assert.Throws(() => sut.AppendUploadToGradleFile(invalidSentryCliPath)); - Assert.AreEqual(invalidSentryCliPath, ex.FileName); - } + Assert.AreEqual(2, actualSymbolPaths.Count); + } - [Test] - public void AppendUploadToGradleFile_BuildGradleFileDoesNotExist_ThrowsFileNotFoundException() - { - _fixture.GradleProjectPath = Path.GetRandomFileName(); - var sut = _fixture.GetSut(); + [Test] + public void AppendUploadToGradleFile_SentryCliFileDoesNotExist_ThrowsFileNotFoundException() + { + var invalidSentryCliPath = Path.GetRandomFileName(); + var sut = _fixture.GetSut(); - var ex = Assert.Throws(() => sut.AppendUploadToGradleFile(_fixture.SentryCliPath)); - Assert.AreEqual(GetGradleFilePath(), ex.FileName); - } + var ex = Assert.Throws(() => sut.AppendUploadToGradleFile(invalidSentryCliPath)); + Assert.AreEqual(invalidSentryCliPath, ex.FileName); + } - [Test] - [TestCase(false, false, false)] - [TestCase(true, false, false)] - [TestCase(true, true, false)] - [TestCase(false, true, false)] - [TestCase(false, false, true)] - [TestCase(true, false, true)] - [TestCase(true, true, true)] - [TestCase(false, true, true)] - public void AppendUploadToGradleFile_AllRequirementsMet_AppendsUploadTask(bool isExporting, bool addMapping, bool ignoreCliErrors) - { - _fixture.IsExporting = isExporting; - _fixture.IsMinifyEnabled = addMapping; - _fixture.IgnoreCliErrors = ignoreCliErrors; - var sut = _fixture.GetSut(); + [Test] + public void AppendUploadToGradleFile_BuildGradleFileDoesNotExist_ThrowsFileNotFoundException() + { + _fixture.GradleProjectPath = Path.GetRandomFileName(); + var sut = _fixture.GetSut(); - sut.AppendUploadToGradleFile(_fixture.SentryCliPath); - var actualFileContent = File.ReadAllText(GetGradleFilePath()); + var ex = Assert.Throws(() => sut.AppendUploadToGradleFile(_fixture.SentryCliPath)); + Assert.AreEqual(GetGradleFilePath(), ex.FileName); + } - var keywords = new List - { - "// Autogenerated Sentry symbol upload task [start]", - "// Autogenerated Sentry symbol upload task [end]", - "sentry.properties", - "args = ['debug-files', 'upload', '--il2cpp-mapping', '--include-sources'", - "tasks.assembleDebug.finalizedBy sentryUploadSymbols", - "tasks.assembleRelease.finalizedBy sentryUploadSymbols", - "tasks.bundleDebug.finalizedBy sentryUploadSymbols", - "tasks.bundleRelease.finalizedBy sentryUploadSymbols" - }; + [Test] + [TestCase(false, false, false)] + [TestCase(true, false, false)] + [TestCase(true, true, false)] + [TestCase(false, true, false)] + [TestCase(false, false, true)] + [TestCase(true, false, true)] + [TestCase(true, true, true)] + [TestCase(false, true, true)] + public void AppendUploadToGradleFile_AllRequirementsMet_AppendsUploadTask(bool isExporting, bool addMapping, bool ignoreCliErrors) + { + _fixture.IsExporting = isExporting; + _fixture.IsMinifyEnabled = addMapping; + _fixture.IgnoreCliErrors = ignoreCliErrors; + var sut = _fixture.GetSut(); - if (!isExporting) - { - keywords.Add("logFilePath"); - } + sut.AppendUploadToGradleFile(_fixture.SentryCliPath); + var actualFileContent = File.ReadAllText(GetGradleFilePath()); - if (!isExporting && ignoreCliErrors) - { - keywords.Add("try {"); - keywords.Add("} catch"); - } + var keywords = new List + { + "// Autogenerated Sentry symbol upload task [start]", + "// Autogenerated Sentry symbol upload task [end]", + "sentry.properties", + "args = ['debug-files', 'upload', '--il2cpp-mapping', '--include-sources'", + "tasks.assembleDebug.finalizedBy sentryUploadSymbols", + "tasks.assembleRelease.finalizedBy sentryUploadSymbols", + "tasks.bundleDebug.finalizedBy sentryUploadSymbols", + "tasks.bundleRelease.finalizedBy sentryUploadSymbols" + }; + + if (!isExporting) + { + keywords.Add("logFilePath"); + } - if (addMapping) - { - keywords.Add("args = ['upload-proguard'"); - if (!isExporting) - { - keywords.Add("mappingLogFile"); - } - } + if (!isExporting && ignoreCliErrors) + { + keywords.Add("try {"); + keywords.Add("} catch"); + } - foreach (var keyword in keywords) + if (addMapping) + { + keywords.Add("args = ['upload-proguard'"); + if (!isExporting) { - StringAssert.Contains(keyword, actualFileContent); + keywords.Add("mappingLogFile"); } } - [Test] - public void RemoveUploadTaskFromGradleFile_GradleFileDoesNotExist_ThrowsFileNotFoundException() + foreach (var keyword in keywords) { - _fixture.GradleProjectPath = Path.GetRandomFileName(); - var sut = _fixture.GetSut(); - - var ex = Assert.Throws(() => sut.RemoveUploadFromGradleFile()); - Assert.AreEqual(GetGradleFilePath(), ex.FileName); + StringAssert.Contains(keyword, actualFileContent); } + } - [Test] - public void RemoveUploadTaskFromGradleFile_UploadHasNotBeenAdded_LogsAndReturns() - { - var sut = _fixture.GetSut(); + [Test] + public void RemoveUploadTaskFromGradleFile_GradleFileDoesNotExist_ThrowsFileNotFoundException() + { + _fixture.GradleProjectPath = Path.GetRandomFileName(); + var sut = _fixture.GetSut(); - sut.RemoveUploadFromGradleFile(); + var ex = Assert.Throws(() => sut.RemoveUploadFromGradleFile()); + Assert.AreEqual(GetGradleFilePath(), ex.FileName); + } - _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "No previous upload task found."); - } + [Test] + public void RemoveUploadTaskFromGradleFile_UploadHasNotBeenAdded_LogsAndReturns() + { + var sut = _fixture.GetSut(); - [Test] - public void RemoveUploadTaskFromGradleFile_UploadHasBeenAdded_RemovesUploadTask() - { - var sut = _fixture.GetSut(); - sut.AppendUploadToGradleFile(_fixture.SentryCliPath); + sut.RemoveUploadFromGradleFile(); - // Sanity check - var actualFileContent = File.ReadAllText(GetGradleFilePath()); - StringAssert.Contains("sentry.properties", actualFileContent); + _fixture.UnityTestLogger.AssertLogContains(SentryLevel.Debug, "No previous upload task found."); + } - sut.RemoveUploadFromGradleFile(); + [Test] + public void RemoveUploadTaskFromGradleFile_UploadHasBeenAdded_RemovesUploadTask() + { + var sut = _fixture.GetSut(); + sut.AppendUploadToGradleFile(_fixture.SentryCliPath); - actualFileContent = File.ReadAllText(GetGradleFilePath()); - StringAssert.DoesNotContain("sentry.properties", actualFileContent); - } + // Sanity check + var actualFileContent = File.ReadAllText(GetGradleFilePath()); + StringAssert.Contains("sentry.properties", actualFileContent); - [Test] - [TestCase("2019.4")] - [TestCase("2020.3")] - [TestCase("2021.1")] - [TestCase("2021.2")] - [TestCase("2022.1")] - public void TryCopySymbolsToGradleProject_CopiesFilesFromBuildOutputToSymbolsDirectory(string unityVersion) - { - _fixture.Application = new TestApplication(unityVersion: unityVersion); - _fixture.IsExporting = true; - var expectedSymbolsPath = Path.Combine(_fixture.GradleProjectPath, "symbols"); - var sut = _fixture.GetSut(); + sut.RemoveUploadFromGradleFile(); - sut.TryCopySymbolsToGradleProject(_fixture.Application); + actualFileContent = File.ReadAllText(GetGradleFilePath()); + StringAssert.DoesNotContain("sentry.properties", actualFileContent); + } - var files = Directory.GetFiles(expectedSymbolsPath, "*.so", SearchOption.AllDirectories).ToList(); - Assert.IsNotNull(files.Find(f => f.EndsWith("libil2cpp.dbg.so"))); - Assert.IsNotNull(files.Find(f => f.EndsWith("libil2cpp.sym.so"))); - Assert.IsNotNull(files.Find(f => f.EndsWith("libunity.sym.so"))); - } + [Test] + [TestCase("2019.4")] + [TestCase("2020.3")] + [TestCase("2021.1")] + [TestCase("2021.2")] + [TestCase("2022.1")] + public void TryCopySymbolsToGradleProject_CopiesFilesFromBuildOutputToSymbolsDirectory(string unityVersion) + { + _fixture.Application = new TestApplication(unityVersion: unityVersion); + _fixture.IsExporting = true; + var expectedSymbolsPath = Path.Combine(_fixture.GradleProjectPath, "symbols"); + var sut = _fixture.GetSut(); - public static void SetupFakeProject(string fakeProjectPath) - { - Directory.CreateDirectory(fakeProjectPath); + sut.TryCopySymbolsToGradleProject(_fixture.Application); - var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var projectTemplatePath = Path.Combine(assemblyPath, "TestFiles", "SymbolsUploadProject"); + var files = Directory.GetFiles(expectedSymbolsPath, "*.so", SearchOption.AllDirectories).ToList(); + Assert.IsNotNull(files.Find(f => f.EndsWith("libil2cpp.dbg.so"))); + Assert.IsNotNull(files.Find(f => f.EndsWith("libil2cpp.sym.so"))); + Assert.IsNotNull(files.Find(f => f.EndsWith("libunity.sym.so"))); + } - foreach (var dirPath in Directory.GetDirectories(projectTemplatePath, "*", SearchOption.AllDirectories)) - { - Directory.CreateDirectory(dirPath.Replace(projectTemplatePath, fakeProjectPath)); - } + public static void SetupFakeProject(string fakeProjectPath) + { + Directory.CreateDirectory(fakeProjectPath); - foreach (var newPath in Directory.GetFiles(projectTemplatePath, "*", SearchOption.AllDirectories)) - { - File.Copy(newPath, newPath.Replace(projectTemplatePath, fakeProjectPath), true); - } + var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var projectTemplatePath = Path.Combine(assemblyPath, "TestFiles", "SymbolsUploadProject"); + + foreach (var dirPath in Directory.GetDirectories(projectTemplatePath, "*", SearchOption.AllDirectories)) + { + Directory.CreateDirectory(dirPath.Replace(projectTemplatePath, fakeProjectPath)); } - private string GetGradleFilePath() => Path.Combine(_fixture.GradleProjectPath, "launcher/build.gradle"); + foreach (var newPath in Directory.GetFiles(projectTemplatePath, "*", SearchOption.AllDirectories)) + { + File.Copy(newPath, newPath.Replace(projectTemplatePath, fakeProjectPath), true); + } } -} + + private string GetGradleFilePath() => Path.Combine(_fixture.GradleProjectPath, "launcher/build.gradle"); +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs b/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs index 802a7bc6a..cf6cf0917 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs @@ -6,104 +6,103 @@ using Sentry.Unity.Editor.Android; using Sentry.Unity.Tests.SharedClasses; -namespace Sentry.Unity.Editor.Tests.Android +namespace Sentry.Unity.Editor.Tests.Android; + +public class GradleSetupTests { - public class GradleSetupTests - { - private TestLogger Logger { get; set; } = new(); - private string FakeProjectPath { get; set; } = null!; - private string UnityProjectPath { get; set; } = null!; - private string GradleProjectPath { get; set; } = null!; + private TestLogger Logger { get; set; } = new(); + private string FakeProjectPath { get; set; } = null!; + private string UnityProjectPath { get; set; } = null!; + private string GradleProjectPath { get; set; } = null!; - [SetUp] - public void SetUp() - { - FakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - UnityProjectPath = Path.Combine(FakeProjectPath, "UnityProject"); - GradleProjectPath = Path.Combine(FakeProjectPath, "GradleProject"); + [SetUp] + public void SetUp() + { + FakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + UnityProjectPath = Path.Combine(FakeProjectPath, "UnityProject"); + GradleProjectPath = Path.Combine(FakeProjectPath, "GradleProject"); - DebugSymbolUploadTests.SetupFakeProject(FakeProjectPath); - } + DebugSymbolUploadTests.SetupFakeProject(FakeProjectPath); + } - [TearDown] - public void TearDown() + [TearDown] + public void TearDown() + { + if (Directory.Exists(FakeProjectPath)) { - if (Directory.Exists(FakeProjectPath)) - { - Directory.Delete(FakeProjectPath, true); - } + Directory.Delete(FakeProjectPath, true); } + } - [Test] - public void LoadGradleScript_FileNotFound_ThrowsFileNotFoundException() - { - var brokenPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Assert.Throws(() => GradleSetup.LoadGradleScript(brokenPath)); - } + [Test] + public void LoadGradleScript_FileNotFound_ThrowsFileNotFoundException() + { + var brokenPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Assert.Throws(() => GradleSetup.LoadGradleScript(brokenPath)); + } - [Test] - public void UpdateGradleProject_ModifiesGradleFiles() - { - var sut = new GradleSetup(Logger, GradleProjectPath); + [Test] + public void UpdateGradleProject_ModifiesGradleFiles() + { + var sut = new GradleSetup(Logger, GradleProjectPath); - sut.UpdateGradleProject(); + sut.UpdateGradleProject(); - var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle"); - var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); - StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); - } + var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle"); + var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); + StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); + } - [Test] - public void UpdateGradleProject_GradleAlreadyModified_LogsAndReturns() - { - var sut = new GradleSetup(Logger, GradleProjectPath); - sut.UpdateGradleProject(); - var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle"); - var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); - StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check + [Test] + public void UpdateGradleProject_GradleAlreadyModified_LogsAndReturns() + { + var sut = new GradleSetup(Logger, GradleProjectPath); + sut.UpdateGradleProject(); + var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle"); + var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); + StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check - sut.UpdateGradleProject(); + sut.UpdateGradleProject(); - unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); - StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check + unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); + StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check - Assert.IsTrue(Logger.Logs.Any(log => - log.logLevel == SentryLevel.Debug && - log.message.Contains(GradleSetup.DependenciesAddedMessage))); - } + Assert.IsTrue(Logger.Logs.Any(log => + log.logLevel == SentryLevel.Debug && + log.message.Contains(GradleSetup.DependenciesAddedMessage))); + } - [Test] - public void ClearGradleProject_GradleFilesModified_RemovesModification() - { - var sut = new GradleSetup(Logger, GradleProjectPath); - sut.UpdateGradleProject(); - var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle"); - var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); - StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check + [Test] + public void ClearGradleProject_GradleFilesModified_RemovesModification() + { + var sut = new GradleSetup(Logger, GradleProjectPath); + sut.UpdateGradleProject(); + var unityLibraryGradleFilePath = Path.Combine(GradleProjectPath, "unityLibrary", "build.gradle"); + var unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); + StringAssert.Contains(GradleSetup.SdkDependencies, unityLibraryGradleContent); // Sanity check - sut.ClearGradleProject(); + sut.ClearGradleProject(); - unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); - StringAssert.DoesNotContain(GradleSetup.SdkDependencies, unityLibraryGradleContent); - } + unityLibraryGradleContent = File.ReadAllText(unityLibraryGradleFilePath); + StringAssert.DoesNotContain(GradleSetup.SdkDependencies, unityLibraryGradleContent); + } - [Test] - [TestCase("InsertIntoScope/build.gradle_test_1")] - [TestCase("InsertIntoScope/build.gradle_test_2")] - public void InsertIntoScope_ResultMatchesExpected(string testCaseFileName) - { - var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt"); - var testCaseExpectedPath = - Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt"); - var expectedGradleContent = File.ReadAllText(testCaseExpectedPath); + [Test] + [TestCase("InsertIntoScope/build.gradle_test_1")] + [TestCase("InsertIntoScope/build.gradle_test_2")] + public void InsertIntoScope_ResultMatchesExpected(string testCaseFileName) + { + var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt"); + var testCaseExpectedPath = + Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt"); + var expectedGradleContent = File.ReadAllText(testCaseExpectedPath); - var gradleContent = File.ReadAllText(testCasePath); - var sut = new GradleSetup(Logger, GradleProjectPath); + var gradleContent = File.ReadAllText(testCasePath); + var sut = new GradleSetup(Logger, GradleProjectPath); - var actualResult = sut.AddSentryToGradle(gradleContent); + var actualResult = sut.AddSentryToGradle(gradleContent); - StringAssert.AreEqualIgnoringCase(actualResult, expectedGradleContent); - } + StringAssert.AreEqualIgnoringCase(actualResult, expectedGradleContent); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs b/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs index 43d60610f..e2eb27a40 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs @@ -5,130 +5,129 @@ using NUnit.Framework; using Sentry.Unity.Editor.Android; -namespace Sentry.Unity.Editor.Tests.Android +namespace Sentry.Unity.Editor.Tests.Android; + +public class ProguardSetupTests { - public class ProguardSetupTests + private string _fakeProjectPath = null!; + private string _gradleProjectPath = null!; + private string _outputPath = null!; + + [SetUp] + public void SetUp() { - private string _fakeProjectPath = null!; - private string _gradleProjectPath = null!; - private string _outputPath = null!; + _fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + _gradleProjectPath = Path.Combine(_fakeProjectPath, "GradleProject"); + _outputPath = Path.Combine(_gradleProjectPath, "unityLibrary"); + DebugSymbolUploadTests.SetupFakeProject(_fakeProjectPath); + } - [SetUp] - public void SetUp() - { - _fakeProjectPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - _gradleProjectPath = Path.Combine(_fakeProjectPath, "GradleProject"); - _outputPath = Path.Combine(_gradleProjectPath, "unityLibrary"); - DebugSymbolUploadTests.SetupFakeProject(_fakeProjectPath); - } + [TearDown] + public void TearDown() => Directory.Delete(_fakeProjectPath, true); + + private ProguardSetup GetSut() => new(new UnityLogger(new SentryOptions()), _gradleProjectPath); - [TearDown] - public void TearDown() => Directory.Delete(_fakeProjectPath, true); + [Test] + [TestCase(false)] + [TestCase(true)] + public void RemovesRuleFile(bool existsBefore) + { + var ruleFile = Path.Combine(_outputPath, ProguardSetup.RuleFileName); - private ProguardSetup GetSut() => new(new UnityLogger(new SentryOptions()), _gradleProjectPath); + var sut = GetSut(); - [Test] - [TestCase(false)] - [TestCase(true)] - public void RemovesRuleFile(bool existsBefore) + if (existsBefore) { - var ruleFile = Path.Combine(_outputPath, ProguardSetup.RuleFileName); + File.WriteAllText(ruleFile, ""); + } + Assert.True(File.Exists(ruleFile) == existsBefore); - var sut = GetSut(); + sut.RemoveFromGradleProject(); - if (existsBefore) - { - File.WriteAllText(ruleFile, ""); - } - Assert.True(File.Exists(ruleFile) == existsBefore); + Assert.False(File.Exists(ruleFile)); + } - sut.RemoveFromGradleProject(); + [Test] + [TestCase(false)] + [TestCase(true)] + public void AddsRuleFile(bool existsBefore) + { + var ruleFile = Path.Combine(_outputPath, ProguardSetup.RuleFileName); - Assert.False(File.Exists(ruleFile)); - } + var sut = GetSut(); - [Test] - [TestCase(false)] - [TestCase(true)] - public void AddsRuleFile(bool existsBefore) + if (existsBefore) { - var ruleFile = Path.Combine(_outputPath, ProguardSetup.RuleFileName); - - var sut = GetSut(); - - if (existsBefore) - { - File.WriteAllText(ruleFile, ""); - } - Assert.True(File.Exists(ruleFile) == existsBefore); + File.WriteAllText(ruleFile, ""); + } + Assert.True(File.Exists(ruleFile) == existsBefore); - sut.AddToGradleProject(); + sut.AddToGradleProject(); - Assert.True(File.Exists(ruleFile)); - Assert.GreaterOrEqual(File.ReadAllText(ruleFile).Length, 1); - } + Assert.True(File.Exists(ruleFile)); + Assert.GreaterOrEqual(File.ReadAllText(ruleFile).Length, 1); + } - [Test] - [TestCase("\n")] - [TestCase("\r\n")] - public void AddsRuleToGradleScript(string lineSeparator) - { - var gradleScript = Path.Combine(_outputPath, "build.gradle"); + [Test] + [TestCase("\n")] + [TestCase("\r\n")] + public void AddsRuleToGradleScript(string lineSeparator) + { + var gradleScript = Path.Combine(_outputPath, "build.gradle"); - // Update the file to have the expected separators for this test case. - File.WriteAllText(gradleScript, Regex.Replace(File.ReadAllText(gradleScript), "\r?\n", lineSeparator)); + // Update the file to have the expected separators for this test case. + File.WriteAllText(gradleScript, Regex.Replace(File.ReadAllText(gradleScript), "\r?\n", lineSeparator)); - // Sanity check that the previous replacement worked. - StringAssert.Contains(lineSeparator, File.ReadAllText(gradleScript)); - Assert.AreEqual(47, Regex.Matches(File.ReadAllText(gradleScript), lineSeparator).Count); + // Sanity check that the previous replacement worked. + StringAssert.Contains(lineSeparator, File.ReadAllText(gradleScript)); + Assert.AreEqual(47, Regex.Matches(File.ReadAllText(gradleScript), lineSeparator).Count); - var sut = GetSut(); + var sut = GetSut(); - var regex = $"consumerProguardFiles [^\r\n]*, '{ProguardSetup.RuleFileName}'"; - StringAssert.DoesNotMatch(regex, File.ReadAllText(gradleScript)); + var regex = $"consumerProguardFiles [^\r\n]*, '{ProguardSetup.RuleFileName}'"; + StringAssert.DoesNotMatch(regex, File.ReadAllText(gradleScript)); - sut.AddToGradleProject(); + sut.AddToGradleProject(); - var newContent = File.ReadAllText(gradleScript); - StringAssert.IsMatch(regex, newContent); + var newContent = File.ReadAllText(gradleScript); + StringAssert.IsMatch(regex, newContent); - // Doesn't add again on the second run. - sut.AddToGradleProject(); - Assert.AreEqual(File.ReadAllText(gradleScript), newContent); - } + // Doesn't add again on the second run. + sut.AddToGradleProject(); + Assert.AreEqual(File.ReadAllText(gradleScript), newContent); + } - [Test] - [TestCase("AddingProguard/build.gradle_test_1")] - [TestCase("AddingProguard/build.gradle_test_2")] - public void AddsRulesToGradleScript_ContainsConsumerProguardRules_MatchesExpectedOutput(string testCaseFileName) - { - var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt"); - var expectedPath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt"); + [Test] + [TestCase("AddingProguard/build.gradle_test_1")] + [TestCase("AddingProguard/build.gradle_test_2")] + public void AddsRulesToGradleScript_ContainsConsumerProguardRules_MatchesExpectedOutput(string testCaseFileName) + { + var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt"); + var expectedPath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt"); - var actualOutputPath = Path.Combine(_outputPath, "build.gradle"); - File.Copy(testCasePath, actualOutputPath, true); + var actualOutputPath = Path.Combine(_outputPath, "build.gradle"); + File.Copy(testCasePath, actualOutputPath, true); - GetSut().AddToGradleProject(); + GetSut().AddToGradleProject(); - StringAssert.AreEqualIgnoringCase(File.ReadAllText(expectedPath), File.ReadAllText(actualOutputPath)); - } + StringAssert.AreEqualIgnoringCase(File.ReadAllText(expectedPath), File.ReadAllText(actualOutputPath)); + } - [Test] - [TestCase("AddingProguard/build.gradle_test_1")] - [TestCase("AddingProguard/build.gradle_test_2")] - public void RemovesRuleFromGradleScript(string testCaseFileName) - { - var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt"); - var expectedPath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt"); + [Test] + [TestCase("AddingProguard/build.gradle_test_1")] + [TestCase("AddingProguard/build.gradle_test_2")] + public void RemovesRuleFromGradleScript(string testCaseFileName) + { + var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var testCasePath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + "_expected.txt"); + var expectedPath = Path.Combine(assemblyPath, "TestFiles", "Android", testCaseFileName + ".txt"); - var actualOutputPath = Path.Combine(_outputPath, "build.gradle"); - File.Copy(testCasePath, actualOutputPath, true); + var actualOutputPath = Path.Combine(_outputPath, "build.gradle"); + File.Copy(testCasePath, actualOutputPath, true); - GetSut().RemoveFromGradleProject(); + GetSut().RemoveFromGradleProject(); - StringAssert.AreEqualIgnoringCase(File.ReadAllText(expectedPath), File.ReadAllText(actualOutputPath)); - } + StringAssert.AreEqualIgnoringCase(File.ReadAllText(expectedPath), File.ReadAllText(actualOutputPath)); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs b/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs index 908798157..19b5bf526 100644 --- a/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs +++ b/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs @@ -5,74 +5,73 @@ using Sentry.Unity.Editor.ConfigurationWindow; using UnityEngine.TestTools; -namespace Sentry.Unity.Editor.Tests +namespace Sentry.Unity.Editor.Tests; + +public sealed class EditorModeTests { - public sealed class EditorModeTests + [Test] + public void ValidateDsn_WrongFormat_CreatesError() { - [Test] - public void ValidateDsn_WrongFormat_CreatesError() - { - LogAssert.ignoreFailingMessages = true; // mandatory + LogAssert.ignoreFailingMessages = true; // mandatory - // arrange - var validationErrors = new List(); + // arrange + var validationErrors = new List(); - // act + // act - // Do the 'act' phase inside 'using', not outside. There is no window 'outside'. - using (var window = SentryTestWindow.Open()) - { - window.OnValidationError += error => validationErrors.Add(error); - window.Options.Dsn = "qwerty"; - } - - // assert - Assert.GreaterOrEqual(validationErrors.Count, 1, "Expected at least 1 error"); - Assert.NotNull(validationErrors.FirstOrDefault(e => e.PropertyName.Contains(nameof(SentryTestWindow.Options.Dsn)))); + // Do the 'act' phase inside 'using', not outside. There is no window 'outside'. + using (var window = SentryTestWindow.Open()) + { + window.OnValidationError += error => validationErrors.Add(error); + window.Options.Dsn = "qwerty"; } - [Test] - public void ValidateDsn_Empty_CreatesNoError() - { - LogAssert.ignoreFailingMessages = true; // mandatory + // assert + Assert.GreaterOrEqual(validationErrors.Count, 1, "Expected at least 1 error"); + Assert.NotNull(validationErrors.FirstOrDefault(e => e.PropertyName.Contains(nameof(SentryTestWindow.Options.Dsn)))); + } - // arrange - var validationErrors = new List(); + [Test] + public void ValidateDsn_Empty_CreatesNoError() + { + LogAssert.ignoreFailingMessages = true; // mandatory - // act + // arrange + var validationErrors = new List(); - // Do the 'act' phase inside 'using', not outside. There is no window 'outside'. - using (var window = SentryTestWindow.Open()) - { - window.OnValidationError += error => validationErrors.Add(error); - window.Options.Dsn = ""; - } + // act - // assert - Assert.AreEqual(0, validationErrors.Count); + // Do the 'act' phase inside 'using', not outside. There is no window 'outside'. + using (var window = SentryTestWindow.Open()) + { + window.OnValidationError += error => validationErrors.Add(error); + window.Options.Dsn = ""; } - [Test] - public void ValidateDsn_CorrectFormat_CreatesNoError() - { - LogAssert.ignoreFailingMessages = true; // mandatory + // assert + Assert.AreEqual(0, validationErrors.Count); + } - // arrange - var validationErrors = new List(); + [Test] + public void ValidateDsn_CorrectFormat_CreatesNoError() + { + LogAssert.ignoreFailingMessages = true; // mandatory - // act + // arrange + var validationErrors = new List(); - // Do the 'act' phase inside 'using', not outside. There is no window 'outside'. - using (var window = SentryTestWindow.Open()) - { - window.OnValidationError += error => validationErrors.Add(error); + // act - Uri.TryCreate("https://sentryTest.io", UriKind.Absolute, out Uri testUri); - window.Options.Dsn = testUri.ToString(); - } + // Do the 'act' phase inside 'using', not outside. There is no window 'outside'. + using (var window = SentryTestWindow.Open()) + { + window.OnValidationError += error => validationErrors.Add(error); - // assert - Assert.AreEqual(0, validationErrors.Count); + Uri.TryCreate("https://sentryTest.io", UriKind.Absolute, out Uri testUri); + window.Options.Dsn = testUri.ToString(); } + + // assert + Assert.AreEqual(0, validationErrors.Count); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs b/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs index 583368091..bdaaa1664 100644 --- a/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs +++ b/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs @@ -1,18 +1,17 @@ using NUnit.Framework; using Sentry.Unity.Editor.ConfigurationWindow; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class EmbeddedResourcesTests { - public sealed class EmbeddedResourcesTests + [Test] + public void Resources_Embedded() { - [Test] - public void Resources_Embedded() - { - var resourceNames = typeof(SentryWindow).Assembly.GetManifestResourceNames(); + var resourceNames = typeof(SentryWindow).Assembly.GetManifestResourceNames(); - Assert.NotNull(resourceNames); - Assert.Contains("Sentry.Unity.Editor.Resources.SentryLogoLight.png", resourceNames); - Assert.Contains("Sentry.Unity.Editor.Resources.SentryLogoDark.png", resourceNames); - } + Assert.NotNull(resourceNames); + Assert.Contains("Sentry.Unity.Editor.Resources.SentryLogoLight.png", resourceNames); + Assert.Contains("Sentry.Unity.Editor.Resources.SentryLogoDark.png", resourceNames); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index 06d7dade8..738b3f0fc 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -1,87 +1,85 @@ using System; -using System.Linq; using NUnit.Framework; -namespace Sentry.Unity.Editor.Tests -{ - public class Il2CppBuildPreProcessTests - { - private string arguments = null!; - private string resultingArguments = null!; +namespace Sentry.Unity.Editor.Tests; - [SetUp] - public void Setup() - { - arguments = string.Empty; - resultingArguments = string.Empty; - } +public class Il2CppBuildPreProcessTests +{ + private string arguments = null!; + private string resultingArguments = null!; - [Test] - public void SetAdditionalArguments_Il2CppEnabled_AddsArguments() - { - var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; + [SetUp] + public void Setup() + { + arguments = string.Empty; + resultingArguments = string.Empty; + } - Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + [Test] + public void SetAdditionalArguments_Il2CppEnabled_AddsArguments() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; - Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); - } + Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); - [Test] - public void SetAdditionalArguments_Il2CppDisabled_FoesNotAddArguments() - { - var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = false }; + Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); + } - Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + [Test] + public void SetAdditionalArguments_Il2CppDisabled_FoesNotAddArguments() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = false }; - Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); - } + Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); - [Test] - public void SetAdditionalArguments_Il2CppEnabled_ExistingArgumentsDoNotGetOverwritten() - { - var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; - var expectedArgument = "--MyArgument"; - arguments = expectedArgument; + Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); + } - Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + [Test] + public void SetAdditionalArguments_Il2CppEnabled_ExistingArgumentsDoNotGetOverwritten() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; + var expectedArgument = "--MyArgument"; + arguments = expectedArgument; - Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); // sanity check - Assert.That(resultingArguments, Does.Contain(expectedArgument)); - } + Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); - [Test] - public void SetAdditionalArguments_Il2CppEnabledAndArgumentAlreadyAdded_AddsArgumentsOnlyOnce() - { - var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; + Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); // sanity check + Assert.That(resultingArguments, Does.Contain(expectedArgument)); + } - Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); - Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); // sanity check + [Test] + public void SetAdditionalArguments_Il2CppEnabledAndArgumentAlreadyAdded_AddsArgumentsOnlyOnce() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = true }; - Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); // sanity check - Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); // sanity check - var occurrences = 0; - var i = 0; - while ((i = resultingArguments.IndexOf(Il2CppBuildPreProcess.SourceMappingArgument, i, StringComparison.Ordinal)) != -1) - { - i += Il2CppBuildPreProcess.SourceMappingArgument.Length; - occurrences++; - } + Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); - Assert.AreEqual(1, occurrences); + Assert.That(resultingArguments, Does.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); // sanity check + var occurrences = 0; + var i = 0; + while ((i = resultingArguments.IndexOf(Il2CppBuildPreProcess.SourceMappingArgument, i, StringComparison.Ordinal)) != -1) + { + i += Il2CppBuildPreProcess.SourceMappingArgument.Length; + occurrences++; } - [Test] - public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_RemovesArguments() - { - var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = false }; - var expectedArgument = "--MyArgument"; - arguments = $"{expectedArgument} {Il2CppBuildPreProcess.SourceMappingArgument}"; + Assert.AreEqual(1, occurrences); + } - Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + [Test] + public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_RemovesArguments() + { + var options = new SentryUnityOptions { Il2CppLineNumberSupportEnabled = false }; + var expectedArgument = "--MyArgument"; + arguments = $"{expectedArgument} {Il2CppBuildPreProcess.SourceMappingArgument}"; - Assert.That(resultingArguments, Does.Contain(expectedArgument)); - Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); - } + Il2CppBuildPreProcess.SetAdditionalIl2CppArguments(options, () => arguments, s => resultingArguments = s); + + Assert.That(resultingArguments, Does.Contain(expectedArgument)); + Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs b/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs index f65f8f941..640137a6b 100644 --- a/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs @@ -3,57 +3,56 @@ using UnityEditor; using UnityEngine; -namespace Sentry.Unity.Editor.Tests +namespace Sentry.Unity.Editor.Tests; + +public class ScriptableSentryUnityOptionsTests { - public class ScriptableSentryUnityOptionsTests + [Test] + public void ScriptableSentryUnityOptions_Creation_AllPropertiesPresent() { - [Test] - public void ScriptableSentryUnityOptions_Creation_AllPropertiesPresent() - { - const string testOptionsPath = "Assets/TestOptions.asset"; + const string testOptionsPath = "Assets/TestOptions.asset"; - var scriptableOptions = ScriptableObject.CreateInstance(); - AssetDatabase.CreateAsset(scriptableOptions, testOptionsPath); - AssetDatabase.SaveAssets(); + var scriptableOptions = ScriptableObject.CreateInstance(); + AssetDatabase.CreateAsset(scriptableOptions, testOptionsPath); + AssetDatabase.SaveAssets(); - var optionsAsString = File.ReadAllText(testOptionsPath); + var optionsAsString = File.ReadAllText(testOptionsPath); - StringAssert.Contains("Enabled", optionsAsString); - StringAssert.Contains("Dsn", optionsAsString); - StringAssert.Contains("CaptureInEditor", optionsAsString); - StringAssert.Contains("EnableLogDebouncing", optionsAsString); - StringAssert.Contains("TracesSampleRate", optionsAsString); - StringAssert.Contains("AutoSessionTracking", optionsAsString); - StringAssert.Contains("AutoSessionTrackingInterval", optionsAsString); - StringAssert.Contains("ReleaseOverride", optionsAsString); - StringAssert.Contains("EnvironmentOverride", optionsAsString); - StringAssert.Contains("AttachStacktrace", optionsAsString); - StringAssert.Contains("AttachScreenshot", optionsAsString); - StringAssert.Contains("ScreenshotQuality", optionsAsString); - StringAssert.Contains("ScreenshotCompression", optionsAsString); - StringAssert.Contains("MaxBreadcrumbs", optionsAsString); - StringAssert.Contains("ReportAssembliesMode", optionsAsString); - StringAssert.Contains("SendDefaultPii", optionsAsString); - StringAssert.Contains("IsEnvironmentUser", optionsAsString); - StringAssert.Contains("EnableOfflineCaching", optionsAsString); - StringAssert.Contains("MaxCacheItems", optionsAsString); - StringAssert.Contains("InitCacheFlushTimeout", optionsAsString); - StringAssert.Contains("SampleRate", optionsAsString); - StringAssert.Contains("ShutdownTimeout", optionsAsString); - StringAssert.Contains("MaxQueueItems", optionsAsString); - StringAssert.Contains("IosNativeSupportEnabled", optionsAsString); - StringAssert.Contains("AndroidNativeSupportEnabled", optionsAsString); - StringAssert.Contains("WindowsNativeSupportEnabled", optionsAsString); - StringAssert.Contains("MacosNativeSupportEnabled", optionsAsString); - StringAssert.Contains("LinuxNativeSupportEnabled", optionsAsString); - StringAssert.Contains("Il2CppLineNumberSupportEnabled", optionsAsString); - StringAssert.Contains("OptionsConfiguration", optionsAsString); - StringAssert.Contains("Debug", optionsAsString); - StringAssert.Contains("DebugOnlyInEditor", optionsAsString); - StringAssert.Contains("DiagnosticLevel", optionsAsString); + StringAssert.Contains("Enabled", optionsAsString); + StringAssert.Contains("Dsn", optionsAsString); + StringAssert.Contains("CaptureInEditor", optionsAsString); + StringAssert.Contains("EnableLogDebouncing", optionsAsString); + StringAssert.Contains("TracesSampleRate", optionsAsString); + StringAssert.Contains("AutoSessionTracking", optionsAsString); + StringAssert.Contains("AutoSessionTrackingInterval", optionsAsString); + StringAssert.Contains("ReleaseOverride", optionsAsString); + StringAssert.Contains("EnvironmentOverride", optionsAsString); + StringAssert.Contains("AttachStacktrace", optionsAsString); + StringAssert.Contains("AttachScreenshot", optionsAsString); + StringAssert.Contains("ScreenshotQuality", optionsAsString); + StringAssert.Contains("ScreenshotCompression", optionsAsString); + StringAssert.Contains("MaxBreadcrumbs", optionsAsString); + StringAssert.Contains("ReportAssembliesMode", optionsAsString); + StringAssert.Contains("SendDefaultPii", optionsAsString); + StringAssert.Contains("IsEnvironmentUser", optionsAsString); + StringAssert.Contains("EnableOfflineCaching", optionsAsString); + StringAssert.Contains("MaxCacheItems", optionsAsString); + StringAssert.Contains("InitCacheFlushTimeout", optionsAsString); + StringAssert.Contains("SampleRate", optionsAsString); + StringAssert.Contains("ShutdownTimeout", optionsAsString); + StringAssert.Contains("MaxQueueItems", optionsAsString); + StringAssert.Contains("IosNativeSupportEnabled", optionsAsString); + StringAssert.Contains("AndroidNativeSupportEnabled", optionsAsString); + StringAssert.Contains("WindowsNativeSupportEnabled", optionsAsString); + StringAssert.Contains("MacosNativeSupportEnabled", optionsAsString); + StringAssert.Contains("LinuxNativeSupportEnabled", optionsAsString); + StringAssert.Contains("Il2CppLineNumberSupportEnabled", optionsAsString); + StringAssert.Contains("OptionsConfiguration", optionsAsString); + StringAssert.Contains("Debug", optionsAsString); + StringAssert.Contains("DebugOnlyInEditor", optionsAsString); + StringAssert.Contains("DiagnosticLevel", optionsAsString); - AssetDatabase.DeleteAsset(testOptionsPath); - AssetDatabase.Refresh(); - } + AssetDatabase.DeleteAsset(testOptionsPath); + AssetDatabase.Refresh(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs b/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs index b77a7410d..a93ecc982 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs @@ -1,143 +1,140 @@ using System; using System.IO; using NUnit.Framework; -using Sentry.Unity.Tests.SharedClasses; -using Sentry.Unity.Tests.Stubs; using UnityEngine; -namespace Sentry.Unity.Editor.Tests +namespace Sentry.Unity.Editor.Tests; + +public class SentryCliTests { - public class SentryCliTests + [Test] + public void GetSentryCliPlatformExecutable_UnrecognizedPlatform_ThrowsInvalidOperationException() { - [Test] - public void GetSentryCliPlatformExecutable_UnrecognizedPlatform_ThrowsInvalidOperationException() - { - Assert.Throws(() => SentryCli.GetSentryCliPlatformExecutable(RuntimePlatform.LinuxPlayer)); - } + Assert.Throws(() => SentryCli.GetSentryCliPlatformExecutable(RuntimePlatform.LinuxPlayer)); + } - [Test] - [TestCase(RuntimePlatform.WindowsEditor, SentryCli.SentryCliWindows)] - [TestCase(RuntimePlatform.OSXEditor, SentryCli.SentryCliMacOS)] - [TestCase(RuntimePlatform.LinuxEditor, SentryCli.SentryCliLinux)] - public void GetSentryPlatformName_RecognizedPlatform_SetsSentryCliName(RuntimePlatform platform, string expectedName) - { - var actualName = SentryCli.GetSentryCliPlatformExecutable(platform); + [Test] + [TestCase(RuntimePlatform.WindowsEditor, SentryCli.SentryCliWindows)] + [TestCase(RuntimePlatform.OSXEditor, SentryCli.SentryCliMacOS)] + [TestCase(RuntimePlatform.LinuxEditor, SentryCli.SentryCliLinux)] + public void GetSentryPlatformName_RecognizedPlatform_SetsSentryCliName(RuntimePlatform platform, string expectedName) + { + var actualName = SentryCli.GetSentryCliPlatformExecutable(platform); - Assert.AreEqual(expectedName, actualName); - } + Assert.AreEqual(expectedName, actualName); + } - [Test] - public void GetSentryCliPath_InvalidFileName_ThrowsFileNotFoundException() - { - Assert.Throws(() => SentryCli.GetSentryCliPath("InvalidName")); - } + [Test] + public void GetSentryCliPath_InvalidFileName_ThrowsFileNotFoundException() + { + Assert.Throws(() => SentryCli.GetSentryCliPath("InvalidName")); + } - [Test] - public void GetSentryCliPath_ValidFileName_ReturnsPath() - { - var sentryCliPlatformName = SentryCli.GetSentryCliPlatformExecutable(Application.platform); - var expectedPath = Path.GetFullPath( - Path.Combine("Packages", SentryPackageInfo.GetName(), "Editor", "sentry-cli", sentryCliPlatformName)); + [Test] + public void GetSentryCliPath_ValidFileName_ReturnsPath() + { + var sentryCliPlatformName = SentryCli.GetSentryCliPlatformExecutable(Application.platform); + var expectedPath = Path.GetFullPath( + Path.Combine("Packages", SentryPackageInfo.GetName(), "Editor", "sentry-cli", sentryCliPlatformName)); - var actualPath = SentryCli.GetSentryCliPath(sentryCliPlatformName); + var actualPath = SentryCli.GetSentryCliPath(sentryCliPlatformName); - Assert.AreEqual(expectedPath, actualPath); - } + Assert.AreEqual(expectedPath, actualPath); + } - [Test] - public void SetExecutePermission_FileDoesNotExist_ThrowsUnauthorizedAccessException() + [Test] + public void SetExecutePermission_FileDoesNotExist_ThrowsUnauthorizedAccessException() + { + if (Application.platform == RuntimePlatform.WindowsEditor) { - if (Application.platform == RuntimePlatform.WindowsEditor) - { - Assert.Inconclusive("Skipping chmod on Windows."); - return; - } - - Assert.Throws(() => SentryCli.SetExecutePermission("non-existent-file")); + Assert.Inconclusive("Skipping chmod on Windows."); + return; } - [Test] - [TestCase("")] - [TestCase("urlOverride")] - public void CreateSentryProperties_PropertyFileCreatedAndContainsSentryCliOptions(string urlOverride) - { - var propertiesDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(propertiesDirectory); + Assert.Throws(() => SentryCli.SetExecutePermission("non-existent-file")); + } - var sentryCliTestOptions = ScriptableObject.CreateInstance(); - sentryCliTestOptions.Auth = Guid.NewGuid().ToString(); - sentryCliTestOptions.Organization = Guid.NewGuid().ToString(); - sentryCliTestOptions.Project = Guid.NewGuid().ToString(); - sentryCliTestOptions.UrlOverride = urlOverride; + [Test] + [TestCase("")] + [TestCase("urlOverride")] + public void CreateSentryProperties_PropertyFileCreatedAndContainsSentryCliOptions(string urlOverride) + { + var propertiesDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(propertiesDirectory); - SentryCli.CreateSentryProperties(propertiesDirectory, sentryCliTestOptions, new()); + var sentryCliTestOptions = ScriptableObject.CreateInstance(); + sentryCliTestOptions.Auth = Guid.NewGuid().ToString(); + sentryCliTestOptions.Organization = Guid.NewGuid().ToString(); + sentryCliTestOptions.Project = Guid.NewGuid().ToString(); + sentryCliTestOptions.UrlOverride = urlOverride; - var properties = File.ReadAllText(Path.Combine(propertiesDirectory, "sentry.properties")); + SentryCli.CreateSentryProperties(propertiesDirectory, sentryCliTestOptions, new()); - StringAssert.Contains(sentryCliTestOptions.Auth, properties); - StringAssert.Contains(sentryCliTestOptions.Organization, properties); - StringAssert.Contains(sentryCliTestOptions.Project, properties); + var properties = File.ReadAllText(Path.Combine(propertiesDirectory, "sentry.properties")); - if (!string.IsNullOrEmpty(sentryCliTestOptions.UrlOverride)) - { - StringAssert.Contains(urlOverride, properties); - } + StringAssert.Contains(sentryCliTestOptions.Auth, properties); + StringAssert.Contains(sentryCliTestOptions.Organization, properties); + StringAssert.Contains(sentryCliTestOptions.Project, properties); - Directory.Delete(propertiesDirectory, true); - } - - [Test] - public void SetupSentryCli_WithoutArgs_ReturnsValidCliPath() + if (!string.IsNullOrEmpty(sentryCliTestOptions.UrlOverride)) { - var returnedPath = SentryCli.SetupSentryCli(); - Assert.IsTrue(File.Exists(returnedPath)); + StringAssert.Contains(urlOverride, properties); } - [Test] - public void SetupSentryCli_WithCustomBuildHost_ReturnsValidCliPath() - { - var returnedPath = SentryCli.SetupSentryCli(null, RuntimePlatform.OSXEditor); - var expectedExeName = SentryCli.GetSentryCliPlatformExecutable(RuntimePlatform.OSXEditor); + Directory.Delete(propertiesDirectory, true); + } - Assert.IsTrue(File.Exists(returnedPath)); - Assert.AreEqual(expectedExeName, Path.GetFileName(returnedPath)); - } + [Test] + public void SetupSentryCli_WithoutArgs_ReturnsValidCliPath() + { + var returnedPath = SentryCli.SetupSentryCli(); + Assert.IsTrue(File.Exists(returnedPath)); + } - [Test] - public void SetupSentryCli_ProjectPathDoesNotExist_ThrowsDirectoryNotFoundException() - { - Assert.Throws(() => SentryCli.SetupSentryCli("non-existent-path")); - } + [Test] + public void SetupSentryCli_WithCustomBuildHost_ReturnsValidCliPath() + { + var returnedPath = SentryCli.SetupSentryCli(null, RuntimePlatform.OSXEditor); + var expectedExeName = SentryCli.GetSentryCliPlatformExecutable(RuntimePlatform.OSXEditor); - [Test] - public void SetupSentryCli_ProjectPathExists_CopiesSentryCli() - { - var fakeProjectDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - Directory.CreateDirectory(fakeProjectDirectory); + Assert.IsTrue(File.Exists(returnedPath)); + Assert.AreEqual(expectedExeName, Path.GetFileName(returnedPath)); + } - var returnedPath = SentryCli.SetupSentryCli(fakeProjectDirectory); - var expectedPath = Path.Combine(fakeProjectDirectory, SentryCli.GetSentryCliPlatformExecutable(Application.platform)); + [Test] + public void SetupSentryCli_ProjectPathDoesNotExist_ThrowsDirectoryNotFoundException() + { + Assert.Throws(() => SentryCli.SetupSentryCli("non-existent-path")); + } - Assert.AreEqual(expectedPath, returnedPath); - Assert.IsTrue(File.Exists(returnedPath)); + [Test] + public void SetupSentryCli_ProjectPathExists_CopiesSentryCli() + { + var fakeProjectDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + Directory.CreateDirectory(fakeProjectDirectory); - Directory.Delete(fakeProjectDirectory, true); - } + var returnedPath = SentryCli.SetupSentryCli(fakeProjectDirectory); + var expectedPath = Path.Combine(fakeProjectDirectory, SentryCli.GetSentryCliPlatformExecutable(Application.platform)); - [Test] - public void UrlOverride() - { - Assert.IsNull(SentryCli.UrlOverride(null, null)); - Assert.IsNull(SentryCli.UrlOverride("", null)); - Assert.IsNull(SentryCli.UrlOverride(null, "")); - Assert.IsNull(SentryCli.UrlOverride("", "")); - Assert.IsNull(SentryCli.UrlOverride("https://key@o447951.ingest.sentry.io/5439417", null)); - Assert.IsNull(SentryCli.UrlOverride("https://foo.sentry.io/5439417", null)); - Assert.IsNull(SentryCli.UrlOverride("http://sentry.io", null)); - Assert.AreEqual("http://127.0.0.1:8000", SentryCli.UrlOverride("http://key@127.0.0.1:8000/12345", null)); - Assert.AreEqual("pass-through", SentryCli.UrlOverride("http://key@127.0.0.1:8000/12345", "pass-through")); - Assert.AreEqual("https://example.com", SentryCli.UrlOverride("https://key@example.com/12345", null)); - Assert.AreEqual("http://localhost:8000", SentryCli.UrlOverride("http://key@localhost:8000/12345", null)); - } + Assert.AreEqual(expectedPath, returnedPath); + Assert.IsTrue(File.Exists(returnedPath)); + + Directory.Delete(fakeProjectDirectory, true); + } + + [Test] + public void UrlOverride() + { + Assert.IsNull(SentryCli.UrlOverride(null, null)); + Assert.IsNull(SentryCli.UrlOverride("", null)); + Assert.IsNull(SentryCli.UrlOverride(null, "")); + Assert.IsNull(SentryCli.UrlOverride("", "")); + Assert.IsNull(SentryCli.UrlOverride("https://key@o447951.ingest.sentry.io/5439417", null)); + Assert.IsNull(SentryCli.UrlOverride("https://foo.sentry.io/5439417", null)); + Assert.IsNull(SentryCli.UrlOverride("http://sentry.io", null)); + Assert.AreEqual("http://127.0.0.1:8000", SentryCli.UrlOverride("http://key@127.0.0.1:8000/12345", null)); + Assert.AreEqual("pass-through", SentryCli.UrlOverride("http://key@127.0.0.1:8000/12345", "pass-through")); + Assert.AreEqual("https://example.com", SentryCli.UrlOverride("https://key@example.com/12345", null)); + Assert.AreEqual("http://localhost:8000", SentryCli.UrlOverride("http://key@localhost:8000/12345", null)); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs b/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs index 3785ff283..fe827085e 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs @@ -3,78 +3,77 @@ using NUnit.Framework; using UnityEditor; -namespace Sentry.Unity.Editor.Tests +namespace Sentry.Unity.Editor.Tests; + +public class SentryScriptableObjectTests { - public class SentryScriptableObjectTests - { - private string _tempPath = null!; // Assigned in Setup + private string _tempPath = null!; // Assigned in Setup - [SetUp] - public void Setup() => _tempPath = Path.Combine("Assets", Guid.NewGuid().ToString(), "TestOptions.asset"); + [SetUp] + public void Setup() => _tempPath = Path.Combine("Assets", Guid.NewGuid().ToString(), "TestOptions.asset"); - [TearDown] - public void TearDown() => AssetDatabase.DeleteAsset(Path.GetDirectoryName(_tempPath)); + [TearDown] + public void TearDown() => AssetDatabase.DeleteAsset(Path.GetDirectoryName(_tempPath)); - [Test] - public void CreateOrLoad_ScriptableSentryUnityOptionsAssetDoesNotExist_CreatesNewOptionsAsset() - { - Assert.IsFalse(File.Exists(_tempPath)); // Sanity check + [Test] + public void CreateOrLoad_ScriptableSentryUnityOptionsAssetDoesNotExist_CreatesNewOptionsAsset() + { + Assert.IsFalse(File.Exists(_tempPath)); // Sanity check - SentryScriptableObject.CreateOrLoad(_tempPath); + SentryScriptableObject.CreateOrLoad(_tempPath); - Assert.IsTrue(File.Exists(_tempPath)); - } + Assert.IsTrue(File.Exists(_tempPath)); + } - [Test] - public void CreateOrLoad_SentryCliOptionsAssetDoesNotExist_CreatesNewOptionsAsset() - { - Assert.IsFalse(File.Exists(_tempPath)); // Sanity check + [Test] + public void CreateOrLoad_SentryCliOptionsAssetDoesNotExist_CreatesNewOptionsAsset() + { + Assert.IsFalse(File.Exists(_tempPath)); // Sanity check - SentryScriptableObject.CreateOrLoad(_tempPath); + SentryScriptableObject.CreateOrLoad(_tempPath); - Assert.IsTrue(File.Exists(_tempPath)); - } + Assert.IsTrue(File.Exists(_tempPath)); + } - [Test] - public void Load_OptionsAssetDoesNotExist_ReturnsNull() - { - Assert.IsFalse(File.Exists(_tempPath)); // Sanity check + [Test] + public void Load_OptionsAssetDoesNotExist_ReturnsNull() + { + Assert.IsFalse(File.Exists(_tempPath)); // Sanity check - var options = SentryScriptableObject.Load(_tempPath); + var options = SentryScriptableObject.Load(_tempPath); - Assert.IsNull(options); - } + Assert.IsNull(options); + } - [Test] - public void Load_ScriptableSentryUnityOptionsExist_LoadsSavedOptionsAsset() - { - var expectedDsn = "test_dsn"; - var options = SentryScriptableObject.CreateOrLoad(_tempPath); - options.Dsn = expectedDsn; - AssetDatabase.SaveAssets(); // Saving to disk + [Test] + public void Load_ScriptableSentryUnityOptionsExist_LoadsSavedOptionsAsset() + { + var expectedDsn = "test_dsn"; + var options = SentryScriptableObject.CreateOrLoad(_tempPath); + options.Dsn = expectedDsn; + AssetDatabase.SaveAssets(); // Saving to disk - Assert.IsTrue(File.Exists(_tempPath)); // Sanity check + Assert.IsTrue(File.Exists(_tempPath)); // Sanity check - var actualOptions = SentryScriptableObject.Load(_tempPath); + var actualOptions = SentryScriptableObject.Load(_tempPath); - Assert.NotNull(actualOptions); - Assert.AreEqual(expectedDsn, actualOptions!.Dsn); - } + Assert.NotNull(actualOptions); + Assert.AreEqual(expectedDsn, actualOptions!.Dsn); + } - [Test] - public void Load_SentryCliOptionsExist_LoadsSavedOptionsAsset() - { - var expectedAuth = "test_auth"; - var options = SentryScriptableObject.CreateOrLoad(_tempPath); - options.Auth = expectedAuth; - AssetDatabase.SaveAssets(); // Saving to disk + [Test] + public void Load_SentryCliOptionsExist_LoadsSavedOptionsAsset() + { + var expectedAuth = "test_auth"; + var options = SentryScriptableObject.CreateOrLoad(_tempPath); + options.Auth = expectedAuth; + AssetDatabase.SaveAssets(); // Saving to disk - Assert.IsTrue(File.Exists(_tempPath)); // Sanity check + Assert.IsTrue(File.Exists(_tempPath)); // Sanity check - var actualOptions = SentryScriptableObject.Load(_tempPath); + var actualOptions = SentryScriptableObject.Load(_tempPath); - Assert.NotNull(actualOptions); - Assert.AreEqual(expectedAuth, actualOptions!.Auth); - } + Assert.NotNull(actualOptions); + Assert.AreEqual(expectedAuth, actualOptions!.Auth); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs index 5f3a4e145..f075836d7 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs @@ -2,38 +2,37 @@ using NUnit.Framework; using Sentry.Unity.Tests.Stubs; -namespace Sentry.Unity.Editor.Tests +namespace Sentry.Unity.Editor.Tests; + +public class SentryUnityVersionTests { - public class SentryUnityVersionTests + [Test] + [TestCase("2019.4.39f1", "2019.4.39")] + [TestCase("2021.1.1b1", "2021.1.1")] + [TestCase("2022.1.0a17", "2022.1.0")] + [TestCase("2022.1.0", "2022.1.0")] + public void GetUnityVersion_WellFormedVersion_ReturnsTrimmedVersion(string unityVersion, string expectedUnityVersion) { - [Test] - [TestCase("2019.4.39f1", "2019.4.39")] - [TestCase("2021.1.1b1", "2021.1.1")] - [TestCase("2022.1.0a17", "2022.1.0")] - [TestCase("2022.1.0", "2022.1.0")] - public void GetUnityVersion_WellFormedVersion_ReturnsTrimmedVersion(string unityVersion, string expectedUnityVersion) - { - var application = new TestApplication(unityVersion: unityVersion); - var expectedVersion = new Version(expectedUnityVersion); + var application = new TestApplication(unityVersion: unityVersion); + var expectedVersion = new Version(expectedUnityVersion); - var actualVersion = SentryUnityVersion.GetVersion(application); + var actualVersion = SentryUnityVersion.GetVersion(application); - Assert.AreEqual(expectedVersion, actualVersion); - } + Assert.AreEqual(expectedVersion, actualVersion); + } - [Test] - [TestCase("2019.4.39f1", "2019.4.39", true)] // are equal - [TestCase("2020.1.1f1", "2022.1.2", false)] // is older - [TestCase("2020.1.1f1", "2022.2", false)] // is older - [TestCase("2020.1.1f1", "2019.4", true)] // is newer - [TestCase("2021.1.1f1", "2020.3", true)] // is newer - public void IsNewerOrEqual(string currentUnityVersion, string versionToCheck, bool expected) - { - var application = new TestApplication(unityVersion: currentUnityVersion); + [Test] + [TestCase("2019.4.39f1", "2019.4.39", true)] // are equal + [TestCase("2020.1.1f1", "2022.1.2", false)] // is older + [TestCase("2020.1.1f1", "2022.2", false)] // is older + [TestCase("2020.1.1f1", "2019.4", true)] // is newer + [TestCase("2021.1.1f1", "2020.3", true)] // is newer + public void IsNewerOrEqual(string currentUnityVersion, string versionToCheck, bool expected) + { + var application = new TestApplication(unityVersion: currentUnityVersion); - var actual = SentryUnityVersion.IsNewerOrEqualThan(versionToCheck, application); + var actual = SentryUnityVersion.IsNewerOrEqualThan(versionToCheck, application); - Assert.AreEqual(expected, actual); - } + Assert.AreEqual(expected, actual); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.Tests/WizardTests.cs b/test/Sentry.Unity.Editor.Tests/WizardTests.cs index e4212782d..8cf74ba60 100644 --- a/test/Sentry.Unity.Editor.Tests/WizardTests.cs +++ b/test/Sentry.Unity.Editor.Tests/WizardTests.cs @@ -1,58 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using Sentry.Unity.Editor.ConfigurationWindow; using Sentry.Unity.Editor.WizardApi; -using UnityEngine.TestTools; using Sentry.Unity.Tests.SharedClasses; -namespace Sentry.Unity.Editor.Tests +namespace Sentry.Unity.Editor.Tests; + +public sealed class WizardJson { - public sealed class WizardJson + [Test] + public void Step1Response() + { + var sut = new WizardLoader(new TestLogger()); + + var parsed = sut.DeserializeJson("{\"hash\":\"foo\"}"); + + Assert.AreEqual("foo", parsed.hash); + } + + [Test] + public void Step2Response() { - [Test] - public void Step1Response() - { - var sut = new WizardLoader(new TestLogger()); - - var parsed = sut.DeserializeJson("{\"hash\":\"foo\"}"); - - Assert.AreEqual("foo", parsed.hash); - } - - [Test] - public void Step2Response() - { - var sut = new WizardLoader(new TestLogger()); - - var json = "{\"apiKeys\":{\"id\":\"key-1\",\"scopes\":[\"org:read\",\"project:read\",\"project:releases\",\"project:write\"],\"application\":null,\"expiresAt\":null,\"dateCreated\":\"2022-03-02T10:37:56.385524Z\",\"state\":null,\"token\":\"api-key-token\",\"refreshToken\":null},\"projects\":[{\"id\":\"project-1\",\"slug\":\"project-slug\",\"name\":\"personal\",\"isPublic\":false,\"isBookmarked\":false,\"color\":\"#3fb7bf\",\"dateCreated\":\"2022-01-15T20:05:53.883628Z\",\"firstEvent\":\"2022-01-15T20:15:10.171648Z\",\"firstTransactionEvent\":false,\"hasSessions\":true,\"features\":[\"alert-filters\",\"issue-alerts-targeting\",\"minidump\",\"performance-suspect-spans-ingestion\",\"race-free-group-creation\",\"similarity-indexing\",\"similarity-view\",\"releases\"],\"status\":\"active\",\"platform\":\"flutter\",\"isInternal\":false,\"isMember\":false,\"hasAccess\":true,\"avatar\":{\"avatarType\":\"letter_avatar\",\"avatarUuid\":null},\"organization\":{\"id\":\"org-1\",\"slug\":\"org-slug\",\"status\":{\"id\":\"active\",\"name\":\"active\"},\"name\":\"organization-1\",\"dateCreated\":\"2022-01-15T20:03:49.620687Z\",\"isEarlyAdopter\":false,\"require2FA\":false,\"requireEmailVerification\":false,\"avatar\":{\"avatarType\":\"letter_avatar\",\"avatarUuid\":null},\"features\":[\"onboarding\",\"ondemand-budgets\",\"slack-overage-notifications\",\"dashboards-template\",\"discover-frontend-use-events-endpoint\",\"integrations-stacktrace-link\",\"crash-rate-alerts\",\"org-subdomains\",\"performance-dry-run-mep\",\"mobile-app\",\"custom-event-title\",\"advanced-search\",\"widget-library\",\"auto-start-free-trial\",\"release-health-return-metrics\",\"minute-resolution-sessions\",\"capture-lead\",\"invite-members-rate-limits\",\"alert-crash-free-metrics\",\"alert-wizard-v3\",\"images-loaded-v2\",\"duplicate-alert-rule\",\"performance-autogroup-sibling-spans\",\"performance-ops-breakdown\",\"new-widget-builder-experience-design\",\"performance-suspect-spans-view\",\"unified-span-view\",\"performance-frontend-use-events-endpoint\",\"widget-viewer-modal\",\"event-attachments\",\"symbol-sources\",\"performance-span-histogram-view\",\"intl-sales-tax\",\"metrics-extraction\",\"performance-view\",\"new-weekly-report\",\"performance-span-tree-autoscroll\",\"metric-alert-snql\",\"shared-issues\",\"dashboard-grid-layout\",\"open-membership\"]},\"keys\":[{\"id\":\"key-1\",\"name\":\"Default\",\"label\":\"Default\",\"public\":\"public-key\",\"secret\":\"secret-key\",\"projectId\":12345,\"isActive\":true,\"rateLimit\":null,\"dsn\":{\"secret\":\"dsn-secret\",\"public\":\"dsn-public\",\"csp\":\"\",\"security\":\"\",\"minidump\":\"\",\"unreal\":\"\",\"cdn\":\"\"},\"browserSdkVersion\":\"6.x\",\"browserSdk\":{\"choices\":[[\"latest\",\"latest\"],[\"7.x\",\"7.x\"],[\"6.x\",\"6.x\"],[\"5.x\",\"5.x\"],[\"4.x\",\"4.x\"]]},\"dateCreated\":\"2022-01-15T20:05:53.895882Z\"}]},{\"id\":\"project-2\",\"slug\":\"trending-movies\",\"name\":\"trending-movies\",\"isPublic\":false,\"isBookmarked\":false,\"color\":\"#bfb93f\",\"dateCreated\":\"2022-06-16T16:34:36.833418Z\",\"firstEvent\":null,\"firstTransactionEvent\":false,\"hasSessions\":false,\"features\":[\"alert-filters\",\"custom-inbound-filters\",\"data-forwarding\",\"discard-groups\",\"issue-alerts-targeting\",\"minidump\",\"performance-suspect-spans-ingestion\",\"race-free-group-creation\",\"rate-limits\",\"servicehooks\",\"similarity-indexing\",\"similarity-indexing-v2\",\"similarity-view\",\"similarity-view-v2\"],\"status\":\"active\",\"platform\":\"apple-ios\",\"isInternal\":false,\"isMember\":false,\"hasAccess\":true,\"avatar\":{\"avatarType\":\"letter_avatar\",\"avatarUuid\":null},\"organization\":{\"id\":\"organization-2\",\"slug\":\"sentry-sdks\",\"status\":{\"id\":\"active\",\"name\":\"active\"},\"name\":\"Sentry SDKs\",\"dateCreated\":\"2020-09-14T17:28:14.933511Z\",\"isEarlyAdopter\":true,\"require2FA\":false,\"requireEmailVerification\":false,\"avatar\":{\"avatarType\":\"upload\",\"avatarUuid\":\"\"},\"features\":[\"mobile-screenshots\",\"integrations-ticket-rules\",\"ondemand-budgets\",\"dashboards-template\",\"metric-alert-chartcuterie\",\"reprocessing-v2\",\"filters-and-sampling\",\"grouping-stacktrace-ui\",\"discover-frontend-use-events-endpoint\",\"grouping-title-ui\",\"app-store-connect-multiple\",\"crash-rate-alerts\",\"org-subdomains\",\"performance-dry-run-mep\",\"mobile-app\",\"grouping-tree-ui\",\"custom-event-title\",\"widget-library\",\"advanced-search\",\"auto-start-free-trial\",\"global-views\",\"integrations-chat-unfurl\",\"release-health-return-metrics\",\"integrations-stacktrace-link\",\"dashboards-basic\",\"minute-resolution-sessions\",\"discover-basic\",\"capture-lead\",\"alert-crash-free-metrics\",\"data-forwarding\",\"custom-symbol-sources\",\"sso-saml2\",\"integrations-alert-rule\",\"alert-wizard-v3\",\"invite-members\",\"team-insights\",\"integrations-issue-sync\",\"images-loaded-v2\",\"duplicate-alert-rule\",\"performance-autogroup-sibling-spans\",\"monitors\",\"new-widget-builder-experience-design\",\"dashboards-edit\",\"integrations-issue-basic\",\"performance-ops-breakdown\",\"performance-suspect-spans-view\",\"unified-span-view\",\"related-events\",\"integrations-event-hooks\",\"performance-frontend-use-events-endpoint\",\"sso-basic\",\"widget-viewer-modal\",\"event-attachments\",\"symbol-sources\",\"performance-span-histogram-view\",\"new-widget-builder-experience\",\"profiling\",\"dashboards-releases\",\"metrics-extraction\",\"integrations-incident-management\",\"new-weekly-report\",\"performance-view\",\"performance-span-tree-autoscroll\",\"open-membership\",\"metric-alert-snql\",\"shared-issues\",\"integrations-codeowners\",\"change-alerts\",\"dashboard-grid-layout\",\"baa\",\"discover-query\",\"alert-filters\",\"incidents\",\"relay\"]},\"keys\":[{\"id\":\"key-2\",\"name\":\"Default\",\"label\":\"Default\",\"public\":\"public\",\"secret\":\"secret\",\"projectId\":6789,\"isActive\":true,\"rateLimit\":null,\"dsn\":{\"secret\":\"dsn-secret\",\"public\":\"dsn-public\",\"csp\":\"\",\"security\":\"\",\"minidump\":\"\",\"unreal\":\"\",\"cdn\":\"\"},\"browserSdkVersion\":\"7.x\",\"browserSdk\":{\"choices\":[[\"latest\",\"latest\"],[\"7.x\",\"7.x\"],[\"6.x\",\"6.x\"],[\"5.x\",\"5.x\"],[\"4.x\",\"4.x\"]]},\"dateCreated\":\"2022-06-16T16:34:36.845197Z\"}]}]}"; - var parsed = sut.DeserializeJson(json); - - Assert.NotNull(parsed.apiKeys); - Assert.AreEqual("api-key-token", parsed.apiKeys!.token); - - Assert.NotNull(parsed.projects); - Assert.AreEqual(2, parsed.projects.Count); - var project = parsed.projects[0]; - Assert.AreEqual("project-slug", project.slug); - Assert.AreEqual("personal", project.name); - Assert.AreEqual("flutter", project.platform); - - Assert.IsFalse(project.IsUnity); - project.platform = "unity"; - Assert.IsTrue(project.IsUnity); - - Assert.NotNull(project.organization); - var org = project.organization!; - Assert.AreEqual("organization-1", org.name); - Assert.AreEqual("org-slug", org.slug); - - Assert.NotNull(project.keys); - Assert.AreEqual(1, project.keys!.Count); - var key = project.keys[0]; - Assert.NotNull(key.dsn); - Assert.AreEqual("dsn-public", key.dsn!.@public); - } + var sut = new WizardLoader(new TestLogger()); + + var json = "{\"apiKeys\":{\"id\":\"key-1\",\"scopes\":[\"org:read\",\"project:read\",\"project:releases\",\"project:write\"],\"application\":null,\"expiresAt\":null,\"dateCreated\":\"2022-03-02T10:37:56.385524Z\",\"state\":null,\"token\":\"api-key-token\",\"refreshToken\":null},\"projects\":[{\"id\":\"project-1\",\"slug\":\"project-slug\",\"name\":\"personal\",\"isPublic\":false,\"isBookmarked\":false,\"color\":\"#3fb7bf\",\"dateCreated\":\"2022-01-15T20:05:53.883628Z\",\"firstEvent\":\"2022-01-15T20:15:10.171648Z\",\"firstTransactionEvent\":false,\"hasSessions\":true,\"features\":[\"alert-filters\",\"issue-alerts-targeting\",\"minidump\",\"performance-suspect-spans-ingestion\",\"race-free-group-creation\",\"similarity-indexing\",\"similarity-view\",\"releases\"],\"status\":\"active\",\"platform\":\"flutter\",\"isInternal\":false,\"isMember\":false,\"hasAccess\":true,\"avatar\":{\"avatarType\":\"letter_avatar\",\"avatarUuid\":null},\"organization\":{\"id\":\"org-1\",\"slug\":\"org-slug\",\"status\":{\"id\":\"active\",\"name\":\"active\"},\"name\":\"organization-1\",\"dateCreated\":\"2022-01-15T20:03:49.620687Z\",\"isEarlyAdopter\":false,\"require2FA\":false,\"requireEmailVerification\":false,\"avatar\":{\"avatarType\":\"letter_avatar\",\"avatarUuid\":null},\"features\":[\"onboarding\",\"ondemand-budgets\",\"slack-overage-notifications\",\"dashboards-template\",\"discover-frontend-use-events-endpoint\",\"integrations-stacktrace-link\",\"crash-rate-alerts\",\"org-subdomains\",\"performance-dry-run-mep\",\"mobile-app\",\"custom-event-title\",\"advanced-search\",\"widget-library\",\"auto-start-free-trial\",\"release-health-return-metrics\",\"minute-resolution-sessions\",\"capture-lead\",\"invite-members-rate-limits\",\"alert-crash-free-metrics\",\"alert-wizard-v3\",\"images-loaded-v2\",\"duplicate-alert-rule\",\"performance-autogroup-sibling-spans\",\"performance-ops-breakdown\",\"new-widget-builder-experience-design\",\"performance-suspect-spans-view\",\"unified-span-view\",\"performance-frontend-use-events-endpoint\",\"widget-viewer-modal\",\"event-attachments\",\"symbol-sources\",\"performance-span-histogram-view\",\"intl-sales-tax\",\"metrics-extraction\",\"performance-view\",\"new-weekly-report\",\"performance-span-tree-autoscroll\",\"metric-alert-snql\",\"shared-issues\",\"dashboard-grid-layout\",\"open-membership\"]},\"keys\":[{\"id\":\"key-1\",\"name\":\"Default\",\"label\":\"Default\",\"public\":\"public-key\",\"secret\":\"secret-key\",\"projectId\":12345,\"isActive\":true,\"rateLimit\":null,\"dsn\":{\"secret\":\"dsn-secret\",\"public\":\"dsn-public\",\"csp\":\"\",\"security\":\"\",\"minidump\":\"\",\"unreal\":\"\",\"cdn\":\"\"},\"browserSdkVersion\":\"6.x\",\"browserSdk\":{\"choices\":[[\"latest\",\"latest\"],[\"7.x\",\"7.x\"],[\"6.x\",\"6.x\"],[\"5.x\",\"5.x\"],[\"4.x\",\"4.x\"]]},\"dateCreated\":\"2022-01-15T20:05:53.895882Z\"}]},{\"id\":\"project-2\",\"slug\":\"trending-movies\",\"name\":\"trending-movies\",\"isPublic\":false,\"isBookmarked\":false,\"color\":\"#bfb93f\",\"dateCreated\":\"2022-06-16T16:34:36.833418Z\",\"firstEvent\":null,\"firstTransactionEvent\":false,\"hasSessions\":false,\"features\":[\"alert-filters\",\"custom-inbound-filters\",\"data-forwarding\",\"discard-groups\",\"issue-alerts-targeting\",\"minidump\",\"performance-suspect-spans-ingestion\",\"race-free-group-creation\",\"rate-limits\",\"servicehooks\",\"similarity-indexing\",\"similarity-indexing-v2\",\"similarity-view\",\"similarity-view-v2\"],\"status\":\"active\",\"platform\":\"apple-ios\",\"isInternal\":false,\"isMember\":false,\"hasAccess\":true,\"avatar\":{\"avatarType\":\"letter_avatar\",\"avatarUuid\":null},\"organization\":{\"id\":\"organization-2\",\"slug\":\"sentry-sdks\",\"status\":{\"id\":\"active\",\"name\":\"active\"},\"name\":\"Sentry SDKs\",\"dateCreated\":\"2020-09-14T17:28:14.933511Z\",\"isEarlyAdopter\":true,\"require2FA\":false,\"requireEmailVerification\":false,\"avatar\":{\"avatarType\":\"upload\",\"avatarUuid\":\"\"},\"features\":[\"mobile-screenshots\",\"integrations-ticket-rules\",\"ondemand-budgets\",\"dashboards-template\",\"metric-alert-chartcuterie\",\"reprocessing-v2\",\"filters-and-sampling\",\"grouping-stacktrace-ui\",\"discover-frontend-use-events-endpoint\",\"grouping-title-ui\",\"app-store-connect-multiple\",\"crash-rate-alerts\",\"org-subdomains\",\"performance-dry-run-mep\",\"mobile-app\",\"grouping-tree-ui\",\"custom-event-title\",\"widget-library\",\"advanced-search\",\"auto-start-free-trial\",\"global-views\",\"integrations-chat-unfurl\",\"release-health-return-metrics\",\"integrations-stacktrace-link\",\"dashboards-basic\",\"minute-resolution-sessions\",\"discover-basic\",\"capture-lead\",\"alert-crash-free-metrics\",\"data-forwarding\",\"custom-symbol-sources\",\"sso-saml2\",\"integrations-alert-rule\",\"alert-wizard-v3\",\"invite-members\",\"team-insights\",\"integrations-issue-sync\",\"images-loaded-v2\",\"duplicate-alert-rule\",\"performance-autogroup-sibling-spans\",\"monitors\",\"new-widget-builder-experience-design\",\"dashboards-edit\",\"integrations-issue-basic\",\"performance-ops-breakdown\",\"performance-suspect-spans-view\",\"unified-span-view\",\"related-events\",\"integrations-event-hooks\",\"performance-frontend-use-events-endpoint\",\"sso-basic\",\"widget-viewer-modal\",\"event-attachments\",\"symbol-sources\",\"performance-span-histogram-view\",\"new-widget-builder-experience\",\"profiling\",\"dashboards-releases\",\"metrics-extraction\",\"integrations-incident-management\",\"new-weekly-report\",\"performance-view\",\"performance-span-tree-autoscroll\",\"open-membership\",\"metric-alert-snql\",\"shared-issues\",\"integrations-codeowners\",\"change-alerts\",\"dashboard-grid-layout\",\"baa\",\"discover-query\",\"alert-filters\",\"incidents\",\"relay\"]},\"keys\":[{\"id\":\"key-2\",\"name\":\"Default\",\"label\":\"Default\",\"public\":\"public\",\"secret\":\"secret\",\"projectId\":6789,\"isActive\":true,\"rateLimit\":null,\"dsn\":{\"secret\":\"dsn-secret\",\"public\":\"dsn-public\",\"csp\":\"\",\"security\":\"\",\"minidump\":\"\",\"unreal\":\"\",\"cdn\":\"\"},\"browserSdkVersion\":\"7.x\",\"browserSdk\":{\"choices\":[[\"latest\",\"latest\"],[\"7.x\",\"7.x\"],[\"6.x\",\"6.x\"],[\"5.x\",\"5.x\"],[\"4.x\",\"4.x\"]]},\"dateCreated\":\"2022-06-16T16:34:36.845197Z\"}]}]}"; + var parsed = sut.DeserializeJson(json); + + Assert.NotNull(parsed.apiKeys); + Assert.AreEqual("api-key-token", parsed.apiKeys!.token); + + Assert.NotNull(parsed.projects); + Assert.AreEqual(2, parsed.projects.Count); + var project = parsed.projects[0]; + Assert.AreEqual("project-slug", project.slug); + Assert.AreEqual("personal", project.name); + Assert.AreEqual("flutter", project.platform); + + Assert.IsFalse(project.IsUnity); + project.platform = "unity"; + Assert.IsTrue(project.IsUnity); + + Assert.NotNull(project.organization); + var org = project.organization!; + Assert.AreEqual("organization-1", org.name); + Assert.AreEqual("org-slug", org.slug); + + Assert.NotNull(project.keys); + Assert.AreEqual(1, project.keys!.Count); + var key = project.keys[0]; + Assert.NotNull(key.dsn); + Assert.AreEqual("dsn-public", key.dsn!.@public); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs index ee5b7f943..710e1d27f 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs @@ -6,229 +6,228 @@ using Sentry.Unity.Tests.SharedClasses; using UnityEditor.Build; -namespace Sentry.Unity.Editor.iOS.Tests +namespace Sentry.Unity.Editor.iOS.Tests; + +public class BuildPostProcessorTests { - public class BuildPostProcessorTests + private string _testDirectoryRoot = null!; + private string _testFrameworkPath = null!; + private string _testFilePath = null!; + private string _outputProjectPath = null!; + + [SetUp] + public void Setup() { - private string _testDirectoryRoot = null!; - private string _testFrameworkPath = null!; - private string _testFilePath = null!; - private string _outputProjectPath = null!; + _testDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Substring(0, 4)); + _testFrameworkPath = Path.Combine(_testDirectoryRoot, "Test.framework"); + _testFilePath = Path.Combine(_testDirectoryRoot, "Test.m"); + _outputProjectPath = Path.Combine(_testDirectoryRoot, "XcodeProject"); + + Directory.CreateDirectory(_testDirectoryRoot); + Directory.CreateDirectory(_testFrameworkPath); + File.Create(_testFilePath).Close(); + + // Test setup for output Xcode project + var testXcodeProjectPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestFiles", "2019_4"); + SentryFileUtil.CopyDirectory(testXcodeProjectPath, _outputProjectPath); + } - [SetUp] - public void Setup() - { - _testDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Substring(0, 4)); - _testFrameworkPath = Path.Combine(_testDirectoryRoot, "Test.framework"); - _testFilePath = Path.Combine(_testDirectoryRoot, "Test.m"); - _outputProjectPath = Path.Combine(_testDirectoryRoot, "XcodeProject"); + [TearDown] + public void TearDown() => Directory.Delete(_testDirectoryRoot, true); - Directory.CreateDirectory(_testDirectoryRoot); - Directory.CreateDirectory(_testFrameworkPath); - File.Create(_testFilePath).Close(); + [Test] + public void AddSentryToXcodeProject_OptionsNull_LogsAndCopiesNoOpBridge() + { + var testLogger = new TestLogger(); - // Test setup for output Xcode project - var testXcodeProjectPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestFiles", "2019_4"); - SentryFileUtil.CopyDirectory(testXcodeProjectPath, _outputProjectPath); - } + BuildPostProcess.AddSentryToXcodeProject(null, null, testLogger, _outputProjectPath); - [TearDown] - public void TearDown() => Directory.Delete(_testDirectoryRoot, true); + Assert.IsFalse(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Info && + log.message.Contains("Attempting to add Sentry to the Xcode project."))); // Sanity check + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Warning && + log.message.Contains("iOS native support disabled because Sentry has not been configured."))); - [Test] - public void AddSentryToXcodeProject_OptionsNull_LogsAndCopiesNoOpBridge() - { - var testLogger = new TestLogger(); + var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); - BuildPostProcess.AddSentryToXcodeProject(null, null, testLogger, _outputProjectPath); + Assert.IsTrue(File.Exists(noOpBridgePath)); + StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK + } - Assert.IsFalse(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Info && - log.message.Contains("Attempting to add Sentry to the Xcode project."))); // Sanity check - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Warning && - log.message.Contains("iOS native support disabled because Sentry has not been configured."))); + [Test] + public void AddSentryToXcodeProject_SdkDisabled_LogsAndCopiesNoOpBridge() + { + var options = new SentryUnityOptions { Enabled = false }; + var testLogger = new TestLogger(); - var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), - SentryXcodeProject.BridgeName); + BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _outputProjectPath); - Assert.IsTrue(File.Exists(noOpBridgePath)); - StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK - } + Assert.IsFalse(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Info && + log.message.Contains("Attempting to add Sentry to the Xcode project."))); // Sanity check + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Warning && + log.message.Contains("Sentry SDK has been disabled. There will be no iOS native support."))); - [Test] - public void AddSentryToXcodeProject_SdkDisabled_LogsAndCopiesNoOpBridge() - { - var options = new SentryUnityOptions { Enabled = false }; - var testLogger = new TestLogger(); + var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); - BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _outputProjectPath); + Assert.IsTrue(File.Exists(noOpBridgePath)); + StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK + } - Assert.IsFalse(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Info && - log.message.Contains("Attempting to add Sentry to the Xcode project."))); // Sanity check - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Warning && - log.message.Contains("Sentry SDK has been disabled. There will be no iOS native support."))); + [Test] + public void SetupNoOpBridge_CopiesNoOpBridgeToOutput() + { + BuildPostProcess.SetupNoOpBridge(new TestLogger(), _outputProjectPath); - var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), - SentryXcodeProject.BridgeName); + var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); - Assert.IsTrue(File.Exists(noOpBridgePath)); - StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK - } + Assert.IsTrue(File.Exists(noOpBridgePath)); + StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK + } - [Test] - public void SetupNoOpBridge_CopiesNoOpBridgeToOutput() - { - BuildPostProcess.SetupNoOpBridge(new TestLogger(), _outputProjectPath); + [Test] + public void SetupSentry_CopiesFrameworkAndBridge() + { + var options = new SentryUnityOptions(); + var testLogger = new TestLogger(); - var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), - SentryXcodeProject.BridgeName); + BuildPostProcess.SetupSentry(options, null, testLogger, _outputProjectPath); - Assert.IsTrue(File.Exists(noOpBridgePath)); - StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK - } + var bridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); + var frameworkPath = Path.Combine(_outputProjectPath, "Frameworks", SentryXcodeProject.FrameworkName); - [Test] - public void SetupSentry_CopiesFrameworkAndBridge() - { - var options = new SentryUnityOptions(); - var testLogger = new TestLogger(); + Assert.IsTrue(File.Exists(bridgePath)); + Assert.IsTrue(Directory.Exists(frameworkPath)); - BuildPostProcess.SetupSentry(options, null, testLogger, _outputProjectPath); + StringAssert.Contains("[SentrySDK", File.ReadAllText(bridgePath)); // Sanity check + } - var bridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), - SentryXcodeProject.BridgeName); - var frameworkPath = Path.Combine(_outputProjectPath, "Frameworks", SentryXcodeProject.FrameworkName); + [Test] + public void IsNativeSupportEnabled_OptionsDisabled_LogsAndReturnsFalse() + { + var options = new SentryUnityOptions { Enabled = false }; + var testLogger = new TestLogger(); - Assert.IsTrue(File.Exists(bridgePath)); - Assert.IsTrue(Directory.Exists(frameworkPath)); + var enabled = BuildPostProcess.IsNativeSupportEnabled(options, testLogger); - StringAssert.Contains("[SentrySDK", File.ReadAllText(bridgePath)); // Sanity check - } + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Warning && + log.message.Contains("Sentry SDK has been disabled. There will be no iOS native support."))); + Assert.IsFalse(enabled); + } - [Test] - public void IsNativeSupportEnabled_OptionsDisabled_LogsAndReturnsFalse() + [Test] + public void IsNativeSupportEnabled_IosNativeSupportDisabled_LogsAndReturnsFalse() + { + var options = new SentryUnityOptions { - var options = new SentryUnityOptions { Enabled = false }; - var testLogger = new TestLogger(); + Dsn = "test_dsn", + IosNativeSupportEnabled = false + }; + var testLogger = new TestLogger(); - var enabled = BuildPostProcess.IsNativeSupportEnabled(options, testLogger); + var enabled = BuildPostProcess.IsNativeSupportEnabled(options, testLogger); - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Warning && - log.message.Contains("Sentry SDK has been disabled. There will be no iOS native support."))); - Assert.IsFalse(enabled); - } + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Info && + log.message.Contains("The iOS native support has been disabled through the options."))); + Assert.IsFalse(enabled); + } - [Test] - public void IsNativeSupportEnabled_IosNativeSupportDisabled_LogsAndReturnsFalse() - { - var options = new SentryUnityOptions - { - Dsn = "test_dsn", - IosNativeSupportEnabled = false - }; - var testLogger = new TestLogger(); - - var enabled = BuildPostProcess.IsNativeSupportEnabled(options, testLogger); - - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Info && - log.message.Contains("The iOS native support has been disabled through the options."))); - Assert.IsFalse(enabled); - } - - [Test] - public void AddSentryToXcodeProject_NativeSupportDisabledButMainAlreadyModified_ThrowsBuildFailedException() - { - var file = new FileInfo(Path.Combine(_outputProjectPath, SentryXcodeProject.MainPath)); - file.Directory?.Create(); - File.WriteAllText(file.FullName, NativeMain.Include); - var options = new SentryUnityOptions - { - Dsn = "test_dsn", - IosNativeSupportEnabled = false - }; - var testLogger = new TestLogger(); - - Assert.Throws(() => - BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _outputProjectPath)); - - Directory.Delete(_outputProjectPath, true); - } - - [Test] - public void CopyFrameworkToXcodeProject_CopyFramework_DirectoryExists() + [Test] + public void AddSentryToXcodeProject_NativeSupportDisabledButMainAlreadyModified_ThrowsBuildFailedException() + { + var file = new FileInfo(Path.Combine(_outputProjectPath, SentryXcodeProject.MainPath)); + file.Directory?.Create(); + File.WriteAllText(file.FullName, NativeMain.Include); + var options = new SentryUnityOptions { - var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"); + Dsn = "test_dsn", + IosNativeSupportEnabled = false + }; + var testLogger = new TestLogger(); - BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, new TestLogger()); + Assert.Throws(() => + BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _outputProjectPath)); - Assert.IsTrue(Directory.Exists(targetPath)); - } + Directory.Delete(_outputProjectPath, true); + } - [Test] - public void CopyFramework_FrameworkAlreadyExists_LogsSkipMessage() - { - var testLogger = new TestLogger(); - var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"); - - BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, testLogger); - BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, testLogger); - - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Debug && - log.message.Contains("has already been copied to"))); - } - - [Test] - public void CopyFramework_SourceMissing_ThrowsDirectoryNotFoundException() => - Assert.Throws(() => - BuildPostProcess.CopyFramework("non-existent-path.framework", - Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); - - [Test] - public void CopyFramework_FailedToCopy_ThrowsDirectoryNotFoundException() => - Assert.Throws(() => - BuildPostProcess.CopyFramework("non-existent-path.framework", - Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); - - [Test] - public void CopyFile_CopyFile_FileExists() - { - var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.m"); + [Test] + public void CopyFrameworkToXcodeProject_CopyFramework_DirectoryExists() + { + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"); - BuildPostProcess.CopyFile(_testFilePath, targetPath, new TestLogger()); + BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, new TestLogger()); - Assert.IsTrue(File.Exists(targetPath)); - } + Assert.IsTrue(Directory.Exists(targetPath)); + } - [Test] - public void CopyFile_FileAlreadyExists_LogsSkipMessage() - { - var testLogger = new TestLogger(); - var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.m"); - - BuildPostProcess.CopyFile(_testFilePath, targetPath, testLogger); - BuildPostProcess.CopyFile(_testFilePath, targetPath, testLogger); - - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Debug && - log.message.Contains("has already been copied to"))); - } - - [Test] - public void CopyFile_SourceMissing_ThrowsFileNotFoundException() => - Assert.Throws(() => - BuildPostProcess.CopyFile("non-existent-path.m", - Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); - - [Test] - public void CopyFile_FailedToCopyFile_ThrowsFileNotFoundException() => - Assert.Throws(() => - BuildPostProcess.CopyFile("non-existent-path.m", - Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); + [Test] + public void CopyFramework_FrameworkAlreadyExists_LogsSkipMessage() + { + var testLogger = new TestLogger(); + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"); + + BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, testLogger); + BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, testLogger); + + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Debug && + log.message.Contains("has already been copied to"))); + } + + [Test] + public void CopyFramework_SourceMissing_ThrowsDirectoryNotFoundException() => + Assert.Throws(() => + BuildPostProcess.CopyFramework("non-existent-path.framework", + Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); + + [Test] + public void CopyFramework_FailedToCopy_ThrowsDirectoryNotFoundException() => + Assert.Throws(() => + BuildPostProcess.CopyFramework("non-existent-path.framework", + Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); + + [Test] + public void CopyFile_CopyFile_FileExists() + { + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.m"); + + BuildPostProcess.CopyFile(_testFilePath, targetPath, new TestLogger()); + + Assert.IsTrue(File.Exists(targetPath)); } -} + + [Test] + public void CopyFile_FileAlreadyExists_LogsSkipMessage() + { + var testLogger = new TestLogger(); + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.m"); + + BuildPostProcess.CopyFile(_testFilePath, targetPath, testLogger); + BuildPostProcess.CopyFile(_testFilePath, targetPath, testLogger); + + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Debug && + log.message.Contains("has already been copied to"))); + } + + [Test] + public void CopyFile_SourceMissing_ThrowsFileNotFoundException() => + Assert.Throws(() => + BuildPostProcess.CopyFile("non-existent-path.m", + Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); + + [Test] + public void CopyFile_FailedToCopyFile_ThrowsFileNotFoundException() => + Assert.Throws(() => + BuildPostProcess.CopyFile("non-existent-path.m", + Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs index 4841b4740..e4ba3f6f8 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs @@ -3,80 +3,79 @@ using System.Reflection; using NUnit.Framework; -namespace Sentry.Unity.Editor.iOS.Tests +namespace Sentry.Unity.Editor.iOS.Tests; + +public class NativeMainTests { - public class NativeMainTests + [Test] + public void ContainsSentry_SentryAlreadyAdded_ReturnsTrue() { - [Test] - public void ContainsSentry_SentryAlreadyAdded_ReturnsTrue() - { - var main = GetFileContents("main_expected.txt"); + var main = GetFileContents("main_expected.txt"); - var containsSentry = NativeMain.ContainsSentry(main, null); + var containsSentry = NativeMain.ContainsSentry(main, null); - Assert.IsTrue(containsSentry); - } + Assert.IsTrue(containsSentry); + } - [Test] - public void ContainsSentry_SentryNotAdded_ReturnsFalse() - { - var main = GetFileContents("main.txt"); + [Test] + public void ContainsSentry_SentryNotAdded_ReturnsFalse() + { + var main = GetFileContents("main.txt"); - var containsSentry = NativeMain.ContainsSentry(main, null); + var containsSentry = NativeMain.ContainsSentry(main, null); - Assert.IsFalse(containsSentry); - } + Assert.IsFalse(containsSentry); + } - [Test] - public void AddSentryToMain_SentryNotAddedTo_MatchesExpectedOutput() - { - var main = GetFileContents("main.txt"); - var expectedMain = GetFileContents("main_expected.txt"); + [Test] + public void AddSentryToMain_SentryNotAddedTo_MatchesExpectedOutput() + { + var main = GetFileContents("main.txt"); + var expectedMain = GetFileContents("main_expected.txt"); - var actualMain = NativeMain.AddSentryToMain(main); + var actualMain = NativeMain.AddSentryToMain(main); - Assert.AreEqual(expectedMain, actualMain); - } + Assert.AreEqual(expectedMain, actualMain); + } - [Test] - public void AddSentryToMain_InvalidMain_ThrowsException() - { - var main = string.Empty; + [Test] + public void AddSentryToMain_InvalidMain_ThrowsException() + { + var main = string.Empty; - var assert = Assert.Throws(() => NativeMain.AddSentryToMain(main)); - Assert.AreEqual("main", assert.ParamName); - } + var assert = Assert.Throws(() => NativeMain.AddSentryToMain(main)); + Assert.AreEqual("main", assert.ParamName); + } - [Test] - public void AddSentry_MainDoesNotExist_ThrowsFileNotFoundException() - { - var pathToMain = "Path/That/Does/Not/Exist"; + [Test] + public void AddSentry_MainDoesNotExist_ThrowsFileNotFoundException() + { + var pathToMain = "Path/That/Does/Not/Exist"; - var assert = Assert.Throws(() => NativeMain.AddSentry(pathToMain, null)); - StringAssert.Contains("Could not find main.", assert.Message); - } + var assert = Assert.Throws(() => NativeMain.AddSentry(pathToMain, null)); + StringAssert.Contains("Could not find main.", assert.Message); + } - [Test] - public void AddSentry_CleanMain_OutputMatchesExpected() - { - var expectedMain = GetFileContents("main_expected.txt"); - var workingMainPath = "temp.txt"; - File.WriteAllText(workingMainPath, GetFileContents("main.txt")); + [Test] + public void AddSentry_CleanMain_OutputMatchesExpected() + { + var expectedMain = GetFileContents("main_expected.txt"); + var workingMainPath = "temp.txt"; + File.WriteAllText(workingMainPath, GetFileContents("main.txt")); - NativeMain.AddSentry(workingMainPath, null); - var actualMain = File.ReadAllText(workingMainPath); + NativeMain.AddSentry(workingMainPath, null); + var actualMain = File.ReadAllText(workingMainPath); - Assert.AreEqual(expectedMain, actualMain); + Assert.AreEqual(expectedMain, actualMain); - File.Delete(workingMainPath); - } + File.Delete(workingMainPath); + } - private string GetFileContents(string fileName) - { - var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - var mainPath = Path.Combine(assemblyPath, "TestFiles", "2019_4", fileName); + private string GetFileContents(string fileName) + { + var assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + var mainPath = Path.Combine(assemblyPath, "TestFiles", "2019_4", fileName); - return File.ReadAllText(mainPath); - } + return File.ReadAllText(mainPath); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs index 36a58c83d..5c009b3b2 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs @@ -3,118 +3,117 @@ using NUnit.Framework; using UnityEngine; -namespace Sentry.Unity.Editor.iOS.Tests +namespace Sentry.Unity.Editor.iOS.Tests; + +public class NativeOptionsTests { - public class NativeOptionsTests + [Test] + public void GenerateOptions_NewSentryOptions_Compiles() { - [Test] - public void GenerateOptions_NewSentryOptions_Compiles() + if (Application.platform != RuntimePlatform.OSXEditor) { - if (Application.platform != RuntimePlatform.OSXEditor) - { - Assert.Inconclusive("Skipping: Not on macOS"); - } + Assert.Inconclusive("Skipping: Not on macOS"); + } - const string testOptionsFileName = "testOptions.m"; - var nativeOptionsString = NativeOptions.Generate(new SentryUnityOptions()); - File.WriteAllText(testOptionsFileName, nativeOptionsString); + const string testOptionsFileName = "testOptions.m"; + var nativeOptionsString = NativeOptions.Generate(new SentryUnityOptions()); + File.WriteAllText(testOptionsFileName, nativeOptionsString); - var process = Process.Start("clang", $"-fsyntax-only {testOptionsFileName}"); - process.WaitForExit(); + var process = Process.Start("clang", $"-fsyntax-only {testOptionsFileName}"); + process.WaitForExit(); - Assert.AreEqual(0, process.ExitCode); + Assert.AreEqual(0, process.ExitCode); - File.Delete(testOptionsFileName); - } + File.Delete(testOptionsFileName); + } - [Test] - public void GenerateOptions_NewSentryOptionsGarbageAppended_FailsToCompile() + [Test] + public void GenerateOptions_NewSentryOptionsGarbageAppended_FailsToCompile() + { + if (Application.platform != RuntimePlatform.OSXEditor) { - if (Application.platform != RuntimePlatform.OSXEditor) - { - Assert.Inconclusive("Skipping: Not on macOS"); - } + Assert.Inconclusive("Skipping: Not on macOS"); + } - const string testOptionsFileName = "testOptions.m"; - var nativeOptionsString = NativeOptions.Generate(new SentryUnityOptions()); - nativeOptionsString += "AppendedTextToFailCompilation"; + const string testOptionsFileName = "testOptions.m"; + var nativeOptionsString = NativeOptions.Generate(new SentryUnityOptions()); + nativeOptionsString += "AppendedTextToFailCompilation"; - File.WriteAllText(testOptionsFileName, nativeOptionsString); + File.WriteAllText(testOptionsFileName, nativeOptionsString); - var process = Process.Start("clang", $"-fsyntax-only -framework Foundation {testOptionsFileName}"); - process.WaitForExit(); + var process = Process.Start("clang", $"-fsyntax-only -framework Foundation {testOptionsFileName}"); + process.WaitForExit(); - Assert.AreEqual(1, process.ExitCode); + Assert.AreEqual(1, process.ExitCode); - File.Delete(testOptionsFileName); - } + File.Delete(testOptionsFileName); + } - [Test] - public void CreateOptionsFile_NewSentryOptions_FileCreated() - { - const string testOptionsFileName = "testOptions.m"; + [Test] + public void CreateOptionsFile_NewSentryOptions_FileCreated() + { + const string testOptionsFileName = "testOptions.m"; - NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions()); + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions()); - Assert.IsTrue(File.Exists(testOptionsFileName)); + Assert.IsTrue(File.Exists(testOptionsFileName)); - File.Delete(testOptionsFileName); - } + File.Delete(testOptionsFileName); + } - [Test] - public void CreateOptionsFile_NewSentryOptions_ContainsBaseOptions() - { - const string testOptionsFileName = "testOptions.m"; - - NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions()); - - Assert.IsTrue(File.Exists(testOptionsFileName)); // Sanity check - - var options = File.ReadAllText(testOptionsFileName); - StringAssert.Contains("dsn", options); - StringAssert.Contains("debug", options); - StringAssert.Contains("diagnosticLevel", options); - StringAssert.Contains("maxBreadcrumbs", options); - StringAssert.Contains("maxCacheItems", options); - StringAssert.Contains("enableAutoSessionTracking", options); - StringAssert.Contains("enableAppHangTracking", options); - StringAssert.Contains("enableCaptureFailedRequests", options); - StringAssert.Contains("sendDefaultPii", options); - StringAssert.Contains("attachScreenshot", options); - StringAssert.Contains("release", options); - StringAssert.Contains("environment", options); - - File.Delete(testOptionsFileName); - } + [Test] + public void CreateOptionsFile_NewSentryOptions_ContainsBaseOptions() + { + const string testOptionsFileName = "testOptions.m"; + + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions()); + + Assert.IsTrue(File.Exists(testOptionsFileName)); // Sanity check + + var options = File.ReadAllText(testOptionsFileName); + StringAssert.Contains("dsn", options); + StringAssert.Contains("debug", options); + StringAssert.Contains("diagnosticLevel", options); + StringAssert.Contains("maxBreadcrumbs", options); + StringAssert.Contains("maxCacheItems", options); + StringAssert.Contains("enableAutoSessionTracking", options); + StringAssert.Contains("enableAppHangTracking", options); + StringAssert.Contains("enableCaptureFailedRequests", options); + StringAssert.Contains("sendDefaultPii", options); + StringAssert.Contains("attachScreenshot", options); + StringAssert.Contains("release", options); + StringAssert.Contains("environment", options); + + File.Delete(testOptionsFileName); + } - [Test] - public void CreateOptionsFile_FilterBadGatewayEnabled_AddsFiltering() - { - const string testOptionsFileName = "testOptions.m"; + [Test] + public void CreateOptionsFile_FilterBadGatewayEnabled_AddsFiltering() + { + const string testOptionsFileName = "testOptions.m"; - NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { FilterBadGatewayExceptions = true }); + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { FilterBadGatewayExceptions = true }); - Assert.IsTrue(File.Exists(testOptionsFileName)); // Sanity check + Assert.IsTrue(File.Exists(testOptionsFileName)); // Sanity check - var nativeOptions = File.ReadAllText(testOptionsFileName); - StringAssert.Contains("event.request.url containsString:@\"operate-sdk-telemetry.unity3d.com\"", nativeOptions); + var nativeOptions = File.ReadAllText(testOptionsFileName); + StringAssert.Contains("event.request.url containsString:@\"operate-sdk-telemetry.unity3d.com\"", nativeOptions); - File.Delete(testOptionsFileName); - } + File.Delete(testOptionsFileName); + } - [Test] - public void CreateOptionsFile_FilterBadGatewayDisabled_DoesNotAddFiltering() - { - const string testOptionsFileName = "testOptions.m"; + [Test] + public void CreateOptionsFile_FilterBadGatewayDisabled_DoesNotAddFiltering() + { + const string testOptionsFileName = "testOptions.m"; - NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { FilterBadGatewayExceptions = false }); + NativeOptions.CreateFile(testOptionsFileName, new SentryUnityOptions { FilterBadGatewayExceptions = false }); - Assert.IsTrue(File.Exists(testOptionsFileName)); // Sanity check + Assert.IsTrue(File.Exists(testOptionsFileName)); // Sanity check - var nativeOptions = File.ReadAllText(testOptionsFileName); - StringAssert.DoesNotContain("event.request.url containsString:@\"operate-sdk-telemetry.unity3d.com\"", nativeOptions); + var nativeOptions = File.ReadAllText(testOptionsFileName); + StringAssert.DoesNotContain("event.request.url containsString:@\"operate-sdk-telemetry.unity3d.com\"", nativeOptions); - File.Delete(testOptionsFileName); - } + File.Delete(testOptionsFileName); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs index 5ec9adc40..dcc96891d 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs @@ -3,168 +3,165 @@ using System.Reflection; using System.Text.RegularExpressions; using NUnit.Framework; -using NUnit.Framework.Internal; -using Sentry.Extensibility; using Sentry.Unity.Tests.SharedClasses; -namespace Sentry.Unity.Editor.iOS.Tests +namespace Sentry.Unity.Editor.iOS.Tests; + +public class SentryXcodeProjectTests { - public class SentryXcodeProjectTests + private class Fixture { - private class Fixture - { - public string ProjectRoot { get; set; } = - Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestFiles", "2019_4"); - public SentryUnityOptions Options { get; set; } - public TestLogger TestLogger { get; set; } + public string ProjectRoot { get; set; } = + Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestFiles", "2019_4"); + public SentryUnityOptions Options { get; set; } + public TestLogger TestLogger { get; set; } - public Fixture() + public Fixture() + { + TestLogger = new TestLogger(); + Options = new SentryUnityOptions { - TestLogger = new TestLogger(); - Options = new SentryUnityOptions - { - Debug = true, - DiagnosticLevel = SentryLevel.Debug, - DiagnosticLogger = TestLogger - }; - } - - public SentryXcodeProject GetSut() => new(ProjectRoot, TestLogger); + Debug = true, + DiagnosticLevel = SentryLevel.Debug, + DiagnosticLogger = TestLogger + }; } - private Fixture _fixture = new(); + public SentryXcodeProject GetSut() => new(ProjectRoot, TestLogger); + } + + private Fixture _fixture = new(); - [SetUp] - public void SetUp() => _fixture = new Fixture(); + [SetUp] + public void SetUp() => _fixture = new Fixture(); - [TearDown] - public void DestroyFrameworkDirectories() + [TearDown] + public void DestroyFrameworkDirectories() + { + var frameworkPath = Path.Combine(_fixture.ProjectRoot, "Frameworks"); + if (Directory.Exists(frameworkPath)) { - var frameworkPath = Path.Combine(_fixture.ProjectRoot, "Frameworks"); - if (Directory.Exists(frameworkPath)) - { - Directory.Delete(frameworkPath, true); - } + Directory.Delete(frameworkPath, true); } + } - [Test] - public void ReadFromProjectFile_ProjectExists_ReadsProject() - { - var xcodeProject = _fixture.GetSut(); + [Test] + public void ReadFromProjectFile_ProjectExists_ReadsProject() + { + var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + xcodeProject.ReadFromProjectFile(); - Assert.IsNotEmpty(xcodeProject.ProjectToString()); - } + Assert.IsNotEmpty(xcodeProject.ProjectToString()); + } - [Test] - public void ReadFromProjectFile_ProjectDoesNotExist_ThrowsFileNotFoundException() - { - _fixture.ProjectRoot = "Path/That/Does/Not/Exist"; - var xcodeProject = _fixture.GetSut(); + [Test] + public void ReadFromProjectFile_ProjectDoesNotExist_ThrowsFileNotFoundException() + { + _fixture.ProjectRoot = "Path/That/Does/Not/Exist"; + var xcodeProject = _fixture.GetSut(); - Assert.Throws(() => xcodeProject.ReadFromProjectFile()); - } + Assert.Throws(() => xcodeProject.ReadFromProjectFile()); + } - [Test] - public void AddSentryFramework_CleanXcodeProject_SentryWasAdded() - { - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void AddSentryFramework_CleanXcodeProject_SentryWasAdded() + { + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - xcodeProject.AddSentryFramework(); + xcodeProject.AddSentryFramework(); - StringAssert.Contains(SentryXcodeProject.FrameworkName, xcodeProject.ProjectToString()); - } + StringAssert.Contains(SentryXcodeProject.FrameworkName, xcodeProject.ProjectToString()); + } - [Test] - public void AddSentryFramework_EmbedsFramework() - { - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void AddSentryFramework_EmbedsFramework() + { + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - xcodeProject.AddSentryFramework(); + xcodeProject.AddSentryFramework(); - StringAssert.Contains("/* Sentry.xcframework in Embed Frameworks */", xcodeProject.ProjectToString()); - } + StringAssert.Contains("/* Sentry.xcframework in Embed Frameworks */", xcodeProject.ProjectToString()); + } - [Test] - public void AddSentryFramework_FrameworkSearchPathAlreadySet_DoesNotGetOverwritten() - { - const string testPath = "path_that_should_not_get_overwritten"; - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); - xcodeProject.SetSearchPathBuildProperty(testPath); + [Test] + public void AddSentryFramework_FrameworkSearchPathAlreadySet_DoesNotGetOverwritten() + { + const string testPath = "path_that_should_not_get_overwritten"; + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); + xcodeProject.SetSearchPathBuildProperty(testPath); - xcodeProject.AddSentryFramework(); + xcodeProject.AddSentryFramework(); - StringAssert.Contains(testPath, xcodeProject.ProjectToString()); - } + StringAssert.Contains(testPath, xcodeProject.ProjectToString()); + } - [Test] - public void AddSentryFramework_BitcodeDisabled() - { - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void AddSentryFramework_BitcodeDisabled() + { + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - xcodeProject.AddSentryFramework(); + xcodeProject.AddSentryFramework(); - StringAssert.Contains("ENABLE_BITCODE = NO;", xcodeProject.ProjectToString()); - } + StringAssert.Contains("ENABLE_BITCODE = NO;", xcodeProject.ProjectToString()); + } - [Test] - public void AddSentryNativeBridges_FrameworkSearchPathAlreadySet_DoesNotGetOverwritten() - { - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void AddSentryNativeBridges_FrameworkSearchPathAlreadySet_DoesNotGetOverwritten() + { + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - xcodeProject.AddSentryNativeBridge(); + xcodeProject.AddSentryNativeBridge(); - StringAssert.Contains(SentryXcodeProject.BridgeName, xcodeProject.ProjectToString()); - } + StringAssert.Contains(SentryXcodeProject.BridgeName, xcodeProject.ProjectToString()); + } - [Test] - public void CreateNativeOptions_CleanXcodeProject_NativeOptionsAdded() - { - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void CreateNativeOptions_CleanXcodeProject_NativeOptionsAdded() + { + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - xcodeProject.AddNativeOptions(_fixture.Options, (_, _) => { }); + xcodeProject.AddNativeOptions(_fixture.Options, (_, _) => { }); - StringAssert.Contains(SentryXcodeProject.OptionsName, xcodeProject.ProjectToString()); - } + StringAssert.Contains(SentryXcodeProject.OptionsName, xcodeProject.ProjectToString()); + } - [Test] - public void AddBuildPhaseSymbolUpload_CleanXcodeProject_BuildPhaseSymbolUploadAdded() - { - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void AddBuildPhaseSymbolUpload_CleanXcodeProject_BuildPhaseSymbolUploadAdded() + { + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - var didContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase(); - xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); - var doesContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase(); + var didContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase(); + xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); + var doesContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase(); - Assert.IsFalse(didContainUploadPhase); - Assert.IsTrue(doesContainUploadPhase); - } + Assert.IsFalse(didContainUploadPhase); + Assert.IsTrue(doesContainUploadPhase); + } - [Test] - public void AddBuildPhaseSymbolUpload_PhaseAlreadyAdded_LogsAndDoesNotAddAgain() - { - const int expectedBuildPhaseOccurence = 1; - var xcodeProject = _fixture.GetSut(); - xcodeProject.ReadFromProjectFile(); + [Test] + public void AddBuildPhaseSymbolUpload_PhaseAlreadyAdded_LogsAndDoesNotAddAgain() + { + const int expectedBuildPhaseOccurence = 1; + var xcodeProject = _fixture.GetSut(); + xcodeProject.ReadFromProjectFile(); - xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); - xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); + xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); + xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); - var actualBuildPhaseOccurence = Regex.Matches(xcodeProject.ProjectToString(), - Regex.Escape(SentryXcodeProject.SymbolUploadPhaseName)).Count; + var actualBuildPhaseOccurence = Regex.Matches(xcodeProject.ProjectToString(), + Regex.Escape(SentryXcodeProject.SymbolUploadPhaseName)).Count; - Assert.IsTrue(_fixture.TestLogger.Logs.Any(log => - log.logLevel == SentryLevel.Debug && - log.message.Contains("already added."))); // Sanity check - Assert.AreEqual(expectedBuildPhaseOccurence, actualBuildPhaseOccurence); - } + Assert.IsTrue(_fixture.TestLogger.Logs.Any(log => + log.logLevel == SentryLevel.Debug && + log.message.Contains("already added."))); // Sanity check + Assert.AreEqual(expectedBuildPhaseOccurence, actualBuildPhaseOccurence); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/AnrDetectionTests.cs b/test/Sentry.Unity.Tests/AnrDetectionTests.cs index f8f0fb880..8d8629c18 100644 --- a/test/Sentry.Unity.Tests/AnrDetectionTests.cs +++ b/test/Sentry.Unity.Tests/AnrDetectionTests.cs @@ -8,139 +8,138 @@ using Sentry.Extensibility; using Sentry.Unity.Tests.SharedClasses; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class AnrDetectionTests { - public class AnrDetectionTests + private readonly TimeSpan _timeout = TimeSpan.FromSeconds(0.5); + private readonly IDiagnosticLogger _logger = new TestLogger(forwardToUnityLog: true); + private GameObject _gameObject = null!; + private SentryMonoBehaviour _monoBehaviour = null!; + private AnrWatchDog _sut = null!; + + [SetUp] + public void SetUp() { - private readonly TimeSpan _timeout = TimeSpan.FromSeconds(0.5); - private readonly IDiagnosticLogger _logger = new TestLogger(forwardToUnityLog: true); - private GameObject _gameObject = null!; - private SentryMonoBehaviour _monoBehaviour = null!; - private AnrWatchDog _sut = null!; - - [SetUp] - public void SetUp() - { - _gameObject = new GameObject("Tests"); - _monoBehaviour = _gameObject.AddComponent(); - } + _gameObject = new GameObject("Tests"); + _monoBehaviour = _gameObject.AddComponent(); + } - [TearDown] - public void TearDown() => _sut.Stop(wait: true); + [TearDown] + public void TearDown() => _sut.Stop(wait: true); - private AnrWatchDog CreateWatchDog(bool multiThreaded) - { - UnityEngine.Debug.Log($"Preparing ANR watchdog: timeout={_timeout} multiThreaded={multiThreaded}"); - return multiThreaded - ? new AnrWatchDogMultiThreaded(_logger, _monoBehaviour, _timeout) - : new AnrWatchDogSingleThreaded(_logger, _monoBehaviour, _timeout); - } + private AnrWatchDog CreateWatchDog(bool multiThreaded) + { + UnityEngine.Debug.Log($"Preparing ANR watchdog: timeout={_timeout} multiThreaded={multiThreaded}"); + return multiThreaded + ? new AnrWatchDogMultiThreaded(_logger, _monoBehaviour, _timeout) + : new AnrWatchDogSingleThreaded(_logger, _monoBehaviour, _timeout); + } - // Needed for [UnityTest] - IEnumerator return value - // https://docs.unity3d.com/Packages/com.unity.test-framework@2.0/manual/reference-tests-parameterized.html - private static bool[] MultiThreadingTestValues = { true, false }; + // Needed for [UnityTest] - IEnumerator return value + // https://docs.unity3d.com/Packages/com.unity.test-framework@2.0/manual/reference-tests-parameterized.html + private static bool[] MultiThreadingTestValues = { true, false }; - [UnityTest] - public IEnumerator DetectsStuckUI([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded) - { - ApplicationNotResponding? arn = null; - _sut = CreateWatchDog(multiThreaded); - _sut.OnApplicationNotResponding += (_, e) => arn = e; + [UnityTest] + public IEnumerator DetectsStuckUI([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded) + { + ApplicationNotResponding? arn = null; + _sut = CreateWatchDog(multiThreaded); + _sut.OnApplicationNotResponding += (_, e) => arn = e; - // Thread.Sleep blocks the UI thread - var watch = Stopwatch.StartNew(); - while (watch.Elapsed < TimeSpan.FromTicks(_timeout.Ticks * 2) && arn is null) - { - Thread.Sleep(10); - } + // Thread.Sleep blocks the UI thread + var watch = Stopwatch.StartNew(); + while (watch.Elapsed < TimeSpan.FromTicks(_timeout.Ticks * 2) && arn is null) + { + Thread.Sleep(10); + } - // We need to let the single-threaded watchdog populate `arn` after UI became responsive again. - if (!multiThreaded) + // We need to let the single-threaded watchdog populate `arn` after UI became responsive again. + if (!multiThreaded) + { + watch.Restart(); + while (watch.Elapsed < _timeout && arn is null) { - watch.Restart(); - while (watch.Elapsed < _timeout && arn is null) - { - yield return null; - } + yield return null; } - - Assert.IsNotNull(arn); - Assert.That(arn!.Message, Does.StartWith("Application not responding ")); } - [UnityTest] - public IEnumerator DoesntReportWorkingUI([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded) - { - ApplicationNotResponding? arn = null; - _sut = CreateWatchDog(multiThreaded); - _sut.OnApplicationNotResponding += (_, e) => arn = e; + Assert.IsNotNull(arn); + Assert.That(arn!.Message, Does.StartWith("Application not responding ")); + } - // yield WaitForSeconds doesn't block the UI thread - var watch = Stopwatch.StartNew(); - while (watch.Elapsed < TimeSpan.FromTicks(_timeout.Ticks * 3)) - { - yield return new WaitForSeconds(0.01f); - } + [UnityTest] + public IEnumerator DoesntReportWorkingUI([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded) + { + ApplicationNotResponding? arn = null; + _sut = CreateWatchDog(multiThreaded); + _sut.OnApplicationNotResponding += (_, e) => arn = e; - Assert.IsNull(arn); + // yield WaitForSeconds doesn't block the UI thread + var watch = Stopwatch.StartNew(); + while (watch.Elapsed < TimeSpan.FromTicks(_timeout.Ticks * 3)) + { + yield return new WaitForSeconds(0.01f); } - [Test] - [TestCase(true)] - [TestCase(false)] - public void DoesntReportShortlyStuckUI(bool multiThreaded) - { - ApplicationNotResponding? arn = null; - _sut = CreateWatchDog(multiThreaded); - _sut.OnApplicationNotResponding += (_, e) => arn = e; + Assert.IsNull(arn); + } - // Thread.Sleep blocks the UI thread - Thread.Sleep(TimeSpan.FromTicks(_timeout.Ticks / 2)); + [Test] + [TestCase(true)] + [TestCase(false)] + public void DoesntReportShortlyStuckUI(bool multiThreaded) + { + ApplicationNotResponding? arn = null; + _sut = CreateWatchDog(multiThreaded); + _sut.OnApplicationNotResponding += (_, e) => arn = e; - Assert.IsNull(arn); - } + // Thread.Sleep blocks the UI thread + Thread.Sleep(TimeSpan.FromTicks(_timeout.Ticks / 2)); - [UnityTest] - public IEnumerator DoesntReportWhilePaused([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded) - { - ApplicationNotResponding? arn = null; - _sut = CreateWatchDog(multiThreaded); - _sut.OnApplicationNotResponding += (_, e) => arn = e; + Assert.IsNull(arn); + } - // mark the app as paused - _monoBehaviour.UpdatePauseStatus(true); + [UnityTest] + public IEnumerator DoesntReportWhilePaused([ValueSource(nameof(MultiThreadingTestValues))] bool multiThreaded) + { + ApplicationNotResponding? arn = null; + _sut = CreateWatchDog(multiThreaded); + _sut.OnApplicationNotResponding += (_, e) => arn = e; + + // mark the app as paused + _monoBehaviour.UpdatePauseStatus(true); - // Thread.Sleep blocks the UI thread - Thread.Sleep(TimeSpan.FromTicks(_timeout.Ticks * 2)); + // Thread.Sleep blocks the UI thread + Thread.Sleep(TimeSpan.FromTicks(_timeout.Ticks * 2)); - // We need to let the single-threaded watchdog populate `arn` after UI became responsive again. - if (!multiThreaded) + // We need to let the single-threaded watchdog populate `arn` after UI became responsive again. + if (!multiThreaded) + { + var watch = Stopwatch.StartNew(); + while (watch.Elapsed < _timeout && arn is null) { - var watch = Stopwatch.StartNew(); - while (watch.Elapsed < _timeout && arn is null) - { - yield return null; - } + yield return null; } + } - // mark as resumed - _monoBehaviour.UpdatePauseStatus(false); + // mark as resumed + _monoBehaviour.UpdatePauseStatus(false); - Assert.IsNull(arn); - } + Assert.IsNull(arn); + } - [UnityTest] - public IEnumerator IsNotAffectedByTimeScale() - { - ApplicationNotResponding? anr = null; - _sut = CreateWatchDog(true); - _sut.OnApplicationNotResponding += (_, e) => anr = e; + [UnityTest] + public IEnumerator IsNotAffectedByTimeScale() + { + ApplicationNotResponding? anr = null; + _sut = CreateWatchDog(true); + _sut.OnApplicationNotResponding += (_, e) => anr = e; - Time.timeScale = 0.0f; - yield return new WaitForSecondsRealtime((float)_timeout.TotalSeconds * 2); - Time.timeScale = 1.0f; + Time.timeScale = 0.0f; + yield return new WaitForSecondsRealtime((float)_timeout.TotalSeconds * 2); + Time.timeScale = 1.0f; - Assert.IsNull(anr); - } + Assert.IsNull(anr); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index f26e70235..830b08775 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -4,230 +4,228 @@ using Sentry.Unity.Tests.SharedClasses; using Sentry.Unity.Tests.Stubs; using UnityEngine; -using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class ContextWriterTests { - public sealed class ContextWriterTests + private GameObject _gameObject = null!; + private SentryMonoBehaviour _sentryMonoBehaviour = null!; + private TestApplication _testApplication = null!; + + [SetUp] + public void SetUp() { - private GameObject _gameObject = null!; - private SentryMonoBehaviour _sentryMonoBehaviour = null!; - private TestApplication _testApplication = null!; + _gameObject = new GameObject("ContextWriterTest"); + _sentryMonoBehaviour = _gameObject.AddComponent(); + _testApplication = new(); + } - [SetUp] - public void SetUp() - { - _gameObject = new GameObject("ContextWriterTest"); - _sentryMonoBehaviour = _gameObject.AddComponent(); - _testApplication = new(); - } + [TearDown] + public void TearDown() + { + UnityEngine.Object.Destroy(_gameObject); + } - [TearDown] - public void TearDown() + [Test] + public void Arguments() + { + // arrange + var sysInfo = _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - UnityEngine.Object.Destroy(_gameObject); - } + OperatingSystem = "OperatingSystem", + ProcessorCount = 1, + SupportsVibration = true, + DeviceType = new(() => "DeviceType"), + CpuDescription = "CpuDescription", + DeviceName = "DeviceName", + DeviceUniqueIdentifier = new Lazy(() => "DeviceUniqueIdentifier"), + DeviceModel = new(() => "DeviceModel"), + SystemMemorySize = 2, + GraphicsDeviceId = 3, + GraphicsDeviceName = "GraphicsDeviceName", + GraphicsDeviceVendorId = new(() => "GraphicsDeviceVendorId"), + GraphicsDeviceVendor = "GraphicsDeviceVendor", + GraphicsMemorySize = 4, + GraphicsMultiThreaded = new(() => true), + NpotSupport = "NpotSupport", + GraphicsDeviceVersion = "GraphicsDeviceVersion", + GraphicsDeviceType = "GraphicsDeviceType", + MaxTextureSize = 5, + SupportsDrawCallInstancing = true, + SupportsRayTracing = false, + SupportsComputeShaders = true, + SupportsGeometryShaders = false, + GraphicsShaderLevel = 6, + IsDebugBuild = new(() => true), + InstallMode = "InstallMode", + TargetFrameRate = new(() => "TargetFrameRate"), + CopyTextureSupport = new(() => "CopyTextureSupport"), + RenderingThreadingMode = new(() => "RenderingThreadingMode"), + StartTime = new(() => DateTimeOffset.UtcNow), - [Test] - public void Arguments() + }; + var context = new MockContextWriter(); + var options = new SentryUnityOptions(_sentryMonoBehaviour, _testApplication, false) { - // arrange - var sysInfo = _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - OperatingSystem = "OperatingSystem", - ProcessorCount = 1, - SupportsVibration = true, - DeviceType = new(() => "DeviceType"), - CpuDescription = "CpuDescription", - DeviceName = "DeviceName", - DeviceUniqueIdentifier = new Lazy(() => "DeviceUniqueIdentifier"), - DeviceModel = new(() => "DeviceModel"), - SystemMemorySize = 2, - GraphicsDeviceId = 3, - GraphicsDeviceName = "GraphicsDeviceName", - GraphicsDeviceVendorId = new(() => "GraphicsDeviceVendorId"), - GraphicsDeviceVendor = "GraphicsDeviceVendor", - GraphicsMemorySize = 4, - GraphicsMultiThreaded = new(() => true), - NpotSupport = "NpotSupport", - GraphicsDeviceVersion = "GraphicsDeviceVersion", - GraphicsDeviceType = "GraphicsDeviceType", - MaxTextureSize = 5, - SupportsDrawCallInstancing = true, - SupportsRayTracing = false, - SupportsComputeShaders = true, - SupportsGeometryShaders = false, - GraphicsShaderLevel = 6, - IsDebugBuild = new(() => true), - InstallMode = "InstallMode", - TargetFrameRate = new(() => "TargetFrameRate"), - CopyTextureSupport = new(() => "CopyTextureSupport"), - RenderingThreadingMode = new(() => "RenderingThreadingMode"), - StartTime = new(() => DateTimeOffset.UtcNow), + Dsn = "http://publickey@localhost/12345", + Enabled = true, + AttachStacktrace = true, + SendDefaultPii = true, + Debug = true, + DiagnosticLogger = new TestLogger(), + NativeContextWriter = context, + }; - }; - var context = new MockContextWriter(); - var options = new SentryUnityOptions(_sentryMonoBehaviour, _testApplication, false) - { - Dsn = "http://publickey@localhost/12345", - Enabled = true, - AttachStacktrace = true, - SendDefaultPii = true, - Debug = true, - DiagnosticLogger = new TestLogger(), - NativeContextWriter = context, - }; + // act + _sentryMonoBehaviour.CollectData(); + SentryUnity.Init(options); + Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); - // act - _sentryMonoBehaviour.CollectData(); - SentryUnity.Init(options); - Assert.IsTrue(context.SyncFinished.WaitOne(TimeSpan.FromSeconds(10))); - - // assert - Assert.AreEqual(sysInfo.StartTime?.Value.ToString("o"), context.AppStartTime); - Assert.AreEqual("debug", context.AppBuildType); - Assert.AreEqual(sysInfo.OperatingSystem, context.OperatingSystemRawDescription); - Assert.AreEqual(sysInfo.ProcessorCount, context.DeviceProcessorCount); - Assert.AreEqual(sysInfo.CpuDescription, context.DeviceCpuDescription); - Assert.AreEqual(TimeZoneInfo.Local.Id, context.DeviceTimezone); - Assert.AreEqual(sysInfo.SupportsVibration, context.DeviceSupportsVibration); - Assert.AreEqual(sysInfo.DeviceName, context.DeviceName); - Assert.AreEqual(_testApplication.IsEditor, context.DeviceSimulator); - Assert.AreEqual(sysInfo.DeviceUniqueIdentifier?.Value, context.DeviceDeviceUniqueIdentifier); - Assert.AreEqual(sysInfo.DeviceType?.Value, context.DeviceDeviceType); - Assert.AreEqual(sysInfo.DeviceModel?.Value, context.DeviceModel); - Assert.AreEqual(sysInfo.SystemMemorySize * 1048576L, context.DeviceMemorySize); - Assert.AreEqual(sysInfo.GraphicsDeviceId, context.GpuId); - Assert.AreEqual(sysInfo.GraphicsDeviceName, context.GpuName); - Assert.AreEqual(sysInfo.GraphicsDeviceVendor, context.GpuVendorName); - Assert.AreEqual(sysInfo.GraphicsMemorySize, context.GpuMemorySize); - Assert.AreEqual(sysInfo.NpotSupport, context.GpuNpotSupport); - Assert.AreEqual(sysInfo.GraphicsDeviceVersion, context.GpuVersion); - Assert.AreEqual(sysInfo.GraphicsDeviceType, context.GpuApiType); - Assert.AreEqual(sysInfo.MaxTextureSize, context.GpuMaxTextureSize); - Assert.AreEqual(sysInfo.SupportsDrawCallInstancing, context.GpuSupportsDrawCallInstancing); - Assert.AreEqual(sysInfo.SupportsRayTracing, context.GpuSupportsRayTracing); - Assert.AreEqual(sysInfo.SupportsComputeShaders, context.GpuSupportsComputeShaders); - Assert.AreEqual(sysInfo.SupportsGeometryShaders, context.GpuSupportsGeometryShaders); - Assert.AreEqual(sysInfo.GraphicsDeviceVendorId?.Value, context.GpuVendorId); - Assert.AreEqual(sysInfo.GraphicsMultiThreaded?.Value, context.GpuMultiThreadedRendering); - Assert.AreEqual(sysInfo.GraphicsShaderLevel.ToString(), context.GpuGraphicsShaderLevel); - Assert.AreEqual(sysInfo.EditorVersion, context.UnityEditorVersion); - Assert.AreEqual(sysInfo.InstallMode, context.UnityInstallMode); - Assert.AreEqual(sysInfo.TargetFrameRate?.Value, context.UnityTargetFrameRate); - Assert.AreEqual(sysInfo.CopyTextureSupport?.Value, context.UnityCopyTextureSupport); - Assert.AreEqual(sysInfo.RenderingThreadingMode?.Value, context.UnityRenderingThreadingMode); - } + // assert + Assert.AreEqual(sysInfo.StartTime?.Value.ToString("o"), context.AppStartTime); + Assert.AreEqual("debug", context.AppBuildType); + Assert.AreEqual(sysInfo.OperatingSystem, context.OperatingSystemRawDescription); + Assert.AreEqual(sysInfo.ProcessorCount, context.DeviceProcessorCount); + Assert.AreEqual(sysInfo.CpuDescription, context.DeviceCpuDescription); + Assert.AreEqual(TimeZoneInfo.Local.Id, context.DeviceTimezone); + Assert.AreEqual(sysInfo.SupportsVibration, context.DeviceSupportsVibration); + Assert.AreEqual(sysInfo.DeviceName, context.DeviceName); + Assert.AreEqual(_testApplication.IsEditor, context.DeviceSimulator); + Assert.AreEqual(sysInfo.DeviceUniqueIdentifier?.Value, context.DeviceDeviceUniqueIdentifier); + Assert.AreEqual(sysInfo.DeviceType?.Value, context.DeviceDeviceType); + Assert.AreEqual(sysInfo.DeviceModel?.Value, context.DeviceModel); + Assert.AreEqual(sysInfo.SystemMemorySize * 1048576L, context.DeviceMemorySize); + Assert.AreEqual(sysInfo.GraphicsDeviceId, context.GpuId); + Assert.AreEqual(sysInfo.GraphicsDeviceName, context.GpuName); + Assert.AreEqual(sysInfo.GraphicsDeviceVendor, context.GpuVendorName); + Assert.AreEqual(sysInfo.GraphicsMemorySize, context.GpuMemorySize); + Assert.AreEqual(sysInfo.NpotSupport, context.GpuNpotSupport); + Assert.AreEqual(sysInfo.GraphicsDeviceVersion, context.GpuVersion); + Assert.AreEqual(sysInfo.GraphicsDeviceType, context.GpuApiType); + Assert.AreEqual(sysInfo.MaxTextureSize, context.GpuMaxTextureSize); + Assert.AreEqual(sysInfo.SupportsDrawCallInstancing, context.GpuSupportsDrawCallInstancing); + Assert.AreEqual(sysInfo.SupportsRayTracing, context.GpuSupportsRayTracing); + Assert.AreEqual(sysInfo.SupportsComputeShaders, context.GpuSupportsComputeShaders); + Assert.AreEqual(sysInfo.SupportsGeometryShaders, context.GpuSupportsGeometryShaders); + Assert.AreEqual(sysInfo.GraphicsDeviceVendorId?.Value, context.GpuVendorId); + Assert.AreEqual(sysInfo.GraphicsMultiThreaded?.Value, context.GpuMultiThreadedRendering); + Assert.AreEqual(sysInfo.GraphicsShaderLevel.ToString(), context.GpuGraphicsShaderLevel); + Assert.AreEqual(sysInfo.EditorVersion, context.UnityEditorVersion); + Assert.AreEqual(sysInfo.InstallMode, context.UnityInstallMode); + Assert.AreEqual(sysInfo.TargetFrameRate?.Value, context.UnityTargetFrameRate); + Assert.AreEqual(sysInfo.CopyTextureSupport?.Value, context.UnityCopyTextureSupport); + Assert.AreEqual(sysInfo.RenderingThreadingMode?.Value, context.UnityRenderingThreadingMode); } +} - internal sealed class MockContextWriter : ContextWriter - { - public AutoResetEvent SyncFinished = new AutoResetEvent(false); +internal sealed class MockContextWriter : ContextWriter +{ + public AutoResetEvent SyncFinished = new AutoResetEvent(false); - public string? AppStartTime = null; - public string? AppBuildType = null; - public string? OperatingSystemRawDescription = null; - public int? DeviceProcessorCount = null; - public string? DeviceCpuDescription = null; - public string? DeviceTimezone = null; - public bool? DeviceSupportsVibration = null; - public string? DeviceName = null; - public bool? DeviceSimulator = null; - public string? DeviceDeviceUniqueIdentifier = null; - public string? DeviceDeviceType = null; - public string? DeviceModel = null; - public long? DeviceMemorySize = null; - public int? GpuId = null; - public string? GpuName = null; - public string? GpuVendorName = null; - public int? GpuMemorySize = null; - public string? GpuNpotSupport = null; - public string? GpuVersion = null; - public string? GpuApiType = null; - public int? GpuMaxTextureSize = null; - public bool? GpuSupportsDrawCallInstancing = null; - public bool? GpuSupportsRayTracing = null; - public bool? GpuSupportsComputeShaders = null; - public bool? GpuSupportsGeometryShaders = null; - public string? GpuVendorId = null; - public bool? GpuMultiThreadedRendering = null; - public string? GpuGraphicsShaderLevel = null; - public string? UnityEditorVersion = null; - public string? UnityInstallMode = null; - public string? UnityTargetFrameRate = null; - public string? UnityCopyTextureSupport = null; - public string? UnityRenderingThreadingMode = null; + public string? AppStartTime = null; + public string? AppBuildType = null; + public string? OperatingSystemRawDescription = null; + public int? DeviceProcessorCount = null; + public string? DeviceCpuDescription = null; + public string? DeviceTimezone = null; + public bool? DeviceSupportsVibration = null; + public string? DeviceName = null; + public bool? DeviceSimulator = null; + public string? DeviceDeviceUniqueIdentifier = null; + public string? DeviceDeviceType = null; + public string? DeviceModel = null; + public long? DeviceMemorySize = null; + public int? GpuId = null; + public string? GpuName = null; + public string? GpuVendorName = null; + public int? GpuMemorySize = null; + public string? GpuNpotSupport = null; + public string? GpuVersion = null; + public string? GpuApiType = null; + public int? GpuMaxTextureSize = null; + public bool? GpuSupportsDrawCallInstancing = null; + public bool? GpuSupportsRayTracing = null; + public bool? GpuSupportsComputeShaders = null; + public bool? GpuSupportsGeometryShaders = null; + public string? GpuVendorId = null; + public bool? GpuMultiThreadedRendering = null; + public string? GpuGraphicsShaderLevel = null; + public string? UnityEditorVersion = null; + public string? UnityInstallMode = null; + public string? UnityTargetFrameRate = null; + public string? UnityCopyTextureSupport = null; + public string? UnityRenderingThreadingMode = null; - protected override void WriteScope( - string? AppStartTime, - string? AppBuildType, - string? OperatingSystemRawDescription, - int? DeviceProcessorCount, - string? DeviceCpuDescription, - string? DeviceTimezone, - bool? DeviceSupportsVibration, - string? DeviceName, - bool? DeviceSimulator, - string? DeviceDeviceUniqueIdentifier, - string? DeviceDeviceType, - string? DeviceModel, - long? DeviceMemorySize, - int? GpuId, - string? GpuName, - string? GpuVendorName, - int? GpuMemorySize, - string? GpuNpotSupport, - string? GpuVersion, - string? GpuApiType, - int? GpuMaxTextureSize, - bool? GpuSupportsDrawCallInstancing, - bool? GpuSupportsRayTracing, - bool? GpuSupportsComputeShaders, - bool? GpuSupportsGeometryShaders, - string? GpuVendorId, - bool? GpuMultiThreadedRendering, - string? GpuGraphicsShaderLevel, - string? UnityEditorVersion, - string? UnityInstallMode, - string? UnityTargetFrameRate, - string? UnityCopyTextureSupport, - string? UnityRenderingThreadingMode - ) - { - this.AppStartTime = AppStartTime; - this.AppBuildType = AppBuildType; - this.OperatingSystemRawDescription = OperatingSystemRawDescription; - this.DeviceProcessorCount = DeviceProcessorCount; - this.DeviceCpuDescription = DeviceCpuDescription; - this.DeviceTimezone = DeviceTimezone; - this.DeviceSupportsVibration = DeviceSupportsVibration; - this.DeviceName = DeviceName; - this.DeviceSimulator = DeviceSimulator; - this.DeviceDeviceUniqueIdentifier = DeviceDeviceUniqueIdentifier; - this.DeviceDeviceType = DeviceDeviceType; - this.DeviceModel = DeviceModel; - this.DeviceMemorySize = DeviceMemorySize; - this.GpuId = GpuId; - this.GpuName = GpuName; - this.GpuVendorName = GpuVendorName; - this.GpuMemorySize = GpuMemorySize; - this.GpuNpotSupport = GpuNpotSupport; - this.GpuVersion = GpuVersion; - this.GpuApiType = GpuApiType; - this.GpuMaxTextureSize = GpuMaxTextureSize; - this.GpuSupportsDrawCallInstancing = GpuSupportsDrawCallInstancing; - this.GpuSupportsRayTracing = GpuSupportsRayTracing; - this.GpuSupportsComputeShaders = GpuSupportsComputeShaders; - this.GpuSupportsGeometryShaders = GpuSupportsGeometryShaders; - this.GpuVendorId = GpuVendorId; - this.GpuMultiThreadedRendering = GpuMultiThreadedRendering; - this.GpuGraphicsShaderLevel = GpuGraphicsShaderLevel; - this.UnityEditorVersion = UnityEditorVersion; - this.UnityInstallMode = UnityInstallMode; - this.UnityTargetFrameRate = UnityTargetFrameRate; - this.UnityCopyTextureSupport = UnityCopyTextureSupport; - this.UnityRenderingThreadingMode = UnityRenderingThreadingMode; - SyncFinished.Set(); - } + protected override void WriteScope( + string? AppStartTime, + string? AppBuildType, + string? OperatingSystemRawDescription, + int? DeviceProcessorCount, + string? DeviceCpuDescription, + string? DeviceTimezone, + bool? DeviceSupportsVibration, + string? DeviceName, + bool? DeviceSimulator, + string? DeviceDeviceUniqueIdentifier, + string? DeviceDeviceType, + string? DeviceModel, + long? DeviceMemorySize, + int? GpuId, + string? GpuName, + string? GpuVendorName, + int? GpuMemorySize, + string? GpuNpotSupport, + string? GpuVersion, + string? GpuApiType, + int? GpuMaxTextureSize, + bool? GpuSupportsDrawCallInstancing, + bool? GpuSupportsRayTracing, + bool? GpuSupportsComputeShaders, + bool? GpuSupportsGeometryShaders, + string? GpuVendorId, + bool? GpuMultiThreadedRendering, + string? GpuGraphicsShaderLevel, + string? UnityEditorVersion, + string? UnityInstallMode, + string? UnityTargetFrameRate, + string? UnityCopyTextureSupport, + string? UnityRenderingThreadingMode + ) + { + this.AppStartTime = AppStartTime; + this.AppBuildType = AppBuildType; + this.OperatingSystemRawDescription = OperatingSystemRawDescription; + this.DeviceProcessorCount = DeviceProcessorCount; + this.DeviceCpuDescription = DeviceCpuDescription; + this.DeviceTimezone = DeviceTimezone; + this.DeviceSupportsVibration = DeviceSupportsVibration; + this.DeviceName = DeviceName; + this.DeviceSimulator = DeviceSimulator; + this.DeviceDeviceUniqueIdentifier = DeviceDeviceUniqueIdentifier; + this.DeviceDeviceType = DeviceDeviceType; + this.DeviceModel = DeviceModel; + this.DeviceMemorySize = DeviceMemorySize; + this.GpuId = GpuId; + this.GpuName = GpuName; + this.GpuVendorName = GpuVendorName; + this.GpuMemorySize = GpuMemorySize; + this.GpuNpotSupport = GpuNpotSupport; + this.GpuVersion = GpuVersion; + this.GpuApiType = GpuApiType; + this.GpuMaxTextureSize = GpuMaxTextureSize; + this.GpuSupportsDrawCallInstancing = GpuSupportsDrawCallInstancing; + this.GpuSupportsRayTracing = GpuSupportsRayTracing; + this.GpuSupportsComputeShaders = GpuSupportsComputeShaders; + this.GpuSupportsGeometryShaders = GpuSupportsGeometryShaders; + this.GpuVendorId = GpuVendorId; + this.GpuMultiThreadedRendering = GpuMultiThreadedRendering; + this.GpuGraphicsShaderLevel = GpuGraphicsShaderLevel; + this.UnityEditorVersion = UnityEditorVersion; + this.UnityInstallMode = UnityInstallMode; + this.UnityTargetFrameRate = UnityTargetFrameRate; + this.UnityCopyTextureSupport = UnityCopyTextureSupport; + this.UnityRenderingThreadingMode = UnityRenderingThreadingMode; + SyncFinished.Set(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/DebouncerTests.cs b/test/Sentry.Unity.Tests/DebouncerTests.cs index c6a48a319..812a27685 100644 --- a/test/Sentry.Unity.Tests/DebouncerTests.cs +++ b/test/Sentry.Unity.Tests/DebouncerTests.cs @@ -4,55 +4,54 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +/// +/// Testing debouncer in realtime. +/// +public sealed class DebouncerTests { - /// - /// Testing debouncer in realtime. - /// - public sealed class DebouncerTests - { - private readonly TimeSpan DefaultOffset = TimeSpan.FromMilliseconds(100); + private readonly TimeSpan DefaultOffset = TimeSpan.FromMilliseconds(100); - [UnityTest] - public IEnumerator LogTimeDebounce() - { - Assert.Inconclusive("Flaky"); // Ignoring because of flakiness: https://github.com/getsentry/sentry-unity/issues/335 - yield return AssertDefaultDebounce(new LogTimeDebounce(DefaultOffset)); - } + [UnityTest] + public IEnumerator LogTimeDebounce() + { + Assert.Inconclusive("Flaky"); // Ignoring because of flakiness: https://github.com/getsentry/sentry-unity/issues/335 + yield return AssertDefaultDebounce(new LogTimeDebounce(DefaultOffset)); + } - [UnityTest] - public IEnumerator ErrorTimeDebounce() - { - Assert.Inconclusive("Flaky"); // Ignoring because of flakiness: https://github.com/getsentry/sentry-unity/issues/335 - yield return AssertDefaultDebounce(new ErrorTimeDebounce(DefaultOffset)); - } + [UnityTest] + public IEnumerator ErrorTimeDebounce() + { + Assert.Inconclusive("Flaky"); // Ignoring because of flakiness: https://github.com/getsentry/sentry-unity/issues/335 + yield return AssertDefaultDebounce(new ErrorTimeDebounce(DefaultOffset)); + } - [UnityTest] - public IEnumerator WarningTimeDebounce() - { - Assert.Inconclusive("Flaky"); // Ignoring because of flakiness: https://github.com/getsentry/sentry-unity/issues/335 - yield return AssertDefaultDebounce(new WarningTimeDebounce(DefaultOffset)); - } + [UnityTest] + public IEnumerator WarningTimeDebounce() + { + Assert.Inconclusive("Flaky"); // Ignoring because of flakiness: https://github.com/getsentry/sentry-unity/issues/335 + yield return AssertDefaultDebounce(new WarningTimeDebounce(DefaultOffset)); + } - private IEnumerator AssertDefaultDebounce(TimeDebounceBase debouncer) - { - // pass - Assert.IsTrue(debouncer.Debounced()); + private IEnumerator AssertDefaultDebounce(TimeDebounceBase debouncer) + { + // pass + Assert.IsTrue(debouncer.Debounced()); - yield return new WaitForSeconds(0.050f); + yield return new WaitForSeconds(0.050f); - // skip - Assert.IsFalse(debouncer.Debounced()); + // skip + Assert.IsFalse(debouncer.Debounced()); - yield return new WaitForSeconds(0.02f); + yield return new WaitForSeconds(0.02f); - // skip - Assert.IsFalse(debouncer.Debounced()); + // skip + Assert.IsFalse(debouncer.Debounced()); - yield return new WaitForSeconds(0.04f); + yield return new WaitForSeconds(0.04f); - // pass - Assert.IsTrue(debouncer.Debounced()); - } + // pass + Assert.IsTrue(debouncer.Debounced()); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/IntegrationTests.cs b/test/Sentry.Unity.Tests/IntegrationTests.cs index 0757b10bf..b650ccf19 100644 --- a/test/Sentry.Unity.Tests/IntegrationTests.cs +++ b/test/Sentry.Unity.Tests/IntegrationTests.cs @@ -8,339 +8,338 @@ using UnityEngine.SceneManagement; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class IntegrationTests { - public sealed class IntegrationTests - { - private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup - private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(1); + private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup + private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(1); - private string _eventMessage = null!; // Set in setup - private string _identifyingEventValueAttribute = null!; // Set in setup + private string _eventMessage = null!; // Set in setup + private string _identifyingEventValueAttribute = null!; // Set in setup - [SetUp] - public void SetUp() - { - _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); - _eventMessage = Guid.NewGuid() + " Test Event"; - _identifyingEventValueAttribute = CreateAttribute("value", _eventMessage); - } + [SetUp] + public void SetUp() + { + _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); + _eventMessage = Guid.NewGuid() + " Test Event"; + _identifyingEventValueAttribute = CreateAttribute("value", _eventMessage); + } - [TearDown] - public void TearDown() + [TearDown] + public void TearDown() + { + if (SentrySdk.IsEnabled) { - if (SentrySdk.IsEnabled) - { - SentryUnity.Close(); - } + SentryUnity.Close(); } + } - [UnityTest] - public IEnumerator ThrowException_EventContainingMessageGetsCaptured() - { - yield return SetupSceneCoroutine("1_BugFarm"); + [UnityTest] + public IEnumerator ThrowException_EventContainingMessageGetsCaptured() + { + yield return SetupSceneCoroutine("1_BugFarm"); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - // We don't want to call testBehaviour.TestException(); because it won't go via Sentry infra. - // We don't have it in tests, but in scenes. - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + // We don't want to call testBehaviour.TestException(); because it won't go via Sentry infra. + // We don't have it in tests, but in scenes. + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); - } + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); + } - [UnityTest] - public IEnumerator ThrowException_EventIncludesApplicationProductNameAtVersionAsRelease() - { - yield return SetupSceneCoroutine("1_BugFarm"); + [UnityTest] + public IEnumerator ThrowException_EventIncludesApplicationProductNameAtVersionAsRelease() + { + yield return SetupSceneCoroutine("1_BugFarm"); - var expectedAttribute = CreateAttribute("release", Application.productName + "@" + Application.version); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + var expectedAttribute = CreateAttribute("release", Application.productName + "@" + Application.version); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } + + [UnityTest] + public IEnumerator ThrowException_CustomReleaseSet_EventIncludesCustomRelease() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator ThrowException_CustomReleaseSet_EventIncludesCustomRelease() + var customRelease = "CustomRelease"; + var expectedAttribute = CreateAttribute("release", customRelease); + using var _ = InitSentrySdk(o => { - yield return SetupSceneCoroutine("1_BugFarm"); + o.Release = customRelease; + }); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var customRelease = "CustomRelease"; - var expectedAttribute = CreateAttribute("release", customRelease); - using var _ = InitSentrySdk(o => - { - o.Release = customRelease; - }); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator ThrowException_ProductNameWhitespace_EventIncludesApplicationVersionAsRelease() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator ThrowException_ProductNameWhitespace_EventIncludesApplicationVersionAsRelease() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var originalProductName = PlayerSettings.productName; + PlayerSettings.productName = " "; + var expectedAttribute = CreateAttribute("release", Application.version); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var originalProductName = PlayerSettings.productName; - PlayerSettings.productName = " "; - var expectedAttribute = CreateAttribute("release", Application.version); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + PlayerSettings.productName = originalProductName; + } - PlayerSettings.productName = originalProductName; - } + [UnityTest] + public IEnumerator ThrowException_ProductNameEmpty_EventIncludesApplicationVersionAsRelease() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator ThrowException_ProductNameEmpty_EventIncludesApplicationVersionAsRelease() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var originalProductName = PlayerSettings.productName; + PlayerSettings.productName = null; + var expectedAttribute = CreateAttribute("release", Application.version); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var originalProductName = PlayerSettings.productName; - PlayerSettings.productName = null; - var expectedAttribute = CreateAttribute("release", Application.version); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + PlayerSettings.productName = originalProductName; + } - PlayerSettings.productName = originalProductName; - } + [UnityTest] + public IEnumerator ThrowException_EditorTest_EventIncludesEditorAsEnvironment() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator ThrowException_EditorTest_EventIncludesEditorAsEnvironment() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var expectedAttribute = CreateAttribute("environment", "editor"); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var expectedAttribute = CreateAttribute("environment", "editor"); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator ThrowException_SendDefaultPiiIsTrue_EventIncludesEnvironmentUserNameAsUserName() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator ThrowException_SendDefaultPiiIsTrue_EventIncludesEnvironmentUserNameAsUserName() + var expectedAttribute = CreateAttribute("username", Environment.UserName); + using var _ = InitSentrySdk(o => { - yield return SetupSceneCoroutine("1_BugFarm"); + o.SendDefaultPii = true; + o.IsEnvironmentUser = true; + }); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var expectedAttribute = CreateAttribute("username", Environment.UserName); - using var _ = InitSentrySdk(o => - { - o.SendDefaultPii = true; - o.IsEnvironmentUser = true; - }); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator ThrowException_SendDefaultPiiIsFalse_EventDoesNotIncludeEnvironmentUserNameAsUserName() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator ThrowException_SendDefaultPiiIsFalse_EventDoesNotIncludeEnvironmentUserNameAsUserName() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var expectedAttribute = CreateAttribute("username", Environment.UserName); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var expectedAttribute = CreateAttribute("username", Environment.UserName); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Not.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Not.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator BugFarmScene_MultipleSentryInit_SendEventForTheLatest() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator BugFarmScene_MultipleSentryInit_SendEventForTheLatest() + var firstHttpClientHandler = new TestHttpClientHandler("NotSupposedToBeCalled_TestHttpClientHandler"); + using var firstDisposable = InitSentrySdk(o => { - yield return SetupSceneCoroutine("1_BugFarm"); + o.Dsn = "http://publickey@localhost:8000/12345"; + o.CreateHttpMessageHandler = () => firstHttpClientHandler; + }); - var firstHttpClientHandler = new TestHttpClientHandler("NotSupposedToBeCalled_TestHttpClientHandler"); - using var firstDisposable = InitSentrySdk(o => - { - o.Dsn = "http://publickey@localhost:8000/12345"; - o.CreateHttpMessageHandler = () => firstHttpClientHandler; - }); + using var secondDisposable = InitSentrySdk(); // uses the default test DSN - using var secondDisposable = InitSentrySdk(); // uses the default test DSN + var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); - var testBehaviour = new GameObject("TestHolder").AddComponent(); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.ThrowException), _eventMessage); + // Sanity check + Assert.AreEqual(string.Empty, firstHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout)); - // Sanity check - Assert.AreEqual(string.Empty, firstHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout)); + Assert.AreNotEqual(string.Empty, _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout)); + } - Assert.AreNotEqual(string.Empty, _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout)); - } + [UnityTest] + public IEnumerator DebugLogException_IsMarkedUnhandled() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator DebugLogException_IsMarkedUnhandled() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var expectedMechanism = "\"mechanism\":{\"type\":\"Unity.LogException\",\"handled\":false}}]}"; + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - var expectedMechanism = "\"mechanism\":{\"type\":\"Unity.LogException\",\"handled\":false}}]}"; - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check + Assert.That(triggeredEvent, Does.Contain(expectedMechanism)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); // sanity check - Assert.That(triggeredEvent, Does.Contain(expectedMechanism)); - } + [UnityTest] + public IEnumerator DebugLogError_OnMainThread_IsCapturedAndIsMainThreadIsTrue() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator DebugLogError_OnMainThread_IsCapturedAndIsMainThreadIsTrue() - { - yield return SetupSceneCoroutine("1_BugFarm"); + _identifyingEventValueAttribute = CreateAttribute("message", _eventMessage); // DebugLogError gets captured as a message + var expectedAttribute = CreateAttribute("unity.is_main_thread", "true"); - _identifyingEventValueAttribute = CreateAttribute("message", _eventMessage); // DebugLogError gets captured as a message - var expectedAttribute = CreateAttribute("unity.is_main_thread", "true"); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogError), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogError), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator DebugLogError_InTask_IsCapturedAndIsMainThreadIsFalse() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator DebugLogError_InTask_IsCapturedAndIsMainThreadIsFalse() - { - yield return SetupSceneCoroutine("1_BugFarm"); + _identifyingEventValueAttribute = CreateAttribute("message", _eventMessage); // DebugLogError gets captured as a message + var expectedAttribute = CreateAttribute("unity.is_main_thread", "false"); - _identifyingEventValueAttribute = CreateAttribute("message", _eventMessage); // DebugLogError gets captured as a message - var expectedAttribute = CreateAttribute("unity.is_main_thread", "false"); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogErrorInTask), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogErrorInTask), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator DebugLogException_OnMainThread_IsCapturedAndIsMainThreadIsTrue() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator DebugLogException_OnMainThread_IsCapturedAndIsMainThreadIsTrue() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var expectedAttribute = CreateAttribute("unity.is_main_thread", "true"); - var expectedAttribute = CreateAttribute("unity.is_main_thread", "true"); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogException), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogException), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator DebugLogException_InTask_IsCapturedAndIsMainThreadIsFalse() + { + yield return SetupSceneCoroutine("1_BugFarm"); - [UnityTest] - public IEnumerator DebugLogException_InTask_IsCapturedAndIsMainThreadIsFalse() - { - yield return SetupSceneCoroutine("1_BugFarm"); + var expectedAttribute = CreateAttribute("unity.is_main_thread", "false"); - var expectedAttribute = CreateAttribute("unity.is_main_thread", "false"); + using var _ = InitSentrySdk(); + var testBehaviour = new GameObject("TestHolder").AddComponent(); - using var _ = InitSentrySdk(); - var testBehaviour = new GameObject("TestHolder").AddComponent(); + testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogExceptionInTask), _eventMessage); - testBehaviour.gameObject.SendMessage(nameof(testBehaviour.DebugLogExceptionInTask), _eventMessage); + var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); + Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); + Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); + } - var triggeredEvent = _testHttpClientHandler.GetEvent(_identifyingEventValueAttribute, _eventReceiveTimeout); - Assert.That(triggeredEvent, Does.Contain(_identifyingEventValueAttribute)); - Assert.That(triggeredEvent, Does.Contain(expectedAttribute)); - } + [UnityTest] + public IEnumerator Init_OptionsAreDefaulted() + { + yield return null; - [UnityTest] - public IEnumerator Init_OptionsAreDefaulted() + var expectedOptions = new SentryUnityOptions { - yield return null; - - var expectedOptions = new SentryUnityOptions - { - Dsn = string.Empty // The SentrySDK tries to resolve the DSN from the environment when it's null - }; - - SentryUnityOptions? actualOptions = null; - using var _ = InitSentrySdk(o => - { - o.Dsn = string.Empty; // InitSentrySDK already sets a test dsn - actualOptions = o; - }); - - Assert.NotNull(actualOptions); - ScriptableSentryUnityOptionsTests.AssertOptions(expectedOptions, actualOptions!); - } + Dsn = string.Empty // The SentrySDK tries to resolve the DSN from the environment when it's null + }; - private static string CreateAttribute(string name, string value) => $"\"{name}\":\"{value}\""; - - internal static IEnumerator SetupSceneCoroutine(string sceneName, [CallerMemberName] string callerName = "") + SentryUnityOptions? actualOptions = null; + using var _ = InitSentrySdk(o => { - Debug.Log($"=== Running: '{callerName}' ===\n"); + o.Dsn = string.Empty; // InitSentrySDK already sets a test dsn + actualOptions = o; + }); - // don't fail test if exception is thrown via 'SendMessage', we want to continue - LogAssert.ignoreFailingMessages = true; + Assert.NotNull(actualOptions); + ScriptableSentryUnityOptionsTests.AssertOptions(expectedOptions, actualOptions!); + } - // load scene with initialized Sentry, SceneManager.LoadSceneAsync(sceneName); - SceneManager.LoadScene(sceneName); + private static string CreateAttribute(string name, string value) => $"\"{name}\":\"{value}\""; - // skip a frame for a Unity to properly load a scene - yield return null; - } + internal static IEnumerator SetupSceneCoroutine(string sceneName, [CallerMemberName] string callerName = "") + { + Debug.Log($"=== Running: '{callerName}' ===\n"); - internal IDisposable InitSentrySdk(Action? configure = null) - { - SentryUnity.Init(options => - { - options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; - options.CreateHttpMessageHandler = () => _testHttpClientHandler; + // don't fail test if exception is thrown via 'SendMessage', we want to continue + LogAssert.ignoreFailingMessages = true; - configure?.Invoke(options); - }); + // load scene with initialized Sentry, SceneManager.LoadSceneAsync(sceneName); + SceneManager.LoadScene(sceneName); - return new SentryDisposable(); - } + // skip a frame for a Unity to properly load a scene + yield return null; + } - private sealed class SentryDisposable : IDisposable + internal IDisposable InitSentrySdk(Action? configure = null) + { + SentryUnity.Init(options => { - public void Dispose() => SentrySdk.Close(); - } + options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; + options.CreateHttpMessageHandler = () => _testHttpClientHandler; + + configure?.Invoke(options); + }); + + return new SentryDisposable(); + } + + private sealed class SentryDisposable : IDisposable + { + public void Dispose() => SentrySdk.Close(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs b/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs index d01650b52..b4d7d6ad0 100644 --- a/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs +++ b/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs @@ -4,68 +4,67 @@ using Sentry.Unity.Json; using Sentry.Unity.Tests.SharedClasses; -namespace Sentry.Unity.Tests.Json +namespace Sentry.Unity.Tests.Json; + +public class SafeSerializerTests { - public class SafeSerializerTests + [Test] + public void SerializeExtraValue_ValueSerializable_ReturnsSerializedValue() { - [Test] - public void SerializeExtraValue_ValueSerializable_ReturnsSerializedValue() - { - var testLogger = new TestLogger(); + var testLogger = new TestLogger(); - var actualValue = SafeSerializer.SerializeSafely(new { Member = "testString" }, testLogger); + var actualValue = SafeSerializer.SerializeSafely(new { Member = "testString" }, testLogger); - Assert.NotNull(actualValue); - Assert.AreEqual("{\"Member\":\"testString\"}", actualValue); - Assert.IsEmpty(testLogger.Logs); - } + Assert.NotNull(actualValue); + Assert.AreEqual("{\"Member\":\"testString\"}", actualValue); + Assert.IsEmpty(testLogger.Logs); + } - [Test] - public void SerializeExtraValue_ValueIsString_ReturnsSameString() - { - var testLogger = new TestLogger(); + [Test] + public void SerializeExtraValue_ValueIsString_ReturnsSameString() + { + var testLogger = new TestLogger(); - var expectedValue = "testString"; - var actualValue = SafeSerializer.SerializeSafely(expectedValue, testLogger); + var expectedValue = "testString"; + var actualValue = SafeSerializer.SerializeSafely(expectedValue, testLogger); - Assert.NotNull(actualValue); - Assert.AreEqual(expectedValue, actualValue); - Assert.IsEmpty(testLogger.Logs); - } + Assert.NotNull(actualValue); + Assert.AreEqual(expectedValue, actualValue); + Assert.IsEmpty(testLogger.Logs); + } - [Test] - [TestCase(321)] - [TestCase(-9870L)] - [TestCase(234.12)] - [TestCase(123 + 144D)] - public void SerializeExtraValue_NumericValueType_ReturnsValueAsToString(object valueType) - { - var testLogger = new TestLogger(); + [Test] + [TestCase(321)] + [TestCase(-9870L)] + [TestCase(234.12)] + [TestCase(123 + 144D)] + public void SerializeExtraValue_NumericValueType_ReturnsValueAsToString(object valueType) + { + var testLogger = new TestLogger(); - var actualValue = SafeSerializer.SerializeSafely(valueType, testLogger); + var actualValue = SafeSerializer.SerializeSafely(valueType, testLogger); - Assert.NotNull(actualValue); - Assert.AreEqual(valueType.ToString(), actualValue); - Assert.IsEmpty(testLogger.Logs); - } + Assert.NotNull(actualValue); + Assert.AreEqual(valueType.ToString(), actualValue); + Assert.IsEmpty(testLogger.Logs); + } - [Test] - public void SerializeExtraValue_ValueNotSerializable_ReturnsNull() - { - var testLogger = new TestLogger(); + [Test] + public void SerializeExtraValue_ValueNotSerializable_ReturnsNull() + { + var testLogger = new TestLogger(); - var actualValue = SafeSerializer.SerializeSafely(new SerializationTestClass(), testLogger); + var actualValue = SafeSerializer.SerializeSafely(new SerializationTestClass(), testLogger); - Assert.IsNull(actualValue); - Assert.AreEqual(1, testLogger.Logs.Count); - var (_, _, exception) = testLogger.Logs.Single(); - Assert.NotNull(exception); - Assert.IsAssignableFrom(exception!.InnerException); - } + Assert.IsNull(actualValue); + Assert.AreEqual(1, testLogger.Logs.Count); + var (_, _, exception) = testLogger.Logs.Single(); + Assert.NotNull(exception); + Assert.IsAssignableFrom(exception!.InnerException); + } - private class SerializationTestClass - { - public string Member => throw new DivideByZeroException(); - } + private class SerializationTestClass + { + public string Member => throw new DivideByZeroException(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/Protocol/UnityTests.cs b/test/Sentry.Unity.Tests/Protocol/UnityTests.cs index bd75e2cc4..d6af787a3 100644 --- a/test/Sentry.Unity.Tests/Protocol/UnityTests.cs +++ b/test/Sentry.Unity.Tests/Protocol/UnityTests.cs @@ -1,75 +1,74 @@ using NUnit.Framework; -namespace Sentry.Unity.Tests.Protocol +namespace Sentry.Unity.Tests.Protocol; + +public sealed class UnityTests { - public sealed class UnityTests + [Test] + public void Ctor_NoPropertyFilled_SerializesEmptyObject() { - [Test] - public void Ctor_NoPropertyFilled_SerializesEmptyObject() - { - var sut = new Unity.Protocol.Unity(); + var sut = new Unity.Protocol.Unity(); - var actual = sut.ToJsonString(); + var actual = sut.ToJsonString(); - Assert.AreEqual("{\"type\":\"unity\"}", actual); - } + Assert.AreEqual("{\"type\":\"unity\"}", actual); + } - [Test] - public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() + [Test] + public void SerializeObject_AllPropertiesSetToNonDefault_SerializesValidObject() + { + var sut = new Unity.Protocol.Unity { - var sut = new Unity.Protocol.Unity - { - InstallMode = "DeveloperBuild", - CopyTextureSupport = "Copy3D", - RenderingThreadingMode = "MultiThreaded", - TargetFrameRate = "30" - }; + InstallMode = "DeveloperBuild", + CopyTextureSupport = "Copy3D", + RenderingThreadingMode = "MultiThreaded", + TargetFrameRate = "30" + }; - var actual = sut.ToJsonString(); + var actual = sut.ToJsonString(); - Assert.AreEqual( - "{\"type\":\"unity\"," + - "\"install_mode\":\"DeveloperBuild\"," + - "\"copy_texture_support\":\"Copy3D\"," + - "\"rendering_threading_mode\":\"MultiThreaded\"," + - "\"target_frame_rate\":\"30\"}", - actual); - } + Assert.AreEqual( + "{\"type\":\"unity\"," + + "\"install_mode\":\"DeveloperBuild\"," + + "\"copy_texture_support\":\"Copy3D\"," + + "\"rendering_threading_mode\":\"MultiThreaded\"," + + "\"target_frame_rate\":\"30\"}", + actual); + } - [Test] - public void Clone_CopyValues() + [Test] + public void Clone_CopyValues() + { + var sut = new Unity.Protocol.Unity { - var sut = new Unity.Protocol.Unity - { - InstallMode = "DeveloperBuild", - CopyTextureSupport = "Copy3D", - RenderingThreadingMode = "MultiThreaded", - TargetFrameRate = "30" - }; - - var clone = sut.Clone(); + InstallMode = "DeveloperBuild", + CopyTextureSupport = "Copy3D", + RenderingThreadingMode = "MultiThreaded", + TargetFrameRate = "30" + }; - Assert.AreEqual(sut.InstallMode, clone.InstallMode); - Assert.AreEqual(sut.CopyTextureSupport, clone.CopyTextureSupport); - Assert.AreEqual(sut.RenderingThreadingMode, clone.RenderingThreadingMode); - Assert.AreEqual(sut.TargetFrameRate, clone.TargetFrameRate); - } + var clone = sut.Clone(); - [TestCaseSource(nameof(TestCases))] - public void SerializeObject_TestCase_SerializesAsExpected((Unity.Protocol.Unity device, string serialized) @case) - { - var actual = @case.device.ToJsonString(); + Assert.AreEqual(sut.InstallMode, clone.InstallMode); + Assert.AreEqual(sut.CopyTextureSupport, clone.CopyTextureSupport); + Assert.AreEqual(sut.RenderingThreadingMode, clone.RenderingThreadingMode); + Assert.AreEqual(sut.TargetFrameRate, clone.TargetFrameRate); + } - Assert.AreEqual(@case.serialized, actual); - } + [TestCaseSource(nameof(TestCases))] + public void SerializeObject_TestCase_SerializesAsExpected((Unity.Protocol.Unity device, string serialized) @case) + { + var actual = @case.device.ToJsonString(); - private static object[] TestCases = - { - new object[] { (new Unity.Protocol.Unity(), "{\"type\":\"unity\"}") }, - new object[] { (new Unity.Protocol.Unity { InstallMode = "Adhoc" }, "{\"type\":\"unity\",\"install_mode\":\"Adhoc\"}") }, - new object[] { (new Unity.Protocol.Unity { CopyTextureSupport = "TextureToRT" }, "{\"type\":\"unity\",\"copy_texture_support\":\"TextureToRT\"}") }, - new object[] { (new Unity.Protocol.Unity { RenderingThreadingMode = "NativeGraphicsJobs" }, "{\"type\":\"unity\",\"rendering_threading_mode\":\"NativeGraphicsJobs\"}") }, - new object[] { (new Unity.Protocol.Unity { TargetFrameRate = "30" }, "{\"type\":\"unity\",\"target_frame_rate\":\"30\"}") } - }; + Assert.AreEqual(@case.serialized, actual); } -} + + private static object[] TestCases = + { + new object[] { (new Unity.Protocol.Unity(), "{\"type\":\"unity\"}") }, + new object[] { (new Unity.Protocol.Unity { InstallMode = "Adhoc" }, "{\"type\":\"unity\",\"install_mode\":\"Adhoc\"}") }, + new object[] { (new Unity.Protocol.Unity { CopyTextureSupport = "TextureToRT" }, "{\"type\":\"unity\",\"copy_texture_support\":\"TextureToRT\"}") }, + new object[] { (new Unity.Protocol.Unity { RenderingThreadingMode = "NativeGraphicsJobs" }, "{\"type\":\"unity\",\"rendering_threading_mode\":\"NativeGraphicsJobs\"}") }, + new object[] { (new Unity.Protocol.Unity { TargetFrameRate = "30" }, "{\"type\":\"unity\",\"target_frame_rate\":\"30\"}") } + }; +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs b/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs index d45348eb6..ecd6927df 100644 --- a/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs @@ -5,128 +5,127 @@ using Sentry.Unity.Tests.Stubs; using UnityEngine.SceneManagement; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class SceneManagerIntegrationTests { - public class SceneManagerIntegrationTests + [Test] + public void SceneLoaded_DisabledHub_NoCrumbAdded() + { + _fixture.TestHub = new TestHub(false); + var sut = _fixture.GetSut(); + + sut.Register(_fixture.TestHub, _fixture.SentryOptions); + _fixture.SceneManager.OnSceneLoaded(default, default); + + Assert.Zero(_fixture.TestHub.ConfigureScopeCalls.Count); + } + + [Test] + public void SceneLoaded_EnabledHub_CrumbAdded() + { + _fixture.TestHub = new TestHub(); + var sut = _fixture.GetSut(); + + sut.Register(_fixture.TestHub, _fixture.SentryOptions); + const string? sceneName = "scene name"; + var expectedScene = new SceneAdapter(sceneName); + _fixture.SceneManager.OnSceneLoaded(expectedScene, + LoadSceneMode.Additive); + + var configureScope = _fixture.TestHub.ConfigureScopeCalls.Single(); + var scope = new Scope(_fixture.SentryOptions); + configureScope(scope); + var actualCrumb = scope.Breadcrumbs.Single(); + + Assert.AreEqual($"Scene '{sceneName}' was loaded", actualCrumb.Message); + Assert.AreEqual("scene.loaded", actualCrumb.Category); + } + + [Test] + public void SceneUnloaded_DisabledHub_NoCrumbAdded() + { + _fixture.TestHub = new TestHub(false); + var sut = _fixture.GetSut(); + + sut.Register(_fixture.TestHub, _fixture.SentryOptions); + _fixture.SceneManager.OnSceneUnloaded(default); + + Assert.Zero(_fixture.TestHub.ConfigureScopeCalls.Count); + } + + [Test] + public void SceneUnloaded_EnabledHub_CrumbAdded() + { + _fixture.TestHub = new TestHub(); + var sut = _fixture.GetSut(); + + sut.Register(_fixture.TestHub, _fixture.SentryOptions); + const string? sceneName = "scene name"; + var expectedScene = new SceneAdapter(sceneName); + _fixture.SceneManager.OnSceneUnloaded(expectedScene); + + var configureScope = _fixture.TestHub.ConfigureScopeCalls.Single(); + var scope = new Scope(_fixture.SentryOptions); + configureScope(scope); + var actualCrumb = scope.Breadcrumbs.Single(); + + Assert.AreEqual($"Scene '{sceneName}' was unloaded", actualCrumb.Message); + Assert.AreEqual("scene.unloaded", actualCrumb.Category); + } + + [Test] + public void ActiveSceneChanged_DisabledHub_NoCrumbAdded() + { + _fixture.TestHub = new TestHub(false); + var sut = _fixture.GetSut(); + + sut.Register(_fixture.TestHub, _fixture.SentryOptions); + _fixture.SceneManager.OnActiveSceneChanged(default, default); + + Assert.Zero(_fixture.TestHub.ConfigureScopeCalls.Count); + } + + [Test] + public void ActiveSceneChanged_EnabledHub_CrumbAdded() + { + _fixture.TestHub = new TestHub(); + var sut = _fixture.GetSut(); + + sut.Register(_fixture.TestHub, _fixture.SentryOptions); + const string? fromSceneName = "from scene name"; + const string? toSceneName = "to scene name"; + var expectedFromScene = new SceneAdapter(fromSceneName); + var expectedToScene = new SceneAdapter(toSceneName); + _fixture.SceneManager.OnActiveSceneChanged(expectedFromScene, expectedToScene); + + var configureScope = _fixture.TestHub.ConfigureScopeCalls.Single(); + var scope = new Scope(_fixture.SentryOptions); + configureScope(scope); + var actualCrumb = scope.Breadcrumbs.Single(); + + Assert.AreEqual($"Changed active scene '{expectedFromScene.Name}' to '{expectedToScene.Name}'", actualCrumb.Message); + Assert.AreEqual("scene.changed", actualCrumb.Category); + Assert.Null(actualCrumb.Data); + } + + private class Fixture + { + public FakeSceneManager SceneManager { get; set; } = new(); + public TestHub TestHub { get; set; } = new(); + public SentryOptions SentryOptions { get; set; } = new(); + public SceneManagerIntegration GetSut() => new(SceneManager); + } + + private readonly Fixture _fixture = new(); + + private class FakeSceneManager : ISceneManager { - [Test] - public void SceneLoaded_DisabledHub_NoCrumbAdded() - { - _fixture.TestHub = new TestHub(false); - var sut = _fixture.GetSut(); - - sut.Register(_fixture.TestHub, _fixture.SentryOptions); - _fixture.SceneManager.OnSceneLoaded(default, default); - - Assert.Zero(_fixture.TestHub.ConfigureScopeCalls.Count); - } - - [Test] - public void SceneLoaded_EnabledHub_CrumbAdded() - { - _fixture.TestHub = new TestHub(); - var sut = _fixture.GetSut(); - - sut.Register(_fixture.TestHub, _fixture.SentryOptions); - const string? sceneName = "scene name"; - var expectedScene = new SceneAdapter(sceneName); - _fixture.SceneManager.OnSceneLoaded(expectedScene, - LoadSceneMode.Additive); - - var configureScope = _fixture.TestHub.ConfigureScopeCalls.Single(); - var scope = new Scope(_fixture.SentryOptions); - configureScope(scope); - var actualCrumb = scope.Breadcrumbs.Single(); - - Assert.AreEqual($"Scene '{sceneName}' was loaded", actualCrumb.Message); - Assert.AreEqual("scene.loaded", actualCrumb.Category); - } - - [Test] - public void SceneUnloaded_DisabledHub_NoCrumbAdded() - { - _fixture.TestHub = new TestHub(false); - var sut = _fixture.GetSut(); - - sut.Register(_fixture.TestHub, _fixture.SentryOptions); - _fixture.SceneManager.OnSceneUnloaded(default); - - Assert.Zero(_fixture.TestHub.ConfigureScopeCalls.Count); - } - - [Test] - public void SceneUnloaded_EnabledHub_CrumbAdded() - { - _fixture.TestHub = new TestHub(); - var sut = _fixture.GetSut(); - - sut.Register(_fixture.TestHub, _fixture.SentryOptions); - const string? sceneName = "scene name"; - var expectedScene = new SceneAdapter(sceneName); - _fixture.SceneManager.OnSceneUnloaded(expectedScene); - - var configureScope = _fixture.TestHub.ConfigureScopeCalls.Single(); - var scope = new Scope(_fixture.SentryOptions); - configureScope(scope); - var actualCrumb = scope.Breadcrumbs.Single(); - - Assert.AreEqual($"Scene '{sceneName}' was unloaded", actualCrumb.Message); - Assert.AreEqual("scene.unloaded", actualCrumb.Category); - } - - [Test] - public void ActiveSceneChanged_DisabledHub_NoCrumbAdded() - { - _fixture.TestHub = new TestHub(false); - var sut = _fixture.GetSut(); - - sut.Register(_fixture.TestHub, _fixture.SentryOptions); - _fixture.SceneManager.OnActiveSceneChanged(default, default); - - Assert.Zero(_fixture.TestHub.ConfigureScopeCalls.Count); - } - - [Test] - public void ActiveSceneChanged_EnabledHub_CrumbAdded() - { - _fixture.TestHub = new TestHub(); - var sut = _fixture.GetSut(); - - sut.Register(_fixture.TestHub, _fixture.SentryOptions); - const string? fromSceneName = "from scene name"; - const string? toSceneName = "to scene name"; - var expectedFromScene = new SceneAdapter(fromSceneName); - var expectedToScene = new SceneAdapter(toSceneName); - _fixture.SceneManager.OnActiveSceneChanged(expectedFromScene, expectedToScene); - - var configureScope = _fixture.TestHub.ConfigureScopeCalls.Single(); - var scope = new Scope(_fixture.SentryOptions); - configureScope(scope); - var actualCrumb = scope.Breadcrumbs.Single(); - - Assert.AreEqual($"Changed active scene '{expectedFromScene.Name}' to '{expectedToScene.Name}'", actualCrumb.Message); - Assert.AreEqual("scene.changed", actualCrumb.Category); - Assert.Null(actualCrumb.Data); - } - - private class Fixture - { - public FakeSceneManager SceneManager { get; set; } = new(); - public TestHub TestHub { get; set; } = new(); - public SentryOptions SentryOptions { get; set; } = new(); - public SceneManagerIntegration GetSut() => new(SceneManager); - } - - private readonly Fixture _fixture = new(); - - private class FakeSceneManager : ISceneManager - { - public event Action? SceneLoaded; - public event Action? SceneUnloaded; - public event Action? ActiveSceneChanged; - public void OnSceneLoaded(SceneAdapter scene, LoadSceneMode mode) => SceneLoaded?.Invoke(scene, mode); - public void OnSceneUnloaded(SceneAdapter scene) => SceneUnloaded?.Invoke(scene); - public void OnActiveSceneChanged(SceneAdapter fromScene, SceneAdapter toScene) => ActiveSceneChanged?.Invoke(fromScene, toScene); - } + public event Action? SceneLoaded; + public event Action? SceneUnloaded; + public event Action? ActiveSceneChanged; + public void OnSceneLoaded(SceneAdapter scene, LoadSceneMode mode) => SceneLoaded?.Invoke(scene, mode); + public void OnSceneUnloaded(SceneAdapter scene) => SceneUnloaded?.Invoke(scene); + public void OnActiveSceneChanged(SceneAdapter fromScene, SceneAdapter toScene) => ActiveSceneChanged?.Invoke(fromScene, toScene); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs b/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs index e353b5862..2e33e2ba9 100644 --- a/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs +++ b/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs @@ -1,98 +1,96 @@ using System.IO; using System.Threading; -using System.Threading.Tasks; using NUnit.Framework; using UnityEngine; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class ScreenshotAttachmentTests { - public class ScreenshotAttachmentTests + private class Fixture { - private class Fixture - { - public SentryUnityOptions Options = new() { AttachScreenshot = true }; + public SentryUnityOptions Options = new() { AttachScreenshot = true }; - public ScreenshotAttachmentContent GetSut() => new(Options, SentryMonoBehaviour.Instance); - } + public ScreenshotAttachmentContent GetSut() => new(Options, SentryMonoBehaviour.Instance); + } - private Fixture _fixture = null!; + private Fixture _fixture = null!; - [SetUp] - public void SetUp() => _fixture = new Fixture(); + [SetUp] + public void SetUp() => _fixture = new Fixture(); - [TearDown] - public void TearDown() + [TearDown] + public void TearDown() + { + if (SentrySdk.IsEnabled) { - if (SentrySdk.IsEnabled) - { - SentryUnity.Close(); - } + SentryUnity.Close(); } + } - [Test] - [TestCase(ScreenshotQuality.High, 1920)] - [TestCase(ScreenshotQuality.Medium, 1280)] - [TestCase(ScreenshotQuality.Low, 854)] - public void GetTargetResolution_ReturnsTargetMaxSize(ScreenshotQuality quality, int expectedValue) - { - var actualValue = ScreenshotAttachmentContent.GetTargetResolution(quality); + [Test] + [TestCase(ScreenshotQuality.High, 1920)] + [TestCase(ScreenshotQuality.Medium, 1280)] + [TestCase(ScreenshotQuality.Low, 854)] + public void GetTargetResolution_ReturnsTargetMaxSize(ScreenshotQuality quality, int expectedValue) + { + var actualValue = ScreenshotAttachmentContent.GetTargetResolution(quality); - Assert.AreEqual(expectedValue, actualValue); - } + Assert.AreEqual(expectedValue, actualValue); + } - [Test] - public void GetStream_IsMainThread_ReturnsStream() - { - var sut = _fixture.GetSut(); + [Test] + public void GetStream_IsMainThread_ReturnsStream() + { + var sut = _fixture.GetSut(); - var stream = sut.GetStream(); + var stream = sut.GetStream(); - Assert.IsNotNull(stream); - } + Assert.IsNotNull(stream); + } - [Test] - public void GetStream_IsNonMainThread_ReturnsNullStream() - { - var sut = _fixture.GetSut(); + [Test] + public void GetStream_IsNonMainThread_ReturnsNullStream() + { + var sut = _fixture.GetSut(); - new Thread(() => - { - Thread.CurrentThread.IsBackground = true; - var stream = sut.GetStream(); + new Thread(() => + { + Thread.CurrentThread.IsBackground = true; + var stream = sut.GetStream(); - Assert.NotNull(stream); - Assert.AreEqual(Stream.Null, stream); - }).Start(); - } + Assert.NotNull(stream); + Assert.AreEqual(Stream.Null, stream); + }).Start(); + } - [Test] - [TestCase(ScreenshotQuality.High, 1920)] - [TestCase(ScreenshotQuality.Medium, 1280)] - [TestCase(ScreenshotQuality.Low, 854)] - public void CaptureScreenshot_QualitySet_ScreenshotDoesNotExceedDimensionLimit(ScreenshotQuality quality, int maximumAllowedDimension) - { - _fixture.Options.ScreenshotQuality = quality; - var sut = _fixture.GetSut(); + [Test] + [TestCase(ScreenshotQuality.High, 1920)] + [TestCase(ScreenshotQuality.Medium, 1280)] + [TestCase(ScreenshotQuality.Low, 854)] + public void CaptureScreenshot_QualitySet_ScreenshotDoesNotExceedDimensionLimit(ScreenshotQuality quality, int maximumAllowedDimension) + { + _fixture.Options.ScreenshotQuality = quality; + var sut = _fixture.GetSut(); - var bytes = sut.CaptureScreenshot(2000, 2000); - var texture = new Texture2D(1, 1); // Size does not matter. Will be overwritten by loading - texture.LoadImage(bytes); + var bytes = sut.CaptureScreenshot(2000, 2000); + var texture = new Texture2D(1, 1); // Size does not matter. Will be overwritten by loading + texture.LoadImage(bytes); - Assert.IsTrue(texture.width <= maximumAllowedDimension && texture.height <= maximumAllowedDimension); - } + Assert.IsTrue(texture.width <= maximumAllowedDimension && texture.height <= maximumAllowedDimension); + } - [Test] - public void CaptureScreenshot_QualitySetToFull_ScreenshotInFullSize() - { - var testScreenSize = 2000; - _fixture.Options.ScreenshotQuality = ScreenshotQuality.Full; - var sut = _fixture.GetSut(); + [Test] + public void CaptureScreenshot_QualitySetToFull_ScreenshotInFullSize() + { + var testScreenSize = 2000; + _fixture.Options.ScreenshotQuality = ScreenshotQuality.Full; + var sut = _fixture.GetSut(); - var bytes = sut.CaptureScreenshot(testScreenSize, testScreenSize); - var texture = new Texture2D(1, 1); // Size does not matter. Will be overwritten by loading - texture.LoadImage(bytes); + var bytes = sut.CaptureScreenshot(testScreenSize, testScreenSize); + var texture = new Texture2D(1, 1); // Size does not matter. Will be overwritten by loading + texture.LoadImage(bytes); - Assert.IsTrue(texture.width == testScreenSize && texture.height == testScreenSize); - } + Assert.IsTrue(texture.width == testScreenSize && texture.height == testScreenSize); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs index 418d724e7..d7d916a81 100644 --- a/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs @@ -5,168 +5,167 @@ using Sentry.Unity.Tests.Stubs; using UnityEngine; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +[TestFixture] +public class ScriptableSentryUnityOptionsTests { - [TestFixture] - public class ScriptableSentryUnityOptionsTests + private const string TestSentryOptionsFileName = "TestSentryOptions.json"; + + class Fixture { - private const string TestSentryOptionsFileName = "TestSentryOptions.json"; + public TestApplication Application { get; set; } = new( + productName: "TestApplication", + version: "0.1.0", + persistentDataPath: "test/persistent/data/path"); - class Fixture - { - public TestApplication Application { get; set; } = new( - productName: "TestApplication", - version: "0.1.0", - persistentDataPath: "test/persistent/data/path"); + public TestUnityInfo UnityInfo { get; set; } = new(); + } - public TestUnityInfo UnityInfo { get; set; } = new(); - } + class TestOptionsConfiguration : SentryRuntimeOptionsConfiguration + { + public bool GotCalled; + public override void Configure(SentryUnityOptions options) => GotCalled = true; + } - class TestOptionsConfiguration : SentryRuntimeOptionsConfiguration - { - public bool GotCalled; - public override void Configure(SentryUnityOptions options) => GotCalled = true; - } - - [SetUp] - public void Setup() => _fixture = new Fixture(); - private Fixture _fixture = null!; - - [Test] - [TestCase(true, true)] - [TestCase(false, false)] - public void ToSentryUnityOptions_ValueMapping_AreEqual(bool isBuilding, bool enableOfflineCaching) - { - var expectedOptions = new SentryUnityOptions - { - Enabled = false, - Dsn = "test", - CaptureInEditor = false, - EnableLogDebouncing = true, - TracesSampleRate = 1.0f, - AutoSessionTracking = false, - AutoSessionTrackingInterval = TimeSpan.FromSeconds(1), - AttachStacktrace = true, - AttachScreenshot = true, - MaxBreadcrumbs = 1, - ReportAssembliesMode = ReportAssembliesMode.None, - SendDefaultPii = true, - IsEnvironmentUser = true, - MaxCacheItems = 1, - CacheDirectoryPath = enableOfflineCaching ? _fixture.Application.PersistentDataPath : null, - InitCacheFlushTimeout = TimeSpan.FromSeconds(1), - SampleRate = 0.5f, - ShutdownTimeout = TimeSpan.FromSeconds(1), - MaxQueueItems = 1, - Release = "testRelease", - Environment = "testEnvironment", - Debug = true, - DiagnosticLevel = SentryLevel.Info, - }; - - var scriptableOptions = ScriptableObject.CreateInstance(); - scriptableOptions.Enabled = expectedOptions.Enabled; - scriptableOptions.Dsn = expectedOptions.Dsn; - scriptableOptions.CaptureInEditor = expectedOptions.CaptureInEditor; - scriptableOptions.EnableLogDebouncing = expectedOptions.EnableLogDebouncing; - scriptableOptions.TracesSampleRate = (double)expectedOptions.TracesSampleRate; - scriptableOptions.AutoSessionTracking = expectedOptions.AutoSessionTracking; - scriptableOptions.AutoSessionTrackingInterval = (int)expectedOptions.AutoSessionTrackingInterval.TotalMilliseconds; - scriptableOptions.AttachStacktrace = expectedOptions.AttachStacktrace; - scriptableOptions.AttachScreenshot = expectedOptions.AttachScreenshot; - scriptableOptions.MaxBreadcrumbs = expectedOptions.MaxBreadcrumbs; - scriptableOptions.ReportAssembliesMode = expectedOptions.ReportAssembliesMode; - scriptableOptions.SendDefaultPii = expectedOptions.SendDefaultPii; - scriptableOptions.IsEnvironmentUser = expectedOptions.IsEnvironmentUser; - scriptableOptions.MaxCacheItems = expectedOptions.MaxCacheItems; - scriptableOptions.EnableOfflineCaching = enableOfflineCaching; - scriptableOptions.InitCacheFlushTimeout = (int)expectedOptions.InitCacheFlushTimeout.TotalMilliseconds; - scriptableOptions.SampleRate = (float)expectedOptions.SampleRate; - scriptableOptions.ShutdownTimeout = (int)expectedOptions.ShutdownTimeout.TotalMilliseconds; - scriptableOptions.MaxQueueItems = expectedOptions.MaxQueueItems; - scriptableOptions.ReleaseOverride = expectedOptions.Release; - scriptableOptions.EnvironmentOverride = expectedOptions.Environment; - scriptableOptions.Debug = expectedOptions.Debug; - scriptableOptions.DebugOnlyInEditor = false; // Affects Debug otherwise - scriptableOptions.DiagnosticLevel = expectedOptions.DiagnosticLevel; - - var optionsActual = scriptableOptions.ToSentryUnityOptions(isBuilding, _fixture.UnityInfo, _fixture.Application); - - AssertOptions(expectedOptions, optionsActual); - } - - [Test] - [TestCase(true, true)] - [TestCase(false, false)] - public void ShouldDebug_DebugOnlyInEditor_ReturnsExpectedDebug(bool isEditorPlayer, bool expectedDebug) + [SetUp] + public void Setup() => _fixture = new Fixture(); + private Fixture _fixture = null!; + + [Test] + [TestCase(true, true)] + [TestCase(false, false)] + public void ToSentryUnityOptions_ValueMapping_AreEqual(bool isBuilding, bool enableOfflineCaching) + { + var expectedOptions = new SentryUnityOptions { - var scriptableOptions = ScriptableObject.CreateInstance(); - scriptableOptions.Debug = true; - scriptableOptions.DebugOnlyInEditor = true; + Enabled = false, + Dsn = "test", + CaptureInEditor = false, + EnableLogDebouncing = true, + TracesSampleRate = 1.0f, + AutoSessionTracking = false, + AutoSessionTrackingInterval = TimeSpan.FromSeconds(1), + AttachStacktrace = true, + AttachScreenshot = true, + MaxBreadcrumbs = 1, + ReportAssembliesMode = ReportAssembliesMode.None, + SendDefaultPii = true, + IsEnvironmentUser = true, + MaxCacheItems = 1, + CacheDirectoryPath = enableOfflineCaching ? _fixture.Application.PersistentDataPath : null, + InitCacheFlushTimeout = TimeSpan.FromSeconds(1), + SampleRate = 0.5f, + ShutdownTimeout = TimeSpan.FromSeconds(1), + MaxQueueItems = 1, + Release = "testRelease", + Environment = "testEnvironment", + Debug = true, + DiagnosticLevel = SentryLevel.Info, + }; + + var scriptableOptions = ScriptableObject.CreateInstance(); + scriptableOptions.Enabled = expectedOptions.Enabled; + scriptableOptions.Dsn = expectedOptions.Dsn; + scriptableOptions.CaptureInEditor = expectedOptions.CaptureInEditor; + scriptableOptions.EnableLogDebouncing = expectedOptions.EnableLogDebouncing; + scriptableOptions.TracesSampleRate = (double)expectedOptions.TracesSampleRate; + scriptableOptions.AutoSessionTracking = expectedOptions.AutoSessionTracking; + scriptableOptions.AutoSessionTrackingInterval = (int)expectedOptions.AutoSessionTrackingInterval.TotalMilliseconds; + scriptableOptions.AttachStacktrace = expectedOptions.AttachStacktrace; + scriptableOptions.AttachScreenshot = expectedOptions.AttachScreenshot; + scriptableOptions.MaxBreadcrumbs = expectedOptions.MaxBreadcrumbs; + scriptableOptions.ReportAssembliesMode = expectedOptions.ReportAssembliesMode; + scriptableOptions.SendDefaultPii = expectedOptions.SendDefaultPii; + scriptableOptions.IsEnvironmentUser = expectedOptions.IsEnvironmentUser; + scriptableOptions.MaxCacheItems = expectedOptions.MaxCacheItems; + scriptableOptions.EnableOfflineCaching = enableOfflineCaching; + scriptableOptions.InitCacheFlushTimeout = (int)expectedOptions.InitCacheFlushTimeout.TotalMilliseconds; + scriptableOptions.SampleRate = (float)expectedOptions.SampleRate; + scriptableOptions.ShutdownTimeout = (int)expectedOptions.ShutdownTimeout.TotalMilliseconds; + scriptableOptions.MaxQueueItems = expectedOptions.MaxQueueItems; + scriptableOptions.ReleaseOverride = expectedOptions.Release; + scriptableOptions.EnvironmentOverride = expectedOptions.Environment; + scriptableOptions.Debug = expectedOptions.Debug; + scriptableOptions.DebugOnlyInEditor = false; // Affects Debug otherwise + scriptableOptions.DiagnosticLevel = expectedOptions.DiagnosticLevel; + + var optionsActual = scriptableOptions.ToSentryUnityOptions(isBuilding, _fixture.UnityInfo, _fixture.Application); + + AssertOptions(expectedOptions, optionsActual); + } - var actualDebug = scriptableOptions.ShouldDebug(isEditorPlayer); + [Test] + [TestCase(true, true)] + [TestCase(false, false)] + public void ShouldDebug_DebugOnlyInEditor_ReturnsExpectedDebug(bool isEditorPlayer, bool expectedDebug) + { + var scriptableOptions = ScriptableObject.CreateInstance(); + scriptableOptions.Debug = true; + scriptableOptions.DebugOnlyInEditor = true; - Assert.AreEqual(expectedDebug, actualDebug); - } + var actualDebug = scriptableOptions.ShouldDebug(isEditorPlayer); - [Test] - [TestCase(true)] - [TestCase(false)] - public void ToSentryUnityOptions_HasOptionsConfiguration_GetsCalled(bool isBuilding) - { - var optionsConfiguration = ScriptableObject.CreateInstance(); - var scriptableOptions = ScriptableObject.CreateInstance(); - scriptableOptions.RuntimeOptionsConfiguration = optionsConfiguration; + Assert.AreEqual(expectedDebug, actualDebug); + } - scriptableOptions.ToSentryUnityOptions(isBuilding, _fixture.UnityInfo); + [Test] + [TestCase(true)] + [TestCase(false)] + public void ToSentryUnityOptions_HasOptionsConfiguration_GetsCalled(bool isBuilding) + { + var optionsConfiguration = ScriptableObject.CreateInstance(); + var scriptableOptions = ScriptableObject.CreateInstance(); + scriptableOptions.RuntimeOptionsConfiguration = optionsConfiguration; - Assert.AreEqual(optionsConfiguration.GotCalled, !isBuilding); - } + scriptableOptions.ToSentryUnityOptions(isBuilding, _fixture.UnityInfo); - [Test] - public void ToSentryUnityOptions_UnknownPlatforms_DoesNotAccessDisk() - { - var scriptableOptions = ScriptableObject.CreateInstance(); - _fixture.UnityInfo = new TestUnityInfo(false); + Assert.AreEqual(optionsConfiguration.GotCalled, !isBuilding); + } - var options = scriptableOptions.ToSentryUnityOptions(false, _fixture.UnityInfo, _fixture.Application); + [Test] + public void ToSentryUnityOptions_UnknownPlatforms_DoesNotAccessDisk() + { + var scriptableOptions = ScriptableObject.CreateInstance(); + _fixture.UnityInfo = new TestUnityInfo(false); - Assert.IsNull(options.CacheDirectoryPath); - Assert.IsFalse(options.AutoSessionTracking); - } + var options = scriptableOptions.ToSentryUnityOptions(false, _fixture.UnityInfo, _fixture.Application); - public static void AssertOptions(SentryUnityOptions expected, SentryUnityOptions actual) - { - Assert.AreEqual(expected.Enabled, actual.Enabled); - Assert.AreEqual(expected.Dsn, actual.Dsn); - Assert.AreEqual(expected.CaptureInEditor, actual.CaptureInEditor); - Assert.AreEqual(expected.EnableLogDebouncing, actual.EnableLogDebouncing); - Assert.AreEqual(expected.TracesSampleRate, actual.TracesSampleRate); - Assert.AreEqual(expected.AutoSessionTracking, actual.AutoSessionTracking); - Assert.AreEqual(expected.AutoSessionTrackingInterval, actual.AutoSessionTrackingInterval); - Assert.AreEqual(expected.AttachStacktrace, actual.AttachStacktrace); - Assert.AreEqual(expected.AttachScreenshot, actual.AttachScreenshot); - Assert.AreEqual(expected.MaxBreadcrumbs, actual.MaxBreadcrumbs); - Assert.AreEqual(expected.ReportAssembliesMode, actual.ReportAssembliesMode); - Assert.AreEqual(expected.SendDefaultPii, actual.SendDefaultPii); - Assert.AreEqual(expected.IsEnvironmentUser, actual.IsEnvironmentUser); - Assert.AreEqual(expected.MaxCacheItems, actual.MaxCacheItems); - Assert.AreEqual(expected.InitCacheFlushTimeout, actual.InitCacheFlushTimeout); - Assert.AreEqual(expected.SampleRate, actual.SampleRate); - Assert.AreEqual(expected.ShutdownTimeout, actual.ShutdownTimeout); - Assert.AreEqual(expected.MaxQueueItems, actual.MaxQueueItems); - Assert.AreEqual(expected.Release, actual.Release); - Assert.AreEqual(expected.Environment, actual.Environment); - Assert.AreEqual(expected.CacheDirectoryPath, actual.CacheDirectoryPath); - Assert.AreEqual(expected.Debug, actual.Debug); - Assert.AreEqual(expected.DiagnosticLevel, actual.DiagnosticLevel); - } - - private static string GetTestOptionsFilePath() - { - var assemblyFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - Assert.NotNull(assemblyFolderPath); - return Path.Combine(assemblyFolderPath!, TestSentryOptionsFileName); - } + Assert.IsNull(options.CacheDirectoryPath); + Assert.IsFalse(options.AutoSessionTracking); + } + + public static void AssertOptions(SentryUnityOptions expected, SentryUnityOptions actual) + { + Assert.AreEqual(expected.Enabled, actual.Enabled); + Assert.AreEqual(expected.Dsn, actual.Dsn); + Assert.AreEqual(expected.CaptureInEditor, actual.CaptureInEditor); + Assert.AreEqual(expected.EnableLogDebouncing, actual.EnableLogDebouncing); + Assert.AreEqual(expected.TracesSampleRate, actual.TracesSampleRate); + Assert.AreEqual(expected.AutoSessionTracking, actual.AutoSessionTracking); + Assert.AreEqual(expected.AutoSessionTrackingInterval, actual.AutoSessionTrackingInterval); + Assert.AreEqual(expected.AttachStacktrace, actual.AttachStacktrace); + Assert.AreEqual(expected.AttachScreenshot, actual.AttachScreenshot); + Assert.AreEqual(expected.MaxBreadcrumbs, actual.MaxBreadcrumbs); + Assert.AreEqual(expected.ReportAssembliesMode, actual.ReportAssembliesMode); + Assert.AreEqual(expected.SendDefaultPii, actual.SendDefaultPii); + Assert.AreEqual(expected.IsEnvironmentUser, actual.IsEnvironmentUser); + Assert.AreEqual(expected.MaxCacheItems, actual.MaxCacheItems); + Assert.AreEqual(expected.InitCacheFlushTimeout, actual.InitCacheFlushTimeout); + Assert.AreEqual(expected.SampleRate, actual.SampleRate); + Assert.AreEqual(expected.ShutdownTimeout, actual.ShutdownTimeout); + Assert.AreEqual(expected.MaxQueueItems, actual.MaxQueueItems); + Assert.AreEqual(expected.Release, actual.Release); + Assert.AreEqual(expected.Environment, actual.Environment); + Assert.AreEqual(expected.CacheDirectoryPath, actual.CacheDirectoryPath); + Assert.AreEqual(expected.Debug, actual.Debug); + Assert.AreEqual(expected.DiagnosticLevel, actual.DiagnosticLevel); + } + + private static string GetTestOptionsFilePath() + { + var assemblyFolderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + Assert.NotNull(assemblyFolderPath); + return Path.Combine(assemblyFolderPath!, TestSentryOptionsFileName); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs b/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs index 8c2c48da6..bc4aab423 100644 --- a/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs +++ b/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs @@ -2,81 +2,80 @@ using Sentry.Unity.Tests.Stubs; using UnityEngine; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class SentryMonoBehaviourTests { - public class SentryMonoBehaviourTests + private class Fixture { - private class Fixture + public SentryMonoBehaviour GetSut() { - public SentryMonoBehaviour GetSut() - { - var gameObject = new GameObject("PauseTest"); - var sentryMonoBehaviour = gameObject.AddComponent(); - sentryMonoBehaviour.Application = new TestApplication(); - - return sentryMonoBehaviour; - } + var gameObject = new GameObject("PauseTest"); + var sentryMonoBehaviour = gameObject.AddComponent(); + sentryMonoBehaviour.Application = new TestApplication(); + + return sentryMonoBehaviour; } + } - private Fixture _fixture = null!; + private Fixture _fixture = null!; - [SetUp] - public void SetUp() => _fixture = new Fixture(); + [SetUp] + public void SetUp() => _fixture = new Fixture(); - [Test] - public void OnApplicationPause_PauseStatusTrue_ApplicationPausingInvoked() - { - var wasPausingCalled = false; + [Test] + public void OnApplicationPause_PauseStatusTrue_ApplicationPausingInvoked() + { + var wasPausingCalled = false; - var sut = _fixture.GetSut(); - sut.ApplicationPausing += () => wasPausingCalled = true; + var sut = _fixture.GetSut(); + sut.ApplicationPausing += () => wasPausingCalled = true; - sut.OnApplicationPause(true); + sut.OnApplicationPause(true); - Assert.IsTrue(wasPausingCalled); - } + Assert.IsTrue(wasPausingCalled); + } - [Test] - public void OnApplicationFocus_FocusFalse_ApplicationPausingInvoked() - { - var wasPausingCalled = false; + [Test] + public void OnApplicationFocus_FocusFalse_ApplicationPausingInvoked() + { + var wasPausingCalled = false; - var sut = _fixture.GetSut(); - sut.ApplicationPausing += () => wasPausingCalled = true; + var sut = _fixture.GetSut(); + sut.ApplicationPausing += () => wasPausingCalled = true; - sut.OnApplicationFocus(false); + sut.OnApplicationFocus(false); - Assert.IsTrue(wasPausingCalled); - } + Assert.IsTrue(wasPausingCalled); + } - [Test] - public void UpdatePauseStatus_PausedTwice_ApplicationPausingInvokedOnlyOnce() - { - var counter = 0; + [Test] + public void UpdatePauseStatus_PausedTwice_ApplicationPausingInvokedOnlyOnce() + { + var counter = 0; - var sut = _fixture.GetSut(); - sut.ApplicationPausing += () => counter++; + var sut = _fixture.GetSut(); + sut.ApplicationPausing += () => counter++; - sut.UpdatePauseStatus(true); - sut.UpdatePauseStatus(true); + sut.UpdatePauseStatus(true); + sut.UpdatePauseStatus(true); - Assert.AreEqual(1, counter); - } + Assert.AreEqual(1, counter); + } - [Test] - public void UpdatePauseStatus_ResumedTwice_ApplicationResumingInvokedOnlyOnce() - { - var counter = 0; + [Test] + public void UpdatePauseStatus_ResumedTwice_ApplicationResumingInvokedOnlyOnce() + { + var counter = 0; - var sut = _fixture.GetSut(); - sut.ApplicationResuming += () => counter++; - // We need to pause it first to resume it. - sut.UpdatePauseStatus(true); + var sut = _fixture.GetSut(); + sut.ApplicationResuming += () => counter++; + // We need to pause it first to resume it. + sut.UpdatePauseStatus(true); - sut.UpdatePauseStatus(false); - sut.UpdatePauseStatus(false); + sut.UpdatePauseStatus(false); + sut.UpdatePauseStatus(false); - Assert.AreEqual(1, counter); - } + Assert.AreEqual(1, counter); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SentryTests.cs b/test/Sentry.Unity.Tests/SentryTests.cs index c715fcdb7..99e9d30c2 100644 --- a/test/Sentry.Unity.Tests/SentryTests.cs +++ b/test/Sentry.Unity.Tests/SentryTests.cs @@ -1,28 +1,27 @@ using System; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public static class SentryTests { - public static class SentryTests + internal static IDisposable InitSentrySdk(Action? configure = null, TestHttpClientHandler? testHttpClientHandler = null) { - internal static IDisposable InitSentrySdk(Action? configure = null, TestHttpClientHandler? testHttpClientHandler = null) + SentryUnity.Init(options => { - SentryUnity.Init(options => + options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; + if (testHttpClientHandler is not null) { - options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; - if (testHttpClientHandler is not null) - { - options.CreateHttpMessageHandler = () => testHttpClientHandler; - } + options.CreateHttpMessageHandler = () => testHttpClientHandler; + } - configure?.Invoke(options); - }); + configure?.Invoke(options); + }); - return new SentryDisposable(); - } + return new SentryDisposable(); + } - private sealed class SentryDisposable : IDisposable - { - public void Dispose() => SentrySdk.Close(); - } + private sealed class SentryDisposable : IDisposable + { + public void Dispose() => SentrySdk.Close(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs index ada256703..f040e2019 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs @@ -1,133 +1,132 @@ using NUnit.Framework; using Sentry.Unity.Tests.Stubs; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class SentryUnityOptionsExtensionsTests { - public class SentryUnityOptionsExtensionsTests + private class Fixture { - private class Fixture - { - public TestApplication TestApplication { get; set; } = new(); - public bool Enabled { get; set; } = true; - public string Dsn { get; set; } = "http://test.com"; - public bool CaptureInEditor { get; set; } = true; - public bool Debug { get; set; } = true; - - public SentryUnityOptions GetSut() => new() - { - Enabled = Enabled, - Dsn = Dsn, - CaptureInEditor = CaptureInEditor, - Debug = Debug, - }; - } - - private Fixture _fixture = new(); - - [SetUp] - public void SetUp() => _fixture = new Fixture(); - - [Test] - public void Validate_OptionsDisabled_ReturnsFalse() + public TestApplication TestApplication { get; set; } = new(); + public bool Enabled { get; set; } = true; + public string Dsn { get; set; } = "http://test.com"; + public bool CaptureInEditor { get; set; } = true; + public bool Debug { get; set; } = true; + + public SentryUnityOptions GetSut() => new() { - _fixture.Enabled = false; - var options = _fixture.GetSut(); + Enabled = Enabled, + Dsn = Dsn, + CaptureInEditor = CaptureInEditor, + Debug = Debug, + }; + } - var isValid = options.IsValid(); + private Fixture _fixture = new(); - Assert.IsFalse(isValid); - } + [SetUp] + public void SetUp() => _fixture = new Fixture(); - [Test] - public void Validate_DsnEmpty_ReturnsFalse() - { - _fixture.Dsn = string.Empty; - var options = _fixture.GetSut(); + [Test] + public void Validate_OptionsDisabled_ReturnsFalse() + { + _fixture.Enabled = false; + var options = _fixture.GetSut(); - var isValid = options.IsValid(); + var isValid = options.IsValid(); - Assert.IsFalse(isValid); - } + Assert.IsFalse(isValid); + } - [Test] - public void ShouldInitializeSdk_CorrectlyConfiguredForEditor_ReturnsTrue() - { - var options = _fixture.GetSut(); + [Test] + public void Validate_DsnEmpty_ReturnsFalse() + { + _fixture.Dsn = string.Empty; + var options = _fixture.GetSut(); - var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); + var isValid = options.IsValid(); - Assert.IsTrue(shouldInitialize); - } + Assert.IsFalse(isValid); + } - [Test] - public void ShouldInitializeSdk_OptionsNull_ReturnsFalse() - { - _fixture.TestApplication = new TestApplication(false); - SentryUnityOptions? options = null; + [Test] + public void ShouldInitializeSdk_CorrectlyConfiguredForEditor_ReturnsTrue() + { + var options = _fixture.GetSut(); - var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); + var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); - Assert.IsFalse(shouldInitialize); - } + Assert.IsTrue(shouldInitialize); + } - [Test] - public void ShouldInitializeSdk_CorrectlyConfigured_ReturnsTrue() - { - _fixture.TestApplication = new TestApplication(false); - var options = _fixture.GetSut(); + [Test] + public void ShouldInitializeSdk_OptionsNull_ReturnsFalse() + { + _fixture.TestApplication = new TestApplication(false); + SentryUnityOptions? options = null; + + var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); - var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); + Assert.IsFalse(shouldInitialize); + } + + [Test] + public void ShouldInitializeSdk_CorrectlyConfigured_ReturnsTrue() + { + _fixture.TestApplication = new TestApplication(false); + var options = _fixture.GetSut(); - Assert.IsTrue(shouldInitialize); - } + var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); - [Test] - public void ShouldInitializeSdk_NotCaptureInEditorAndApplicationIsEditor_ReturnsFalse() - { - _fixture.CaptureInEditor = false; - var options = _fixture.GetSut(); + Assert.IsTrue(shouldInitialize); + } - var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); + [Test] + public void ShouldInitializeSdk_NotCaptureInEditorAndApplicationIsEditor_ReturnsFalse() + { + _fixture.CaptureInEditor = false; + var options = _fixture.GetSut(); - Assert.IsFalse(shouldInitialize); - } + var shouldInitialize = options.ShouldInitializeSdk(_fixture.TestApplication); - [Test] - public void SetupLogging_DebugAndNoDiagnosticLogger_SetsUnityLogger() - { - var options = _fixture.GetSut(); + Assert.IsFalse(shouldInitialize); + } - Assert.IsNull(options.DiagnosticLogger); // Sanity check + [Test] + public void SetupLogging_DebugAndNoDiagnosticLogger_SetsUnityLogger() + { + var options = _fixture.GetSut(); - options.SetupLogging(); + Assert.IsNull(options.DiagnosticLogger); // Sanity check - Assert.IsInstanceOf(options.DiagnosticLogger); - } + options.SetupLogging(); - [Test] - public void SetupLogging_DebugFalse_DiagnosticLoggerIsNull() - { - _fixture.Debug = false; - var options = _fixture.GetSut(); - options.DiagnosticLogger = new UnityLogger(options); + Assert.IsInstanceOf(options.DiagnosticLogger); + } + + [Test] + public void SetupLogging_DebugFalse_DiagnosticLoggerIsNull() + { + _fixture.Debug = false; + var options = _fixture.GetSut(); + options.DiagnosticLogger = new UnityLogger(options); - options.SetupLogging(); + options.SetupLogging(); - Assert.IsNull(options.DiagnosticLogger); - } + Assert.IsNull(options.DiagnosticLogger); + } - [Test] - [TestCase(true)] - [TestCase(false)] - public void SetupLogging_DiagnosticLoggerSet_LeavesOrRemovesDiagnosticLogger(bool debug) - { - _fixture.Debug = debug; - var options = _fixture.GetSut(); - options.DiagnosticLogger = new UnityLogger(options); + [Test] + [TestCase(true)] + [TestCase(false)] + public void SetupLogging_DiagnosticLoggerSet_LeavesOrRemovesDiagnosticLogger(bool debug) + { + _fixture.Debug = debug; + var options = _fixture.GetSut(); + options.DiagnosticLogger = new UnityLogger(options); - options.SetupLogging(); + options.SetupLogging(); - Assert.AreEqual(debug, options.DiagnosticLogger is not null); - } + Assert.AreEqual(debug, options.DiagnosticLogger is not null); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs index cc372810c..9046dd4da 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs @@ -1,79 +1,77 @@ using NUnit.Framework; using Sentry.Unity.Tests.Stubs; -using UnityEngine; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class SentryUnityOptionsTests { - public sealed class SentryUnityOptionsTests + class Fixture { - class Fixture - { - public TestApplication Application { get; set; } = new( - productName: "TestApplication", - version: "0.1.0", - buildGUID: "12345", - persistentDataPath: "test/persistent/data/path"); - public bool IsBuilding { get; set; } - - public SentryUnityOptions GetSut() => new SentryUnityOptions(IsBuilding, Application); - } + public TestApplication Application { get; set; } = new( + productName: "TestApplication", + version: "0.1.0", + buildGUID: "12345", + persistentDataPath: "test/persistent/data/path"); + public bool IsBuilding { get; set; } - [SetUp] - public void Setup() => _fixture = new Fixture(); - private Fixture _fixture = null!; + public SentryUnityOptions GetSut() => new SentryUnityOptions(IsBuilding, Application); + } - [Test] - [TestCase(true, true, "production")] - [TestCase(true, false, "editor")] - [TestCase(false, false, "production")] - [TestCase(false, true, "production")] - public void Ctor_Environment_IsNull(bool isEditor, bool isBuilding, string expectedEnvironment) - { - _fixture.Application = new TestApplication(isEditor: isEditor); - _fixture.IsBuilding = isBuilding; + [SetUp] + public void Setup() => _fixture = new Fixture(); + private Fixture _fixture = null!; - var sut = _fixture.GetSut(); + [Test] + [TestCase(true, true, "production")] + [TestCase(true, false, "editor")] + [TestCase(false, false, "production")] + [TestCase(false, true, "production")] + public void Ctor_Environment_IsNull(bool isEditor, bool isBuilding, string expectedEnvironment) + { + _fixture.Application = new TestApplication(isEditor: isEditor); + _fixture.IsBuilding = isBuilding; - Assert.AreEqual(expectedEnvironment, sut.Environment); - } + var sut = _fixture.GetSut(); - [Test] - public void Ctor_CacheDirectoryPath_IsNull() => Assert.IsNull(_fixture.GetSut().CacheDirectoryPath); + Assert.AreEqual(expectedEnvironment, sut.Environment); + } - [Test] - public void Ctor_IsGlobalModeEnabled_IsTrue() => Assert.IsTrue(_fixture.GetSut().IsGlobalModeEnabled); + [Test] + public void Ctor_CacheDirectoryPath_IsNull() => Assert.IsNull(_fixture.GetSut().CacheDirectoryPath); - [Test] - public void Ctor_Release_IsProductNameAtVersion() => - Assert.AreEqual( - $"{_fixture.Application.ProductName}@{_fixture.Application.Version}", - _fixture.GetSut().Release); + [Test] + public void Ctor_IsGlobalModeEnabled_IsTrue() => Assert.IsTrue(_fixture.GetSut().IsGlobalModeEnabled); - [Test] - [TestCase("\n")] - [TestCase("\t")] - [TestCase("/")] - [TestCase("\\")] - [TestCase("..")] - [TestCase("@")] - public void Ctor_Release_DoesNotContainInvalidCharacters(string invalidString) - { - var prefix = "test"; - var suffix = "application"; - var version = "0.1.0"; - _fixture.Application = new TestApplication(productName: $"{prefix}{invalidString}{suffix}", version: version); + [Test] + public void Ctor_Release_IsProductNameAtVersion() => + Assert.AreEqual( + $"{_fixture.Application.ProductName}@{_fixture.Application.Version}", + _fixture.GetSut().Release); - Assert.AreEqual($"{prefix}_{suffix}@{version}", _fixture.GetSut().Release); - } + [Test] + [TestCase("\n")] + [TestCase("\t")] + [TestCase("/")] + [TestCase("\\")] + [TestCase("..")] + [TestCase("@")] + public void Ctor_Release_DoesNotContainInvalidCharacters(string invalidString) + { + var prefix = "test"; + var suffix = "application"; + var version = "0.1.0"; + _fixture.Application = new TestApplication(productName: $"{prefix}{invalidString}{suffix}", version: version); - [Test] - public void Ctor_Release_IgnoresDotOnlyProductNames() - { - _fixture.Application = new TestApplication(productName: ".........", version: "0.1.0"); - Assert.AreEqual($"{_fixture.Application.Version}", _fixture.GetSut().Release); - } + Assert.AreEqual($"{prefix}_{suffix}@{version}", _fixture.GetSut().Release); + } - [Test] - public void Ctor_IsEnvironmentUser_IsFalse() => Assert.AreEqual(false, _fixture.GetSut().IsEnvironmentUser); + [Test] + public void Ctor_Release_IgnoresDotOnlyProductNames() + { + _fixture.Application = new TestApplication(productName: ".........", version: "0.1.0"); + Assert.AreEqual($"{_fixture.Application.Version}", _fixture.GetSut().Release); } -} + + [Test] + public void Ctor_IsEnvironmentUser_IsFalse() => Assert.AreEqual(false, _fixture.GetSut().IsEnvironmentUser); +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SentryUnityTests.cs b/test/Sentry.Unity.Tests/SentryUnityTests.cs index a778e9308..4adea2f32 100644 --- a/test/Sentry.Unity.Tests/SentryUnityTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityTests.cs @@ -6,104 +6,102 @@ using Debug = UnityEngine.Debug; using Sentry.Extensibility; using Sentry.Unity.Tests.SharedClasses; -using UnityEditor.PackageManager; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class SentryUnitySelfInitializationTests { - public class SentryUnitySelfInitializationTests + [TearDown] + public void TearDown() { - [TearDown] - public void TearDown() + if (SentrySdk.IsEnabled) { - if (SentrySdk.IsEnabled) - { - SentryUnity.Close(); - } + SentryUnity.Close(); } + } + + [Test] + public void AsyncStackTrace() + { + var options = new SentryUnityOptions(); + options.AttachStacktrace = true; + var sut = new SentryStackTraceFactory(options); - [Test] - public void AsyncStackTrace() + IList framesSentry = null!; + StackFrame[] framesManual = null!; + Task.Run(() => + { + var stackTrace = new StackTrace(true); + framesManual = stackTrace.GetFrames(); + + var sentryStackTrace = sut.Create()!; + var framesReversed = new System.Collections.Generic.List(sentryStackTrace.Frames); + framesReversed.Reverse(); + framesSentry = framesReversed; + return 42; // returning a value here messes up a stack trace + }).Wait(); + + Debug.Log("Manually captured stack trace:"); + foreach (var frame in framesManual) { - var options = new SentryUnityOptions(); - options.AttachStacktrace = true; - var sut = new SentryStackTraceFactory(options); - - IList framesSentry = null!; - StackFrame[] framesManual = null!; - Task.Run(() => - { - var stackTrace = new StackTrace(true); - framesManual = stackTrace.GetFrames(); - - var sentryStackTrace = sut.Create()!; - var framesReversed = new System.Collections.Generic.List(sentryStackTrace.Frames); - framesReversed.Reverse(); - framesSentry = framesReversed; - return 42; // returning a value here messes up a stack trace - }).Wait(); - - Debug.Log("Manually captured stack trace:"); - foreach (var frame in framesManual) - { - Debug.Log($" {frame} in {frame.GetMethod()?.DeclaringType?.FullName}"); - } - - Debug.Log(""); - - Debug.Log("Sentry captured stack trace:"); - foreach (var frame in framesSentry) - { - Debug.Log($" {frame.Function} in {frame.Module} from ({frame.Package})"); - } - - // Sentry captured frame must be cleaned up - the return type removed from the module (method name) - Assert.AreEqual("System.Threading.Tasks.Task`1", framesSentry[0].Module); - - // Sanity check - the manually captured stack frame must contain the wrong format method - Assert.IsTrue(framesManual[1].GetMethod()?.DeclaringType?.FullName?.StartsWith("System.Threading.Tasks.Task`1[[System.Int32")); + Debug.Log($" {frame} in {frame.GetMethod()?.DeclaringType?.FullName}"); } - [Test] - public void SentryUnity_OptionsValid_Initializes() + Debug.Log(""); + + Debug.Log("Sentry captured stack trace:"); + foreach (var frame in framesSentry) { - var options = new SentryUnityOptions - { - Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880" - }; + Debug.Log($" {frame.Function} in {frame.Module} from ({frame.Package})"); + } - SentryUnity.Init(options); + // Sentry captured frame must be cleaned up - the return type removed from the module (method name) + Assert.AreEqual("System.Threading.Tasks.Task`1", framesSentry[0].Module); - Assert.IsTrue(SentrySdk.IsEnabled); - } + // Sanity check - the manually captured stack frame must contain the wrong format method + Assert.IsTrue(framesManual[1].GetMethod()?.DeclaringType?.FullName?.StartsWith("System.Threading.Tasks.Task`1[[System.Int32")); + } - [Test] - public void SentryUnity_OptionsInvalid_DoesNotInitialize() + [Test] + public void SentryUnity_OptionsValid_Initializes() + { + var options = new SentryUnityOptions { - var options = new SentryUnityOptions(); + Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880" + }; - // Even tho the defaults are set the DSN is missing making the options invalid for initialization - SentryUnity.Init(options); + SentryUnity.Init(options); - Assert.IsFalse(SentrySdk.IsEnabled); - } + Assert.IsTrue(SentrySdk.IsEnabled); + } + + [Test] + public void SentryUnity_OptionsInvalid_DoesNotInitialize() + { + var options = new SentryUnityOptions(); + + // Even tho the defaults are set the DSN is missing making the options invalid for initialization + SentryUnity.Init(options); + + Assert.IsFalse(SentrySdk.IsEnabled); + } - [Test] - public void Init_MultipleTimes_LogsWarning() + [Test] + public void Init_MultipleTimes_LogsWarning() + { + var testLogger = new TestLogger(); + var options = new SentryUnityOptions { - var testLogger = new TestLogger(); - var options = new SentryUnityOptions - { - Debug = true, - Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880", - DiagnosticLogger = testLogger, - }; - - SentryUnity.Init(options); - SentryUnity.Init(options); - - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Warning && - log.message.Contains("The SDK has already been initialized."))); - } + Debug = true, + Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880", + DiagnosticLogger = testLogger, + }; + + SentryUnity.Init(options); + SentryUnity.Init(options); + + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Warning && + log.message.Contains("The SDK has already been initialized."))); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/SessionIntegrationTests.cs b/test/Sentry.Unity.Tests/SessionIntegrationTests.cs index 908d9ab09..656f72c1b 100644 --- a/test/Sentry.Unity.Tests/SessionIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/SessionIntegrationTests.cs @@ -4,41 +4,40 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class SessionIntegrationTests { - public class SessionIntegrationTests + [UnityTest] + public IEnumerator SessionIntegration_Init_SentryMonoBehaviourCreated() { - [UnityTest] - public IEnumerator SessionIntegration_Init_SentryMonoBehaviourCreated() - { - yield return null; + yield return null; - using var _ = InitSentrySdk(o => - { - // o.AutoSessionTracking = true; We expect this to be true by default - }); + using var _ = InitSentrySdk(o => + { + // o.AutoSessionTracking = true; We expect this to be true by default + }); - var sentryGameObject = GameObject.Find("SentryMonoBehaviour"); - var sentryMonoBehaviour = sentryGameObject.GetComponent(); + var sentryGameObject = GameObject.Find("SentryMonoBehaviour"); + var sentryMonoBehaviour = sentryGameObject.GetComponent(); - Assert.IsNotNull(sentryGameObject); - Assert.IsNotNull(sentryMonoBehaviour); - } + Assert.IsNotNull(sentryGameObject); + Assert.IsNotNull(sentryMonoBehaviour); + } - internal IDisposable InitSentrySdk(Action? configure = null) + internal IDisposable InitSentrySdk(Action? configure = null) + { + SentryUnity.Init(options => { - SentryUnity.Init(options => - { - options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; - configure?.Invoke(options); - }); + options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; + configure?.Invoke(options); + }); - return new SentryDisposable(); - } + return new SentryDisposable(); + } - private sealed class SentryDisposable : IDisposable - { - public void Dispose() => SentrySdk.Close(); - } + private sealed class SentryDisposable : IDisposable + { + public void Dispose() => SentrySdk.Close(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/Stubs/TestHub.cs b/test/Sentry.Unity.Tests/Stubs/TestHub.cs index b056640e3..6eb4cd442 100644 --- a/test/Sentry.Unity.Tests/Stubs/TestHub.cs +++ b/test/Sentry.Unity.Tests/Stubs/TestHub.cs @@ -3,170 +3,169 @@ using System.Threading.Tasks; using Sentry.Protocol.Envelopes; -namespace Sentry.Unity.Tests.Stubs +namespace Sentry.Unity.Tests.Stubs; + +internal sealed class TestHub : IHub { - internal sealed class TestHub : IHub - { - private readonly List _capturedEvents = new(); - private readonly List> _configureScopeCalls = new(); - - public IReadOnlyList CapturedEvents => _capturedEvents; - public IReadOnlyList> ConfigureScopeCalls => _configureScopeCalls; - - public TestHub(bool isEnabled = true) - { - IsEnabled = isEnabled; - Metrics = null!; // TODO: Don't do it like that - } - public bool IsEnabled { get; } - - public SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null) - { - _capturedEvents.Add(evt); - return evt.EventId; - } - - public void CaptureUserFeedback(UserFeedback userFeedback) - { - throw new NotImplementedException(); - } - - public void CaptureTransaction(SentryTransaction transaction) - { - } - - public void CaptureTransaction(SentryTransaction transaction, Scope? scope, SentryHint? hint) - { - } - - public void CaptureTransaction(SentryTransaction transaction, SentryHint? hint) - { - throw new NotImplementedException(); - } - - public void CaptureSession(SessionUpdate sessionUpdate) - { - } - - public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null, TimeSpan? duration = null, - Scope? scope = null, Action? configureMonitorOptions = null) - { - throw new NotImplementedException(); - } - - public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null, TimeSpan? duration = null, - Scope? scope = null) - { - throw new NotImplementedException(); - } - - public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null) - { - throw new NotImplementedException(); - } - - public bool CaptureEnvelope(Envelope envelope) - { - throw new NotImplementedException(); - } - - public Task FlushAsync(TimeSpan timeout) - { - return Task.CompletedTask; - } - - public void ConfigureScope(Action configureScope) => _configureScopeCalls.Add(configureScope); - - public Task ConfigureScopeAsync(Func configureScope) => Task.CompletedTask; - - public void BindClient(ISentryClient client) - { - throw new NotImplementedException(); - } - - public IDisposable PushScope() - { - throw new NotImplementedException(); - } - - public IDisposable PushScope(TState state) - { - throw new NotImplementedException(); - } - - public void WithScope(Action scopeCallback) - { - throw new NotImplementedException(); - } - - public SentryId LastEventId { get; } - public IMetricAggregator Metrics { get; } - - public ITransactionTracer StartTransaction(ITransactionContext context, IReadOnlyDictionary customSamplingContext) - { - throw new NotImplementedException(); - } - - public void BindException(Exception exception, ISpan span) - { - throw new NotImplementedException(); - } - - ISpan? IHub.GetSpan() - { - throw new NotImplementedException(); - } - - public SentryTraceHeader? GetTraceHeader() - { - throw new NotImplementedException(); - } - - public BaggageHeader? GetBaggage() - { - throw new NotImplementedException(); - } - - public TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, - string? operation = null) - { - throw new NotImplementedException(); - } - - public TransactionContext ContinueTrace(SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader, string? name = null, - string? operation = null) - { - throw new NotImplementedException(); - } - - public void StartSession() - { - // TODO: test sessions - } - - public void PauseSession() - { - // TODO: test sessions - } - - public void ResumeSession() - { - // TODO: test sessions - } - - public void EndSession(SessionEndStatus status = SessionEndStatus.Exited) - { - // TODO: test sessions - } - - public SentryId CaptureEvent(SentryEvent evt, Action configureScope) - { - throw new NotImplementedException(); - } - - public SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Action configureScope) - { - throw new NotImplementedException(); - } - } -} + private readonly List _capturedEvents = new(); + private readonly List> _configureScopeCalls = new(); + + public IReadOnlyList CapturedEvents => _capturedEvents; + public IReadOnlyList> ConfigureScopeCalls => _configureScopeCalls; + + public TestHub(bool isEnabled = true) + { + IsEnabled = isEnabled; + Metrics = null!; // TODO: Don't do it like that + } + public bool IsEnabled { get; } + + public SentryId CaptureEvent(SentryEvent evt, Scope? scope = null, SentryHint? hint = null) + { + _capturedEvents.Add(evt); + return evt.EventId; + } + + public void CaptureUserFeedback(UserFeedback userFeedback) + { + throw new NotImplementedException(); + } + + public void CaptureTransaction(SentryTransaction transaction) + { + } + + public void CaptureTransaction(SentryTransaction transaction, Scope? scope, SentryHint? hint) + { + } + + public void CaptureTransaction(SentryTransaction transaction, SentryHint? hint) + { + throw new NotImplementedException(); + } + + public void CaptureSession(SessionUpdate sessionUpdate) + { + } + + public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null, TimeSpan? duration = null, + Scope? scope = null, Action? configureMonitorOptions = null) + { + throw new NotImplementedException(); + } + + public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null, TimeSpan? duration = null, + Scope? scope = null) + { + throw new NotImplementedException(); + } + + public SentryId CaptureCheckIn(string monitorSlug, CheckInStatus status, SentryId? sentryId = null) + { + throw new NotImplementedException(); + } + + public bool CaptureEnvelope(Envelope envelope) + { + throw new NotImplementedException(); + } + + public Task FlushAsync(TimeSpan timeout) + { + return Task.CompletedTask; + } + + public void ConfigureScope(Action configureScope) => _configureScopeCalls.Add(configureScope); + + public Task ConfigureScopeAsync(Func configureScope) => Task.CompletedTask; + + public void BindClient(ISentryClient client) + { + throw new NotImplementedException(); + } + + public IDisposable PushScope() + { + throw new NotImplementedException(); + } + + public IDisposable PushScope(TState state) + { + throw new NotImplementedException(); + } + + public void WithScope(Action scopeCallback) + { + throw new NotImplementedException(); + } + + public SentryId LastEventId { get; } + public IMetricAggregator Metrics { get; } + + public ITransactionTracer StartTransaction(ITransactionContext context, IReadOnlyDictionary customSamplingContext) + { + throw new NotImplementedException(); + } + + public void BindException(Exception exception, ISpan span) + { + throw new NotImplementedException(); + } + + ISpan? IHub.GetSpan() + { + throw new NotImplementedException(); + } + + public SentryTraceHeader? GetTraceHeader() + { + throw new NotImplementedException(); + } + + public BaggageHeader? GetBaggage() + { + throw new NotImplementedException(); + } + + public TransactionContext ContinueTrace(string? traceHeader, string? baggageHeader, string? name = null, + string? operation = null) + { + throw new NotImplementedException(); + } + + public TransactionContext ContinueTrace(SentryTraceHeader? traceHeader, BaggageHeader? baggageHeader, string? name = null, + string? operation = null) + { + throw new NotImplementedException(); + } + + public void StartSession() + { + // TODO: test sessions + } + + public void PauseSession() + { + // TODO: test sessions + } + + public void ResumeSession() + { + // TODO: test sessions + } + + public void EndSession(SessionEndStatus status = SessionEndStatus.Exited) + { + // TODO: test sessions + } + + public SentryId CaptureEvent(SentryEvent evt, Action configureScope) + { + throw new NotImplementedException(); + } + + public SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Action configureScope) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs b/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs index 8e293d9da..bbc12fc7b 100644 --- a/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs +++ b/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs @@ -3,28 +3,27 @@ using UnityEngine; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests.TestBehaviours +namespace Sentry.Unity.Tests.TestBehaviours; + +/* + * Behaviour we have access to from Tests project. + */ +internal sealed class TestMonoBehaviour : MonoBehaviour { - /* - * Behaviour we have access to from Tests project. - */ - internal sealed class TestMonoBehaviour : MonoBehaviour + public void ThrowException(string message) => throw new Exception(message); + public void DebugLogError(string message) => Debug.LogError(message); + public void DebugLogErrorInTask(string message) => Task.Run(() => { - public void ThrowException(string message) => throw new Exception(message); - public void DebugLogError(string message) => Debug.LogError(message); - public void DebugLogErrorInTask(string message) => Task.Run(() => - { - // Don't fail test if an error is logged via 'SendMessage'. We want to continue. - LogAssert.ignoreFailingMessages = true; - DebugLogError(message); - }); - public void DebugLogException(string message) => Debug.LogException(new Exception(message)); + // Don't fail test if an error is logged via 'SendMessage'. We want to continue. + LogAssert.ignoreFailingMessages = true; + DebugLogError(message); + }); + public void DebugLogException(string message) => Debug.LogException(new Exception(message)); - public void DebugLogExceptionInTask(string message) => Task.Run(() => - { - // Don't fail test if an exception is thrown via 'SendMessage'. We want to continue. - LogAssert.ignoreFailingMessages = true; - DebugLogException(message); - }); - } -} + public void DebugLogExceptionInTask(string message) => Task.Run(() => + { + // Don't fail test if an exception is thrown via 'SendMessage'. We want to continue. + LogAssert.ignoreFailingMessages = true; + DebugLogException(message); + }); +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/TestHttpClientHandler.cs b/test/Sentry.Unity.Tests/TestHttpClientHandler.cs index 5f2a0c56b..e8460c259 100644 --- a/test/Sentry.Unity.Tests/TestHttpClientHandler.cs +++ b/test/Sentry.Unity.Tests/TestHttpClientHandler.cs @@ -7,71 +7,70 @@ using System.Threading.Tasks; using Debug = UnityEngine.Debug; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class TestHttpClientHandler : HttpClientHandler { - public class TestHttpClientHandler : HttpClientHandler - { - private readonly string name; + private readonly string name; - private readonly List _requests = new(); - private readonly AutoResetEvent _requestReceived = new(false); + private readonly List _requests = new(); + private readonly AutoResetEvent _requestReceived = new(false); - public TestHttpClientHandler(string name = "TestHttpClientHandler") - { - this.name = name; - } + public TestHttpClientHandler(string name = "TestHttpClientHandler") + { + this.name = name; + } - protected override Task SendAsync(HttpRequestMessage request, - CancellationToken cancellationToken) + protected override Task SendAsync(HttpRequestMessage request, + CancellationToken cancellationToken) + { + Receive(request); + return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); + } + + private void Receive(HttpRequestMessage message) + { + var messageText = message.Content.ReadAsStringAsync().Result; + lock (_requests) { - Receive(request); - return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK)); + _requests.Add(messageText); + _requestReceived.Set(); } + } - private void Receive(HttpRequestMessage message) + public string GetEvent(string identifier, TimeSpan timeout) + { + // Check all the already received requests + lock (_requests) { - var messageText = message.Content.ReadAsStringAsync().Result; - lock (_requests) + var eventRequest = _requests.Find(r => r.Contains(identifier)); + if (!string.IsNullOrEmpty(eventRequest)) { - _requests.Add(messageText); - _requestReceived.Set(); + Debug.Log($"{UnityLogger.LogTag}{name} returns event:\n" + eventRequest); + return eventRequest; } } - public string GetEvent(string identifier, TimeSpan timeout) + // While within timeout: check every newly received request + var stopwatch = Stopwatch.StartNew(); + while (stopwatch.Elapsed < timeout) { - // Check all the already received requests - lock (_requests) + if (_requestReceived.WaitOne(TimeSpan.FromMilliseconds(16))) // Once per frame { - var eventRequest = _requests.Find(r => r.Contains(identifier)); - if (!string.IsNullOrEmpty(eventRequest)) + lock (_requests) { - Debug.Log($"{UnityLogger.LogTag}{name} returns event:\n" + eventRequest); - return eventRequest; - } - } - - // While within timeout: check every newly received request - var stopwatch = Stopwatch.StartNew(); - while (stopwatch.Elapsed < timeout) - { - if (_requestReceived.WaitOne(TimeSpan.FromMilliseconds(16))) // Once per frame - { - lock (_requests) + if (_requests.Count > 0 && _requests[_requests.Count - 1].Contains(identifier)) { - if (_requests.Count > 0 && _requests[_requests.Count - 1].Contains(identifier)) - { - var eventRequest = _requests[_requests.Count - 1]; - Debug.Log($"{UnityLogger.LogTag}{name} returns event:\n" + eventRequest); + var eventRequest = _requests[_requests.Count - 1]; + Debug.Log($"{UnityLogger.LogTag}{name} returns event:\n" + eventRequest); - return eventRequest; - } + return eventRequest; } } } - - Debug.LogError($"{UnityLogger.LogTag}{name} timed out waiting for an event."); - return string.Empty; } + + Debug.LogError($"{UnityLogger.LogTag}{name} timed out waiting for an event."); + return string.Empty; } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs index a1868acbf..da057ec45 100644 --- a/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs @@ -3,56 +3,55 @@ using Sentry.Unity.Integrations; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class UnityBadGatewayExceptionFilterTests { - public class UnityBadGatewayExceptionFilterTests - { - private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup - private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(0.5f); + private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup + private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(0.5f); - private string _identifyingEventValue = null!; // Set in Setup + private string _identifyingEventValue = null!; // Set in Setup - [SetUp] - public void SetUp() - { - _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); - _identifyingEventValue = Guid.NewGuid().ToString(); - } + [SetUp] + public void SetUp() + { + _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); + _identifyingEventValue = Guid.NewGuid().ToString(); + } - [Test] - public void Filter_FiltersBadGatewayExceptionsOfTypeException() => + [Test] + public void Filter_FiltersBadGatewayExceptionsOfTypeException() => Assert.IsTrue(new UnityBadGatewayExceptionFilter().Filter(new Exception(UnityBadGatewayExceptionFilter.Message))); - [Test] - public void Init_WithDefaultOptions_DoesNotSendBadGatewayExceptions() - { - LogAssert.ignoreFailingMessages = true; // The TestHttpClientHandler will complain about timing out (and it should!) + [Test] + public void Init_WithDefaultOptions_DoesNotSendBadGatewayExceptions() + { + LogAssert.ignoreFailingMessages = true; // The TestHttpClientHandler will complain about timing out (and it should!) - using var _ = SentryTests.InitSentrySdk(testHttpClientHandler: _testHttpClientHandler); + using var _ = SentryTests.InitSentrySdk(testHttpClientHandler: _testHttpClientHandler); - SentrySdk.CaptureException(new Exception(UnityBadGatewayExceptionFilter.Message + _identifyingEventValue)); + SentrySdk.CaptureException(new Exception(UnityBadGatewayExceptionFilter.Message + _identifyingEventValue)); - var createdEvent = _testHttpClientHandler.GetEvent(_identifyingEventValue, _eventReceiveTimeout); - Assert.AreEqual(string.Empty, createdEvent); - } + var createdEvent = _testHttpClientHandler.GetEvent(_identifyingEventValue, _eventReceiveTimeout); + Assert.AreEqual(string.Empty, createdEvent); + } - internal IDisposable InitSentrySdk(Action? configure = null) + internal IDisposable InitSentrySdk(Action? configure = null) + { + SentryUnity.Init(options => { - SentryUnity.Init(options => - { - options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; - options.CreateHttpMessageHandler = () => _testHttpClientHandler; + options.Dsn = "https://e9ee299dbf554dfd930bc5f3c90d5d4b@o447951.ingest.sentry.io/4504604988538880"; + options.CreateHttpMessageHandler = () => _testHttpClientHandler; - configure?.Invoke(options); - }); + configure?.Invoke(options); + }); - return new SentryDisposable(); - } + return new SentryDisposable(); + } - private sealed class SentryDisposable : IDisposable - { - public void Dispose() => SentrySdk.Close(); - } + private sealed class SentryDisposable : IDisposable + { + public void Dispose() => SentrySdk.Close(); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs index 0199ecd85..2437acf8c 100644 --- a/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs @@ -3,45 +3,44 @@ using Sentry.Unity.Integrations; using Sentry.Unity.Tests.Stubs; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class UnityBeforeSceneLoadIntegrationTests { - public sealed class UnityBeforeSceneLoadIntegrationTests + private class Fixture { - private class Fixture + public UnityBeforeSceneLoadIntegration GetSut(IHub hub, SentryOptions sentryOptions) { - public UnityBeforeSceneLoadIntegration GetSut(IHub hub, SentryOptions sentryOptions) - { - var application = new TestApplication(); - var integration = new UnityBeforeSceneLoadIntegration(application); - integration.Register(hub, sentryOptions); - return integration; - } + var application = new TestApplication(); + var integration = new UnityBeforeSceneLoadIntegration(application); + integration.Register(hub, sentryOptions); + return integration; } + } - private Fixture _fixture = null!; - private TestHub _hub = null!; - private SentryOptions _sentryOptions = null!; + private Fixture _fixture = null!; + private TestHub _hub = null!; + private SentryOptions _sentryOptions = null!; - [SetUp] - public void SetUp() - { - _fixture = new Fixture(); - _hub = new TestHub(); - _sentryOptions = new SentryOptions(); - } + [SetUp] + public void SetUp() + { + _fixture = new Fixture(); + _hub = new TestHub(); + _sentryOptions = new SentryOptions(); + } - [Test] - public void Register_Breadcrumb_Added() - { - _ = _fixture.GetSut(_hub, _sentryOptions); + [Test] + public void Register_Breadcrumb_Added() + { + _ = _fixture.GetSut(_hub, _sentryOptions); - var configureScope = _hub.ConfigureScopeCalls.Single(); - var scope = new Scope(_sentryOptions); - configureScope(scope); - var breadcrumb = scope.Breadcrumbs.Single(); + var configureScope = _hub.ConfigureScopeCalls.Single(); + var scope = new Scope(_sentryOptions); + configureScope(scope); + var breadcrumb = scope.Breadcrumbs.Single(); - Assert.AreEqual(1, _hub.ConfigureScopeCalls.Count); - Assert.AreEqual("scene.beforeload", breadcrumb.Category); - } + Assert.AreEqual(1, _hub.ConfigureScopeCalls.Count); + Assert.AreEqual("scene.beforeload", breadcrumb.Category); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index 3ffdafeb3..d48453fec 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -1,6 +1,4 @@ using System; -using System.Collections; -using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; @@ -10,601 +8,599 @@ using Sentry.Unity.Tests.SharedClasses; using Sentry.Unity.Tests.Stubs; using UnityEngine; -using UnityEngine.TestTools; using Object = UnityEngine.Object; // TODO do we need a real (working) DSN in these tests? -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class UnityEventProcessorThreadingTests { - public sealed class UnityEventProcessorThreadingTests + private GameObject _gameObject = null!; + private TestLogger _testLogger = null!; + private SentryMonoBehaviour _sentryMonoBehaviour = null!; + private TestApplication _testApplication = null!; + + [SetUp] + public void SetUp() + { + _gameObject = new GameObject("ScopeTest"); + _testLogger = new TestLogger(); + _sentryMonoBehaviour = _gameObject.AddComponent(); + _testApplication = new(); + } + + [TearDown] + public void TearDown() { - private GameObject _gameObject = null!; - private TestLogger _testLogger = null!; - private SentryMonoBehaviour _sentryMonoBehaviour = null!; - private TestApplication _testApplication = null!; + Object.Destroy(_gameObject); - [SetUp] - public void SetUp() + if (SentrySdk.IsEnabled) { - _gameObject = new GameObject("ScopeTest"); - _testLogger = new TestLogger(); - _sentryMonoBehaviour = _gameObject.AddComponent(); - _testApplication = new(); + SentryUnity.Close(); } + } - [TearDown] - public void TearDown() + public string FormatLogs(List<(SentryLevel, string, Exception?)> logs) + { + var sb = new StringBuilder() + .AppendLine("Logs found:"); + int counter = 1; + foreach (var log in logs) { - Object.Destroy(_gameObject); - - if (SentrySdk.IsEnabled) - { - SentryUnity.Close(); - } + sb = sb.AppendLine($"[{counter}] - Level: {log.Item1} - Message: {log.Item2} - Exception: {log.Item3}"); + counter++; } + return sb.AppendLine(" === END ===").ToString(); + } - public string FormatLogs(List<(SentryLevel, string, Exception?)> logs) + [Test] + public void SentrySdkCaptureEvent_OnNotUIThread_Succeeds() + { + // arrange + var options = new SentryUnityOptions { - var sb = new StringBuilder() - .AppendLine("Logs found:"); - int counter = 1; - foreach (var log in logs) - { - sb = sb.AppendLine($"[{counter}] - Level: {log.Item1} - Message: {log.Item2} - Exception: {log.Item3}"); - counter++; - } - return sb.AppendLine(" === END ===").ToString(); - } + Dsn = "https://a520c186ed684a8aa7d5d334bd7dab52@o447951.ingest.sentry.io/5801250", + Enabled = true, + AttachStacktrace = true, + Debug = true, + DiagnosticLogger = _testLogger + }; + SentryUnity.Init(options); - [Test] - public void SentrySdkCaptureEvent_OnNotUIThread_Succeeds() + var sentryEvent = new SentryEvent { - // arrange - var options = new SentryUnityOptions + Message = new SentryMessage { - Dsn = "https://a520c186ed684a8aa7d5d334bd7dab52@o447951.ingest.sentry.io/5801250", - Enabled = true, - AttachStacktrace = true, - Debug = true, - DiagnosticLogger = _testLogger - }; - SentryUnity.Init(options); + Message = NUnit.Framework.TestContext.CurrentContext.Test.Name + } + }; - var sentryEvent = new SentryEvent - { - Message = new SentryMessage - { - Message = NUnit.Framework.TestContext.CurrentContext.Test.Name - } - }; + // act + Task.Run(() => SentrySdk.CaptureEvent(sentryEvent)).Wait(); - // act - Task.Run(() => SentrySdk.CaptureEvent(sentryEvent)).Wait(); + SentrySdk.FlushAsync(TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); - SentrySdk.FlushAsync(TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); + // assert + var logsFound = _testLogger.Logs.Where(log => log.logLevel >= SentryLevel.Warning && log.message != "Cache directory is empty.").ToList(); - // assert - var logsFound = _testLogger.Logs.Where(log => log.logLevel >= SentryLevel.Warning && log.message != "Cache directory is empty.").ToList(); + Assert.Zero(logsFound.Count, FormatLogs(logsFound)); + + // Sanity check: At least some logs must have been printed + Assert.NotZero(_testLogger.Logs.Count(log => log.logLevel <= SentryLevel.Info)); + } - Assert.Zero(logsFound.Count, FormatLogs(logsFound)); + [Test] + [TestCase(true)] + [TestCase(false)] + public void SentrySdkCaptureEvent(bool collectOnUiThread) + { + // arrange + var sysInfo = _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo + { + GraphicsDeviceVendorId = new Lazy(() => "VendorId"), + GraphicsMultiThreaded = new Lazy(() => true), + DeviceType = new Lazy(() => "Android"), + DeviceModel = new Lazy(() => "DeviceModel"), + DeviceUniqueIdentifier = new Lazy(() => "83fdd6d4-50b1-4735-a4d1-d4f7de64aff0"), + IsDebugBuild = new Lazy(() => true), + TargetFrameRate = new Lazy(() => "-1"), + CopyTextureSupport = new Lazy(() => "Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture"), + RenderingThreadingMode = new Lazy(() => "MultiThreaded"), + StartTime = new(() => DateTimeOffset.UtcNow), + }; + var options = new SentryUnityOptions(_sentryMonoBehaviour, _testApplication, false) + { + Dsn = "https://b8fd848b31444e80aa102e96d2a6a648@o510466.ingest.sentry.io/5606182", + Enabled = true, + AttachStacktrace = true, + SendDefaultPii = true, // for Device.DeviceUniqueIdentifier + Debug = true, + DiagnosticLogger = _testLogger + }; - // Sanity check: At least some logs must have been printed - Assert.NotZero(_testLogger.Logs.Count(log => log.logLevel <= SentryLevel.Info)); + if (collectOnUiThread) + { + _sentryMonoBehaviour.CollectData(); } + else + { + // Note: Task.Run().Wait() may be executed on the main thread if it hasn't yet started when Wait() runs. + // We prevent it by explicitly sleeping on the main thread + var task = Task.Run(_sentryMonoBehaviour.CollectData); + Thread.Sleep(10); + task.Wait(); + } + + SentryUnity.Init(options); - [Test] - [TestCase(true)] - [TestCase(false)] - public void SentrySdkCaptureEvent(bool collectOnUiThread) + // act & assert + for (int i = 0; i <= 1; i++) { - // arrange - var sysInfo = _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo + var @event = new SentryEvent() { - GraphicsDeviceVendorId = new Lazy(() => "VendorId"), - GraphicsMultiThreaded = new Lazy(() => true), - DeviceType = new Lazy(() => "Android"), - DeviceModel = new Lazy(() => "DeviceModel"), - DeviceUniqueIdentifier = new Lazy(() => "83fdd6d4-50b1-4735-a4d1-d4f7de64aff0"), - IsDebugBuild = new Lazy(() => true), - TargetFrameRate = new Lazy(() => "-1"), - CopyTextureSupport = new Lazy(() => "Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture"), - RenderingThreadingMode = new Lazy(() => "MultiThreaded"), - StartTime = new(() => DateTimeOffset.UtcNow), - }; - var options = new SentryUnityOptions(_sentryMonoBehaviour, _testApplication, false) - { - Dsn = "https://b8fd848b31444e80aa102e96d2a6a648@o510466.ingest.sentry.io/5606182", - Enabled = true, - AttachStacktrace = true, - SendDefaultPii = true, // for Device.DeviceUniqueIdentifier - Debug = true, - DiagnosticLogger = _testLogger + Message = NUnit.Framework.TestContext.CurrentContext.Test.Name }; - if (collectOnUiThread) + // Events should have the same context, regardless of the thread they were issued on. + // The context only depends on the thread the data has been collected in, see CollectData() above. + if (i == 0) { - _sentryMonoBehaviour.CollectData(); + var task = Task.Run(() => SentrySdk.CaptureEvent(@event)); + Thread.Sleep(10); + task.Wait(); } else { - // Note: Task.Run().Wait() may be executed on the main thread if it hasn't yet started when Wait() runs. - // We prevent it by explicitly sleeping on the main thread - var task = Task.Run(_sentryMonoBehaviour.CollectData); - Thread.Sleep(10); - task.Wait(); + SentrySdk.CaptureEvent(@event); } + SentrySdk.FlushAsync(TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); - SentryUnity.Init(options); - - // act & assert - for (int i = 0; i <= 1; i++) + if (collectOnUiThread) { - var @event = new SentryEvent() - { - Message = NUnit.Framework.TestContext.CurrentContext.Test.Name - }; + Assert.AreEqual(sysInfo.GraphicsDeviceVendorId!.Value, @event.Contexts.Gpu.VendorId); + Assert.AreEqual(sysInfo.GraphicsMultiThreaded!.Value, @event.Contexts.Gpu.MultiThreadedRendering); + Assert.AreEqual(sysInfo.DeviceType!.Value, @event.Contexts.Device.DeviceType); + Assert.AreEqual(sysInfo.DeviceModel!.Value, @event.Contexts.Device.Model); + Assert.AreEqual(sysInfo.DeviceUniqueIdentifier!.Value, @event.Contexts.Device.DeviceUniqueIdentifier); + Assert.AreEqual(sysInfo.IsDebugBuild!.Value ? "debug" : "release", @event.Contexts.App.BuildType); + + @event.Contexts.TryGetValue(Unity.Protocol.Unity.Type, out var unityProtocolObject); + var unityContext = unityProtocolObject as Unity.Protocol.Unity; + Assert.IsNotNull(unityContext); + Assert.AreEqual(sysInfo.TargetFrameRate!.Value, unityContext!.TargetFrameRate); + Assert.AreEqual(sysInfo.CopyTextureSupport!.Value, unityContext.CopyTextureSupport); + Assert.AreEqual(sysInfo.RenderingThreadingMode!.Value, unityContext.RenderingThreadingMode); + } + else + { + Assert.IsNull(@event.Contexts.Gpu.VendorId); + Assert.IsNull(@event.Contexts.Gpu.MultiThreadedRendering); + Assert.IsNull(@event.Contexts.Device.DeviceType); + Assert.IsNull(@event.Contexts.Device.Model); + Assert.IsNull(@event.Contexts.Device.DeviceUniqueIdentifier); + Assert.IsNull(@event.Contexts.App.BuildType); + // TODO Assert.IsNull( @event.Contexts.App.StartTime); - // Events should have the same context, regardless of the thread they were issued on. - // The context only depends on the thread the data has been collected in, see CollectData() above. - if (i == 0) - { - var task = Task.Run(() => SentrySdk.CaptureEvent(@event)); - Thread.Sleep(10); - task.Wait(); - } - else - { - SentrySdk.CaptureEvent(@event); - } - SentrySdk.FlushAsync(TimeSpan.FromSeconds(1)).GetAwaiter().GetResult(); - if (collectOnUiThread) - { - Assert.AreEqual(sysInfo.GraphicsDeviceVendorId!.Value, @event.Contexts.Gpu.VendorId); - Assert.AreEqual(sysInfo.GraphicsMultiThreaded!.Value, @event.Contexts.Gpu.MultiThreadedRendering); - Assert.AreEqual(sysInfo.DeviceType!.Value, @event.Contexts.Device.DeviceType); - Assert.AreEqual(sysInfo.DeviceModel!.Value, @event.Contexts.Device.Model); - Assert.AreEqual(sysInfo.DeviceUniqueIdentifier!.Value, @event.Contexts.Device.DeviceUniqueIdentifier); - Assert.AreEqual(sysInfo.IsDebugBuild!.Value ? "debug" : "release", @event.Contexts.App.BuildType); - - @event.Contexts.TryGetValue(Unity.Protocol.Unity.Type, out var unityProtocolObject); - var unityContext = unityProtocolObject as Unity.Protocol.Unity; - Assert.IsNotNull(unityContext); - Assert.AreEqual(sysInfo.TargetFrameRate!.Value, unityContext!.TargetFrameRate); - Assert.AreEqual(sysInfo.CopyTextureSupport!.Value, unityContext.CopyTextureSupport); - Assert.AreEqual(sysInfo.RenderingThreadingMode!.Value, unityContext.RenderingThreadingMode); - } - else + Unity.Protocol.Unity? unityContext; + if (!@event.Contexts.TryGetValue(Unity.Protocol.Unity.Type, out var contextValue) || (unityContext = contextValue as Unity.Protocol.Unity) == null) { - Assert.IsNull(@event.Contexts.Gpu.VendorId); - Assert.IsNull(@event.Contexts.Gpu.MultiThreadedRendering); - Assert.IsNull(@event.Contexts.Device.DeviceType); - Assert.IsNull(@event.Contexts.Device.Model); - Assert.IsNull(@event.Contexts.Device.DeviceUniqueIdentifier); - Assert.IsNull(@event.Contexts.App.BuildType); - // TODO Assert.IsNull( @event.Contexts.App.StartTime); - - - Unity.Protocol.Unity? unityContext; - if (!@event.Contexts.TryGetValue(Unity.Protocol.Unity.Type, out var contextValue) || (unityContext = contextValue as Unity.Protocol.Unity) == null) - { - unityContext = new Unity.Protocol.Unity(); - @event.Contexts[Unity.Protocol.Unity.Type] = unityContext; - } - - Assert.IsNull(unityContext.TargetFrameRate); - Assert.IsNull(unityContext.CopyTextureSupport); - Assert.IsNull(unityContext.RenderingThreadingMode); + unityContext = new Unity.Protocol.Unity(); + @event.Contexts[Unity.Protocol.Unity.Type] = unityContext; } - Assert.IsNull(@event.ServerName); + + Assert.IsNull(unityContext.TargetFrameRate); + Assert.IsNull(unityContext.CopyTextureSupport); + Assert.IsNull(unityContext.RenderingThreadingMode); } + Assert.IsNull(@event.ServerName); } } +} - public sealed class UnityEventProcessorTests +public sealed class UnityEventProcessorTests +{ + private GameObject _gameObject = null!; + private SentryMonoBehaviour _sentryMonoBehaviour = null!; + private MainThreadData _mainThreadData => _sentryMonoBehaviour.MainThreadData; + private SentryUnityOptions _sentryOptions = null!; + private TestApplication _testApplication = null!; + + [SetUp] + public void SetUp() { - private GameObject _gameObject = null!; - private SentryMonoBehaviour _sentryMonoBehaviour = null!; - private MainThreadData _mainThreadData => _sentryMonoBehaviour.MainThreadData; - private SentryUnityOptions _sentryOptions = null!; - private TestApplication _testApplication = null!; - - [SetUp] - public void SetUp() - { - _gameObject = new GameObject("ProcessorTest"); - _sentryMonoBehaviour = _gameObject.AddComponent(); - _sentryOptions = new SentryUnityOptions - { - Debug = true, - DiagnosticLogger = new TestLogger() - }; - _testApplication = new(); - } - - [TearDown] - public void TearDown() - { - Object.Destroy(_gameObject); - } - - [Test] - public void SdkInfo_Correct() + _gameObject = new GameObject("ProcessorTest"); + _sentryMonoBehaviour = _gameObject.AddComponent(); + _sentryOptions = new SentryUnityOptions { - // arrange - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); + Debug = true, + DiagnosticLogger = new TestLogger() + }; + _testApplication = new(); + } - // act - sut.ConfigureScope(scope); + [TearDown] + public void TearDown() + { + Object.Destroy(_gameObject); + } - // assert - Assert.AreEqual(UnitySdkInfo.Name, scope.Sdk.Name); - Assert.AreEqual(UnitySdkInfo.Version, scope.Sdk.Version); + [Test] + public void SdkInfo_Correct() + { + // arrange + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); - var package = scope.Sdk.Packages.FirstOrDefault(); - Assert.IsNotNull(package); - Assert.AreEqual(UnitySdkInfo.PackageName, package!.Name); - Assert.AreEqual(UnitySdkInfo.Version, package!.Version); - } + // act + sut.ConfigureScope(scope); - [TestCaseSource(nameof(EditorSimulatorValues))] - public void EventDeviceSimulator_SetCorrectly(bool isEditor, bool? isSimulator) - { - // arrange - var testApplication = new TestApplication(isEditor); - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, testApplication); - var scope = new Scope(_sentryOptions); + // assert + Assert.AreEqual(UnitySdkInfo.Name, scope.Sdk.Name); + Assert.AreEqual(UnitySdkInfo.Version, scope.Sdk.Version); - // act - sut.ConfigureScope(scope); + var package = scope.Sdk.Packages.FirstOrDefault(); + Assert.IsNotNull(package); + Assert.AreEqual(UnitySdkInfo.PackageName, package!.Name); + Assert.AreEqual(UnitySdkInfo.Version, package!.Version); + } - // assert - Assert.AreEqual(scope.Contexts.Device.Simulator, isSimulator); - } + [TestCaseSource(nameof(EditorSimulatorValues))] + public void EventDeviceSimulator_SetCorrectly(bool isEditor, bool? isSimulator) + { + // arrange + var testApplication = new TestApplication(isEditor); + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, testApplication); + var scope = new Scope(_sentryOptions); - private static readonly object[] EditorSimulatorValues = - { - new object[] { true, true }, - new object[] { false, null! } - }; + // act + sut.ConfigureScope(scope); - [Test] - public void DeviceUniqueIdentifierWithSendDefaultPii_IsNotNull() - { - // arrange - var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; - var sut = new UnityScopeUpdater(sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(sentryOptions); + // assert + Assert.AreEqual(scope.Contexts.Device.Simulator, isSimulator); + } - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + private static readonly object[] EditorSimulatorValues = + { + new object[] { true, true }, + new object[] { false, null! } + }; - // act + [Test] + public void DeviceUniqueIdentifierWithSendDefaultPii_IsNotNull() + { + // arrange + var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; + var sut = new UnityScopeUpdater(sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(sentryOptions); - // assert - Assert.IsNotNull(scope.Contexts.Device.DeviceUniqueIdentifier); - } + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); - [Test] - public void AppProtocol_Assigned() - { - // arrange - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); + // act - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + // assert + Assert.IsNotNull(scope.Contexts.Device.DeviceUniqueIdentifier); + } - // assert - Assert.IsNotNull(scope.Contexts.App.StartTime); - Assert.IsNotNull(scope.Contexts.App.BuildType); - } + [Test] + public void AppProtocol_Assigned() + { + // arrange + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); - [Test] - public void UserId_SetIfEmpty() - { - // arrange - var options = new SentryUnityOptions { DefaultUserId = "foo" }; - var sut = new UnityScopeUpdater(options, _mainThreadData, _testApplication); - var scope = new Scope(options); + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + // assert + Assert.IsNotNull(scope.Contexts.App.StartTime); + Assert.IsNotNull(scope.Contexts.App.BuildType); + } - // assert - Assert.AreEqual(scope.User.Id, options.DefaultUserId); - } + [Test] + public void UserId_SetIfEmpty() + { + // arrange + var options = new SentryUnityOptions { DefaultUserId = "foo" }; + var sut = new UnityScopeUpdater(options, _mainThreadData, _testApplication); + var scope = new Scope(options); - [Test] - public void UserId_UnchangedIfNonEmpty() - { - // arrange - var options = new SentryUnityOptions { DefaultUserId = "foo" }; - var sut = new UnityScopeUpdater(options, _mainThreadData, _testApplication); - var scope = new Scope(options); - scope.User.Id = "bar"; + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + // assert + Assert.AreEqual(scope.User.Id, options.DefaultUserId); + } - // assert - Assert.AreEqual(scope.User.Id, "bar"); - } + [Test] + public void UserId_UnchangedIfNonEmpty() + { + // arrange + var options = new SentryUnityOptions { DefaultUserId = "foo" }; + var sut = new UnityScopeUpdater(options, _mainThreadData, _testApplication); + var scope = new Scope(options); + scope.User.Id = "bar"; + + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); + + // assert + Assert.AreEqual(scope.User.Id, "bar"); + } - [Test] - public void Tags_Set() + [Test] + public void Tags_Set() + { + // arrange + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - // arrange - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - SupportsDrawCallInstancing = true, - DeviceType = new(() => "test type"), - DeviceUniqueIdentifier = new(() => "f810306c-68db-4ebe-89ba-13c457449339"), - InstallMode = ApplicationInstallMode.Store.ToString() - }; - - var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; - var scopeUpdater = new UnityScopeUpdater(sentryOptions, _mainThreadData, _testApplication); - var unityEventProcessor = new UnityEventProcessor(sentryOptions, _sentryMonoBehaviour); - var scope = new Scope(sentryOptions); - var sentryEvent = new SentryEvent(); - var transaction = new SentryTransaction("name", "operation"); + SupportsDrawCallInstancing = true, + DeviceType = new(() => "test type"), + DeviceUniqueIdentifier = new(() => "f810306c-68db-4ebe-89ba-13c457449339"), + InstallMode = ApplicationInstallMode.Store.ToString() + }; - // act - _sentryMonoBehaviour.CollectData(); - scopeUpdater.ConfigureScope(scope); - scope.Apply(sentryEvent); - scope.Apply(transaction); - unityEventProcessor.Process(sentryEvent); - unityEventProcessor.Process(transaction); - - // assert - AssertEventProcessorTags(sentryEvent.Tags); - AssertEventProcessorTags(transaction.Tags); - } + var sentryOptions = new SentryUnityOptions { SendDefaultPii = true }; + var scopeUpdater = new UnityScopeUpdater(sentryOptions, _mainThreadData, _testApplication); + var unityEventProcessor = new UnityEventProcessor(sentryOptions, _sentryMonoBehaviour); + var scope = new Scope(sentryOptions); + var sentryEvent = new SentryEvent(); + var transaction = new SentryTransaction("name", "operation"); + + // act + _sentryMonoBehaviour.CollectData(); + scopeUpdater.ConfigureScope(scope); + scope.Apply(sentryEvent); + scope.Apply(transaction); + unityEventProcessor.Process(sentryEvent); + unityEventProcessor.Process(transaction); + + // assert + AssertEventProcessorTags(sentryEvent.Tags); + AssertEventProcessorTags(transaction.Tags); + } - private void AssertEventProcessorTags(IReadOnlyDictionary tags) - { - Assert.IsNotNull(tags); - Assert.NotZero(tags.Count); + private void AssertEventProcessorTags(IReadOnlyDictionary tags) + { + Assert.IsNotNull(tags); + Assert.NotZero(tags.Count); - var unityInstallMode = tags.SingleOrDefault(t => t.Key == "unity.install_mode"); - Assert.NotNull(unityInstallMode); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.InstallMode, unityInstallMode.Value); + var unityInstallMode = tags.SingleOrDefault(t => t.Key == "unity.install_mode"); + Assert.NotNull(unityInstallMode); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.InstallMode, unityInstallMode.Value); - var supportsInstancing = tags.SingleOrDefault(t => t.Key == "unity.gpu.supports_instancing"); - Assert.NotNull(supportsInstancing); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsDrawCallInstancing, bool.Parse(supportsInstancing.Value)); + var supportsInstancing = tags.SingleOrDefault(t => t.Key == "unity.gpu.supports_instancing"); + Assert.NotNull(supportsInstancing); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsDrawCallInstancing, bool.Parse(supportsInstancing.Value)); - var deviceType = tags.SingleOrDefault(t => t.Key == "unity.device.device_type"); - Assert.NotNull(deviceType); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceType!.Value, deviceType.Value); + var deviceType = tags.SingleOrDefault(t => t.Key == "unity.device.device_type"); + Assert.NotNull(deviceType); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceType!.Value, deviceType.Value); - var deviceUniqueIdentifier = tags.SingleOrDefault(t => t.Key == "unity.device.unique_identifier"); - Assert.NotNull(deviceUniqueIdentifier); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceUniqueIdentifier!.Value, deviceUniqueIdentifier.Value); + var deviceUniqueIdentifier = tags.SingleOrDefault(t => t.Key == "unity.device.unique_identifier"); + Assert.NotNull(deviceUniqueIdentifier); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceUniqueIdentifier!.Value, deviceUniqueIdentifier.Value); - var isMainThread = tags.SingleOrDefault(t => t.Key == "unity.is_main_thread"); - Assert.NotNull(isMainThread); - Assert.AreEqual("true", isMainThread.Value); - } + var isMainThread = tags.SingleOrDefault(t => t.Key == "unity.is_main_thread"); + Assert.NotNull(isMainThread); + Assert.AreEqual("true", isMainThread.Value); + } - [Test] - public void OperatingSystemProtocol_Assigned() - { - // arrange - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { OperatingSystem = "Windows" }; - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); + [Test] + public void OperatingSystemProtocol_Assigned() + { + // arrange + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { OperatingSystem = "Windows" }; + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); - // assert - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.OperatingSystem, scope.Contexts.OperatingSystem.RawDescription); - } + // assert + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.OperatingSystem, scope.Contexts.OperatingSystem.RawDescription); + } - [Test] - public void DeviceProtocol_Assigned() + [Test] + public void DeviceProtocol_Assigned() + { + const long toByte = 1048576L; // in `UnityEventProcessor.PopulateDevice` + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - const long toByte = 1048576L; // in `UnityEventProcessor.PopulateDevice` - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - ProcessorCount = 1, - DeviceType = new Lazy(() => "Console"), - CpuDescription = "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz", - SupportsVibration = true, - DeviceName = "hostname", - DeviceModel = new Lazy(() => "Samsung Galaxy S3"), - SystemMemorySize = 16000 - }; - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); - - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); - - // assert - var device = scope.Contexts.Device; - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.ProcessorCount, device.ProcessorCount); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceType!.Value, device.DeviceType); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.CpuDescription, device.CpuDescription); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsVibration, device.SupportsVibration); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceName, device.Name); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceModel!.Value, device.Model); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SystemMemorySize * toByte, device.MemorySize); - } + ProcessorCount = 1, + DeviceType = new Lazy(() => "Console"), + CpuDescription = "Intel(R) Core(TM)2 Quad CPU Q6600 @ 2.40GHz", + SupportsVibration = true, + DeviceName = "hostname", + DeviceModel = new Lazy(() => "Samsung Galaxy S3"), + SystemMemorySize = 16000 + }; + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); + + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); + + // assert + var device = scope.Contexts.Device; + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.ProcessorCount, device.ProcessorCount); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceType!.Value, device.DeviceType); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.CpuDescription, device.CpuDescription); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsVibration, device.SupportsVibration); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceName, device.Name); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.DeviceModel!.Value, device.Model); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SystemMemorySize * toByte, device.MemorySize); + } - [Test] - public void UnityProtocol_Assigned() + [Test] + public void UnityProtocol_Assigned() + { + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - EditorVersion = "TestEditorVersion2022.3.2f1", - InstallMode = "Editor", - TargetFrameRate = new Lazy(() => "-1"), - CopyTextureSupport = new Lazy(() => "Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture"), - RenderingThreadingMode = new Lazy(() => "MultiThreaded") - }; - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); - - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); - - // assert - scope.Contexts.TryGetValue(Unity.Protocol.Unity.Type, out var unityProtocolObject); - var unityProtocol = unityProtocolObject as Unity.Protocol.Unity; - Assert.IsNotNull(unityProtocol); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.EditorVersion, unityProtocol!.EditorVersion); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.InstallMode, unityProtocol.InstallMode); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.TargetFrameRate!.Value, unityProtocol.TargetFrameRate); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.CopyTextureSupport!.Value, unityProtocol.CopyTextureSupport); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.RenderingThreadingMode!.Value, unityProtocol.RenderingThreadingMode); - } + EditorVersion = "TestEditorVersion2022.3.2f1", + InstallMode = "Editor", + TargetFrameRate = new Lazy(() => "-1"), + CopyTextureSupport = new Lazy(() => "Basic, Copy3D, DifferentTypes, TextureToRT, RTToTexture"), + RenderingThreadingMode = new Lazy(() => "MultiThreaded") + }; + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); + + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); + + // assert + scope.Contexts.TryGetValue(Unity.Protocol.Unity.Type, out var unityProtocolObject); + var unityProtocol = unityProtocolObject as Unity.Protocol.Unity; + Assert.IsNotNull(unityProtocol); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.EditorVersion, unityProtocol!.EditorVersion); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.InstallMode, unityProtocol.InstallMode); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.TargetFrameRate!.Value, unityProtocol.TargetFrameRate); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.CopyTextureSupport!.Value, unityProtocol.CopyTextureSupport); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.RenderingThreadingMode!.Value, unityProtocol.RenderingThreadingMode); + } - [Test] - public void GpuProtocol_Assigned() + [Test] + public void GpuProtocol_Assigned() + { + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - GraphicsDeviceId = 1, - GraphicsDeviceName = "GeForce RTX 3090", - GraphicsDeviceVendorId = new Lazy(() => "25"), - GraphicsDeviceVendor = "NVIDIA", - GraphicsMemorySize = 24000, - GraphicsMultiThreaded = new Lazy(() => true), - NpotSupport = "true", - GraphicsDeviceVersion = "version212134", - GraphicsDeviceType = "devicetype", - MaxTextureSize = 1680, - SupportsDrawCallInstancing = true, - SupportsRayTracing = true, - SupportsComputeShaders = true, - SupportsGeometryShaders = true - }; - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); + GraphicsDeviceId = 1, + GraphicsDeviceName = "GeForce RTX 3090", + GraphicsDeviceVendorId = new Lazy(() => "25"), + GraphicsDeviceVendor = "NVIDIA", + GraphicsMemorySize = 24000, + GraphicsMultiThreaded = new Lazy(() => true), + NpotSupport = "true", + GraphicsDeviceVersion = "version212134", + GraphicsDeviceType = "devicetype", + MaxTextureSize = 1680, + SupportsDrawCallInstancing = true, + SupportsRayTracing = true, + SupportsComputeShaders = true, + SupportsGeometryShaders = true + }; + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); + + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); + + // assert + var gpu = scope.Contexts.Gpu; + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceId, gpu.Id); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceName, gpu.Name); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceVendorId!.Value, gpu.VendorId); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceVendor, gpu.VendorName); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsMemorySize, gpu.MemorySize); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsMultiThreaded!.Value, gpu.MultiThreadedRendering); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.NpotSupport, gpu.NpotSupport); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceVersion, gpu.Version); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceType, gpu.ApiType); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.MaxTextureSize, gpu.MaxTextureSize); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsDrawCallInstancing, gpu.SupportsDrawCallInstancing); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsRayTracing, gpu.SupportsRayTracing); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsComputeShaders, gpu.SupportsComputeShaders); + Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsGeometryShaders, gpu.SupportsGeometryShaders); + } - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); - - // assert - var gpu = scope.Contexts.Gpu; - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceId, gpu.Id); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceName, gpu.Name); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceVendorId!.Value, gpu.VendorId); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceVendor, gpu.VendorName); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsMemorySize, gpu.MemorySize); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsMultiThreaded!.Value, gpu.MultiThreadedRendering); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.NpotSupport, gpu.NpotSupport); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceVersion, gpu.Version); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.GraphicsDeviceType, gpu.ApiType); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.MaxTextureSize, gpu.MaxTextureSize); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsDrawCallInstancing, gpu.SupportsDrawCallInstancing); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsRayTracing, gpu.SupportsRayTracing); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsComputeShaders, gpu.SupportsComputeShaders); - Assert.AreEqual(_sentryMonoBehaviour.SentrySystemInfo.SupportsGeometryShaders, gpu.SupportsGeometryShaders); - } + [Test] + public void GpuProtocolGraphicsShaderLevel_Assigned( + [ValueSource(nameof(ShaderLevels))] (int, string) shaderValue) + { + var (shaderLevel, shaderDescription) = shaderValue; - [Test] - public void GpuProtocolGraphicsShaderLevel_Assigned( - [ValueSource(nameof(ShaderLevels))] (int, string) shaderValue) + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - var (shaderLevel, shaderDescription) = shaderValue; - - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - GraphicsShaderLevel = shaderLevel - }; + GraphicsShaderLevel = shaderLevel + }; - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); - // assert - Assert.AreEqual(shaderDescription, scope.Contexts.Gpu.GraphicsShaderLevel); - } + // assert + Assert.AreEqual(shaderDescription, scope.Contexts.Gpu.GraphicsShaderLevel); + } - private static readonly (int shaderLevel, string shaderDescription)[] ShaderLevels = + private static readonly (int shaderLevel, string shaderDescription)[] ShaderLevels = + { + (20, "Shader Model 2.0"), + (25, "Shader Model 2.5"), + (30, "Shader Model 3.0"), + (35, "OpenGL ES 3.0"), + (40, "Shader Model 4.0"), + (45, "Metal / OpenGL ES 3.1"), + (46, "OpenGL 4.1"), + (50, "Shader Model 5.0"), + (21, "21") + }; + + [Test] + public void GpuProtocolGraphicsShaderLevelMinusOne_Ignored() + { + _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo { - (20, "Shader Model 2.0"), - (25, "Shader Model 2.5"), - (30, "Shader Model 3.0"), - (35, "OpenGL ES 3.0"), - (40, "Shader Model 4.0"), - (45, "Metal / OpenGL ES 3.1"), - (46, "OpenGL 4.1"), - (50, "Shader Model 5.0"), - (21, "21") + GraphicsShaderLevel = -1 }; - [Test] - public void GpuProtocolGraphicsShaderLevelMinusOne_Ignored() - { - _sentryMonoBehaviour.SentrySystemInfo = new TestSentrySystemInfo - { - GraphicsShaderLevel = -1 - }; - - var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); - var scope = new Scope(_sentryOptions); - - // act - _sentryMonoBehaviour.CollectData(); - sut.ConfigureScope(scope); + var sut = new UnityScopeUpdater(_sentryOptions, _mainThreadData, _testApplication); + var scope = new Scope(_sentryOptions); - // assert - Assert.IsNull(scope.Contexts.Gpu.GraphicsShaderLevel); - } - } + // act + _sentryMonoBehaviour.CollectData(); + sut.ConfigureScope(scope); - internal sealed class TestSentrySystemInfo : ISentrySystemInfo - { - public int? MainThreadId { get; set; } = Thread.CurrentThread.ManagedThreadId; - public string? OperatingSystem { get; set; } - public int? ProcessorCount { get; set; } - public bool? SupportsVibration { get; set; } - public Lazy? DeviceType { get; set; } - public string? CpuDescription { get; set; } - public string? DeviceName { get; set; } - public Lazy? DeviceUniqueIdentifier { get; set; } - public Lazy? DeviceModel { get; set; } - public int? SystemMemorySize { get; set; } - public int? GraphicsDeviceId { get; set; } - public string? GraphicsDeviceName { get; set; } - public Lazy? GraphicsDeviceVendorId { get; set; } - public string? GraphicsDeviceVendor { get; set; } - public int? GraphicsMemorySize { get; set; } - public Lazy? GraphicsMultiThreaded { get; set; } - public string? NpotSupport { get; set; } - public string? GraphicsDeviceVersion { get; set; } - public string? GraphicsDeviceType { get; set; } - public int? MaxTextureSize { get; set; } - public bool? SupportsDrawCallInstancing { get; set; } - public bool? SupportsRayTracing { get; set; } - public bool? SupportsComputeShaders { get; set; } - public bool? SupportsGeometryShaders { get; set; } - public int? GraphicsShaderLevel { get; set; } - public bool? GraphicsUVStartsAtTop { get; } - public Lazy? IsDebugBuild { get; set; } - public string? EditorVersion { get; set; } - public string? InstallMode { get; set; } - public Lazy? TargetFrameRate { get; set; } - public Lazy? CopyTextureSupport { get; set; } - public Lazy? RenderingThreadingMode { get; set; } - public Lazy? StartTime { get; set; } + // assert + Assert.IsNull(scope.Contexts.Gpu.GraphicsShaderLevel); } } + +internal sealed class TestSentrySystemInfo : ISentrySystemInfo +{ + public int? MainThreadId { get; set; } = Thread.CurrentThread.ManagedThreadId; + public string? OperatingSystem { get; set; } + public int? ProcessorCount { get; set; } + public bool? SupportsVibration { get; set; } + public Lazy? DeviceType { get; set; } + public string? CpuDescription { get; set; } + public string? DeviceName { get; set; } + public Lazy? DeviceUniqueIdentifier { get; set; } + public Lazy? DeviceModel { get; set; } + public int? SystemMemorySize { get; set; } + public int? GraphicsDeviceId { get; set; } + public string? GraphicsDeviceName { get; set; } + public Lazy? GraphicsDeviceVendorId { get; set; } + public string? GraphicsDeviceVendor { get; set; } + public int? GraphicsMemorySize { get; set; } + public Lazy? GraphicsMultiThreaded { get; set; } + public string? NpotSupport { get; set; } + public string? GraphicsDeviceVersion { get; set; } + public string? GraphicsDeviceType { get; set; } + public int? MaxTextureSize { get; set; } + public bool? SupportsDrawCallInstancing { get; set; } + public bool? SupportsRayTracing { get; set; } + public bool? SupportsComputeShaders { get; set; } + public bool? SupportsGeometryShaders { get; set; } + public int? GraphicsShaderLevel { get; set; } + public bool? GraphicsUVStartsAtTop { get; } + public Lazy? IsDebugBuild { get; set; } + public string? EditorVersion { get; set; } + public string? InstallMode { get; set; } + public Lazy? TargetFrameRate { get; set; } + public Lazy? CopyTextureSupport { get; set; } + public Lazy? RenderingThreadingMode { get; set; } + public Lazy? StartTime { get; set; } +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs b/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs index ec2101a4a..418364ebd 100644 --- a/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs +++ b/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs @@ -1,21 +1,20 @@ using NUnit.Framework; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class UnityIl2CppEventExceptionProcessorTests { - public class UnityIl2CppEventExceptionProcessorTests + [Test] + [TestCase(null, null)] + [TestCase("f30fef22-d93e-7f60-0000-000000000000", "f30fef22d93e7f60")] + [TestCase("0c9249e5-e223-8bd5-0000-000000000000", "0c9249e5e2238bd5")] + [TestCase("6f42afa0-45c8-86e6-2372-a02513d55560", "6f42afa045c886e62372a02513d55560")] + [TestCase("ff25f952-44c9-4c78-b54f-f8403f185b50-b9a5f714", "ff25f95244c94c78b54ff8403f185b50b9a5f714")] + [TestCase("94552647-48dc-4fe4-ba75-7ccd3c43c44d-917f8072", "9455264748dc4fe4ba757ccd3c43c44d917f8072")] + public void NormalizeUuid_ReturnValueMatchesExpected(string input, string expected) { - [Test] - [TestCase(null, null)] - [TestCase("f30fef22-d93e-7f60-0000-000000000000", "f30fef22d93e7f60")] - [TestCase("0c9249e5-e223-8bd5-0000-000000000000", "0c9249e5e2238bd5")] - [TestCase("6f42afa0-45c8-86e6-2372-a02513d55560", "6f42afa045c886e62372a02513d55560")] - [TestCase("ff25f952-44c9-4c78-b54f-f8403f185b50-b9a5f714", "ff25f95244c94c78b54ff8403f185b50b9a5f714")] - [TestCase("94552647-48dc-4fe4-ba75-7ccd3c43c44d-917f8072", "9455264748dc4fe4ba757ccd3c43c44d917f8072")] - public void NormalizeUuid_ReturnValueMatchesExpected(string input, string expected) - { - var actual = UnityIl2CppEventExceptionProcessor.NormalizeUuid(input); + var actual = UnityIl2CppEventExceptionProcessor.NormalizeUuid(input); - Assert.AreEqual(actual, expected); - } + Assert.AreEqual(actual, expected); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs index 81fb79296..3cd9cbed8 100644 --- a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs @@ -7,254 +7,253 @@ using Sentry.Unity.Tests.Stubs; using UnityEngine; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class UnityLogHandlerIntegrationTests { - public sealed class UnityLogHandlerIntegrationTests + private class Fixture { - private class Fixture + public TestHub Hub { get; set; } = null!; + public SentryUnityOptions SentryOptions { get; set; } = null!; + + public UnityLogHandlerIntegration GetSut() { - public TestHub Hub { get; set; } = null!; - public SentryUnityOptions SentryOptions { get; set; } = null!; - - public UnityLogHandlerIntegration GetSut() - { - var application = new TestApplication(); - var integration = new UnityLogHandlerIntegration(SentryOptions, application); - integration.Register(Hub, SentryOptions); - return integration; - } + var application = new TestApplication(); + var integration = new UnityLogHandlerIntegration(SentryOptions, application); + integration.Register(Hub, SentryOptions); + return integration; } + } - private Fixture _fixture = null!; + private Fixture _fixture = null!; - [SetUp] - public void SetUp() + [SetUp] + public void SetUp() + { + _fixture = new Fixture { - _fixture = new Fixture - { - Hub = new TestHub(), - SentryOptions = new SentryUnityOptions() - }; - } + Hub = new TestHub(), + SentryOptions = new SentryUnityOptions() + }; + } - [Test] - public void CaptureLogFormat_ParamArgsContainNull_CaptureEvent() - { - var sut = _fixture.GetSut(); + [Test] + public void CaptureLogFormat_ParamArgsContainNull_CaptureEvent() + { + var sut = _fixture.GetSut(); - sut.CaptureLogFormat(LogType.Error, null, "{0}: {1}", new object[] { null!, null! }); + sut.CaptureLogFormat(LogType.Error, null, "{0}: {1}", new object[] { null!, null! }); - Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); - } + Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); + } - [Test] - public void CaptureLogFormat_LogContainsSentryTag_NotCaptured() - { - var sut = _fixture.GetSut(); + [Test] + public void CaptureLogFormat_LogContainsSentryTag_NotCaptured() + { + var sut = _fixture.GetSut(); - sut.CaptureLogFormat(LogType.Error, null, "{0}: {1}", UnityLogger.LogTag, "Test Message"); + sut.CaptureLogFormat(LogType.Error, null, "{0}: {1}", UnityLogger.LogTag, "Test Message"); - Assert.AreEqual(0, _fixture.Hub.CapturedEvents.Count); - } + Assert.AreEqual(0, _fixture.Hub.CapturedEvents.Count); + } - [Test] - public void CaptureLogFormat_LogTypeError_CaptureEvent() - { - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + public void CaptureLogFormat_LogTypeError_CaptureEvent() + { + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureLogFormat(LogType.Error, null, "{0}", message); + sut.CaptureLogFormat(LogType.Error, null, "{0}", message); - Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); - Assert.NotNull(_fixture.Hub.CapturedEvents[0].Message); - Assert.AreEqual(message, _fixture.Hub.CapturedEvents[0].Message!.Message); - } + Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); + Assert.NotNull(_fixture.Hub.CapturedEvents[0].Message); + Assert.AreEqual(message, _fixture.Hub.CapturedEvents[0].Message!.Message); + } - [Test] - [TestCase(LogType.Log)] - [TestCase(LogType.Warning)] - [TestCase(LogType.Error)] - public void CaptureLogFormat_LogDebounceEnabled_DebouncesMessage(LogType unityLogType) - { - _fixture.SentryOptions.EnableLogDebouncing = true; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + [TestCase(LogType.Log)] + [TestCase(LogType.Warning)] + [TestCase(LogType.Error)] + public void CaptureLogFormat_LogDebounceEnabled_DebouncesMessage(LogType unityLogType) + { + _fixture.SentryOptions.EnableLogDebouncing = true; + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureLogFormat(unityLogType, null, "{0}", message); - sut.CaptureLogFormat(unityLogType, null, "{0}", message); + sut.CaptureLogFormat(unityLogType, null, "{0}", message); + sut.CaptureLogFormat(unityLogType, null, "{0}", message); - Assert.AreEqual(1, _fixture.Hub.ConfigureScopeCalls.Count); - } + Assert.AreEqual(1, _fixture.Hub.ConfigureScopeCalls.Count); + } - private static readonly object[] LogTypesCaptured = - { - new object[] { LogType.Error, SentryLevel.Error, BreadcrumbLevel.Error }, - new object[] { LogType.Assert, SentryLevel.Error, BreadcrumbLevel.Error } - }; + private static readonly object[] LogTypesCaptured = + { + new object[] { LogType.Error, SentryLevel.Error, BreadcrumbLevel.Error }, + new object[] { LogType.Assert, SentryLevel.Error, BreadcrumbLevel.Error } + }; - [TestCaseSource(nameof(LogTypesCaptured))] - public void CaptureLogFormat_UnityErrorLogTypes_CapturedAndCorrespondToSentryLevel(LogType unityLogType, SentryLevel sentryLevel, BreadcrumbLevel breadcrumbLevel) - { - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [TestCaseSource(nameof(LogTypesCaptured))] + public void CaptureLogFormat_UnityErrorLogTypes_CapturedAndCorrespondToSentryLevel(LogType unityLogType, SentryLevel sentryLevel, BreadcrumbLevel breadcrumbLevel) + { + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureLogFormat(unityLogType, null, "{0}", message); + sut.CaptureLogFormat(unityLogType, null, "{0}", message); - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); + var scope = new Scope(_fixture.SentryOptions); + _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); + var breadcrumb = scope.Breadcrumbs.Single(); - Assert.NotNull(_fixture.Hub.CapturedEvents.SingleOrDefault(capturedEvent => capturedEvent.Level == sentryLevel)); - Assert.AreEqual(message, breadcrumb.Message); - Assert.AreEqual("unity.logger", breadcrumb.Category); - Assert.AreEqual(breadcrumbLevel, breadcrumb.Level); - } + Assert.NotNull(_fixture.Hub.CapturedEvents.SingleOrDefault(capturedEvent => capturedEvent.Level == sentryLevel)); + Assert.AreEqual(message, breadcrumb.Message); + Assert.AreEqual("unity.logger", breadcrumb.Category); + Assert.AreEqual(breadcrumbLevel, breadcrumb.Level); + } - private static readonly object[] LogTypesNotCaptured = - { - new object[] { LogType.Log, BreadcrumbLevel.Info }, - new object[] { LogType.Warning, BreadcrumbLevel.Warning } - }; + private static readonly object[] LogTypesNotCaptured = + { + new object[] { LogType.Log, BreadcrumbLevel.Info }, + new object[] { LogType.Warning, BreadcrumbLevel.Warning } + }; - [TestCaseSource(nameof(LogTypesNotCaptured))] - public void CaptureLogFormat_UnityNotErrorLogTypes_NotCaptured(LogType unityLogType, BreadcrumbLevel breadcrumbLevel) - { - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [TestCaseSource(nameof(LogTypesNotCaptured))] + public void CaptureLogFormat_UnityNotErrorLogTypes_NotCaptured(LogType unityLogType, BreadcrumbLevel breadcrumbLevel) + { + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureLogFormat(unityLogType, null, "{0}", message); + sut.CaptureLogFormat(unityLogType, null, "{0}", message); - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); + var scope = new Scope(_fixture.SentryOptions); + _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); + var breadcrumb = scope.Breadcrumbs.Single(); - Assert.AreEqual(0, _fixture.Hub.CapturedEvents.Count); - Assert.AreEqual(message, breadcrumb.Message); - Assert.AreEqual("unity.logger", breadcrumb.Category); - Assert.AreEqual(breadcrumbLevel, breadcrumb.Level); - } + Assert.AreEqual(0, _fixture.Hub.CapturedEvents.Count); + Assert.AreEqual(message, breadcrumb.Message); + Assert.AreEqual("unity.logger", breadcrumb.Category); + Assert.AreEqual(breadcrumbLevel, breadcrumb.Level); + } - [Test] - [TestCase(LogType.Log)] - [TestCase(LogType.Warning)] - [TestCase(LogType.Error)] - [TestCase(LogType.Assert)] - public void CaptureLogFormat_AddAsBreadcrumbEnabled_AddedAsBreadcrumb(LogType logType) - { - _fixture.SentryOptions.AddBreadcrumbsForLogType[logType] = true; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + [TestCase(LogType.Log)] + [TestCase(LogType.Warning)] + [TestCase(LogType.Error)] + [TestCase(LogType.Assert)] + public void CaptureLogFormat_AddAsBreadcrumbEnabled_AddedAsBreadcrumb(LogType logType) + { + _fixture.SentryOptions.AddBreadcrumbsForLogType[logType] = true; + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureLogFormat(logType, null, "{0}", message); + sut.CaptureLogFormat(logType, null, "{0}", message); - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); + var scope = new Scope(_fixture.SentryOptions); + _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); + var breadcrumb = scope.Breadcrumbs.Single(); - Assert.AreEqual(message, breadcrumb.Message); - } + Assert.AreEqual(message, breadcrumb.Message); + } - [Test] - public void CaptureException_AddAsBreadcrumbEnabled_AddedAsBreadcrumb() - { - _fixture.SentryOptions.AddBreadcrumbsForLogType[LogType.Exception] = true; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + public void CaptureException_AddAsBreadcrumbEnabled_AddedAsBreadcrumb() + { + _fixture.SentryOptions.AddBreadcrumbsForLogType[LogType.Exception] = true; + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureException(new Exception(message), null); + sut.CaptureException(new Exception(message), null); - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); + var scope = new Scope(_fixture.SentryOptions); + _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); + var breadcrumb = scope.Breadcrumbs.Single(); - StringAssert.Contains(message, breadcrumb.Message); - } + StringAssert.Contains(message, breadcrumb.Message); + } - [Test] - [TestCase(LogType.Log)] - [TestCase(LogType.Warning)] - [TestCase(LogType.Error)] - [TestCase(LogType.Assert)] - public void CaptureLogFormat_AddAsBreadcrumbDisabled_NotAddedAsBreadcrumb(LogType logType) - { - _fixture.SentryOptions.AddBreadcrumbsForLogType[logType] = false; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + [TestCase(LogType.Log)] + [TestCase(LogType.Warning)] + [TestCase(LogType.Error)] + [TestCase(LogType.Assert)] + public void CaptureLogFormat_AddAsBreadcrumbDisabled_NotAddedAsBreadcrumb(LogType logType) + { + _fixture.SentryOptions.AddBreadcrumbsForLogType[logType] = false; + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureLogFormat(logType, null, "{0}", message); + sut.CaptureLogFormat(logType, null, "{0}", message); - Assert.IsFalse(_fixture.Hub.ConfigureScopeCalls.Count > 0); - } + Assert.IsFalse(_fixture.Hub.ConfigureScopeCalls.Count > 0); + } - [Test] - public void CaptureException_AddAsBreadcrumbEnabled_NotAddedAsBreadcrumb() - { - _fixture.SentryOptions.AddBreadcrumbsForLogType[LogType.Exception] = false; - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + public void CaptureException_AddAsBreadcrumbEnabled_NotAddedAsBreadcrumb() + { + _fixture.SentryOptions.AddBreadcrumbsForLogType[LogType.Exception] = false; + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureException(new Exception("Test Exception"), null); + sut.CaptureException(new Exception("Test Exception"), null); - Assert.IsFalse(_fixture.Hub.ConfigureScopeCalls.Count > 0); - } + Assert.IsFalse(_fixture.Hub.ConfigureScopeCalls.Count > 0); + } - [Test] - public void CaptureException_ExceptionCapturedAndMechanismSet() - { - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + [Test] + public void CaptureException_ExceptionCapturedAndMechanismSet() + { + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - sut.CaptureException(new Exception(message), null); + sut.CaptureException(new Exception(message), null); - Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); + Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); - var capturedEvent = _fixture.Hub.CapturedEvents.Single(); - Assert.NotNull(capturedEvent); + var capturedEvent = _fixture.Hub.CapturedEvents.Single(); + Assert.NotNull(capturedEvent); - Assert.NotNull(capturedEvent.Exception); - Assert.AreEqual(message, capturedEvent.Exception!.Message); + Assert.NotNull(capturedEvent.Exception); + Assert.AreEqual(message, capturedEvent.Exception!.Message); - Assert.IsTrue(capturedEvent.Exception!.Data.Contains(Mechanism.HandledKey)); - Assert.IsFalse((bool)capturedEvent.Exception!.Data[Mechanism.HandledKey]); + Assert.IsTrue(capturedEvent.Exception!.Data.Contains(Mechanism.HandledKey)); + Assert.IsFalse((bool)capturedEvent.Exception!.Data[Mechanism.HandledKey]); - Assert.IsTrue(capturedEvent.Exception!.Data.Contains(Mechanism.MechanismKey)); - Assert.AreEqual("Unity.LogException", (string)capturedEvent.Exception!.Data[Mechanism.MechanismKey]); - } + Assert.IsTrue(capturedEvent.Exception!.Data.Contains(Mechanism.MechanismKey)); + Assert.AreEqual("Unity.LogException", (string)capturedEvent.Exception!.Data[Mechanism.MechanismKey]); + } - [Test] - public void CaptureException_CapturedExceptionAddedAsBreadcrumb() - { - var sut = _fixture.GetSut(); - var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; - var exception = new Exception(message); + [Test] + public void CaptureException_CapturedExceptionAddedAsBreadcrumb() + { + var sut = _fixture.GetSut(); + var message = NUnit.Framework.TestContext.CurrentContext.Test.Name; + var exception = new Exception(message); - sut.CaptureException(exception, null); + sut.CaptureException(exception, null); - Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); // Sanity check + Assert.AreEqual(1, _fixture.Hub.CapturedEvents.Count); // Sanity check - var scope = new Scope(_fixture.SentryOptions); - _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); - var breadcrumb = scope.Breadcrumbs.Single(); + var scope = new Scope(_fixture.SentryOptions); + _fixture.Hub.ConfigureScopeCalls.Single().Invoke(scope); + var breadcrumb = scope.Breadcrumbs.Single(); - Assert.AreEqual(exception.GetType() + ": " + message, breadcrumb.Message); - Assert.AreEqual("unity.logger", breadcrumb.Category); - Assert.AreEqual(BreadcrumbLevel.Error, breadcrumb.Level); - } + Assert.AreEqual(exception.GetType() + ": " + message, breadcrumb.Message); + Assert.AreEqual("unity.logger", breadcrumb.Category); + Assert.AreEqual(BreadcrumbLevel.Error, breadcrumb.Level); + } - [Test] - public void Register_RegisteredASecondTime_LogsWarningAndReturns() - { - var testLogger = new TestLogger(); - _fixture.SentryOptions.DiagnosticLogger = testLogger; - _fixture.SentryOptions.Debug = true; - var sut = _fixture.GetSut(); + [Test] + public void Register_RegisteredASecondTime_LogsWarningAndReturns() + { + var testLogger = new TestLogger(); + _fixture.SentryOptions.DiagnosticLogger = testLogger; + _fixture.SentryOptions.Debug = true; + var sut = _fixture.GetSut(); - // Edge-case of initializing the SDK twice with the same options object. - sut.Register(_fixture.Hub, _fixture.SentryOptions); + // Edge-case of initializing the SDK twice with the same options object. + sut.Register(_fixture.Hub, _fixture.SentryOptions); - Assert.IsTrue(testLogger.Logs.Any(log => - log.logLevel == SentryLevel.Warning && - log.message.Contains("UnityLogHandlerIntegration has already been registered."))); - } + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Warning && + log.message.Contains("UnityLogHandlerIntegration has already been registered."))); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityLoggerTests.cs b/test/Sentry.Unity.Tests/UnityLoggerTests.cs index ea6d3bbc4..951acf9dd 100644 --- a/test/Sentry.Unity.Tests/UnityLoggerTests.cs +++ b/test/Sentry.Unity.Tests/UnityLoggerTests.cs @@ -1,57 +1,53 @@ -using System; -using System.Globalization; using NUnit.Framework; using Sentry.Unity.Tests.SharedClasses; using UnityEngine; using UnityEngine.TestTools; -using Object = UnityEngine.Object; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public sealed class UnityLoggerTests { - public sealed class UnityLoggerTests + [Test] + [TestCase(SentryLevel.Debug, LogType.Log)] + [TestCase(SentryLevel.Info, LogType.Log)] + [TestCase(SentryLevel.Warning, LogType.Warning)] + [TestCase(SentryLevel.Error, LogType.Error)] + [TestCase(SentryLevel.Fatal, LogType.Error)] + public void GetUnityLogType_LogTypes_Correspond(SentryLevel sentryLevel, LogType expectedLogType) + { + Assert.AreEqual(expectedLogType, UnityLogger.GetUnityLogType(sentryLevel)); + } + + [Test] + [TestCase(SentryLevel.Info, SentryLevel.Debug)] + [TestCase(SentryLevel.Warning, SentryLevel.Info)] + [TestCase(SentryLevel.Error, SentryLevel.Warning)] + [TestCase(SentryLevel.Fatal, SentryLevel.Error)] + public void Log_LowerLevelThanInitializationLevel_DisablesLogger(SentryLevel initializationLevel, SentryLevel logLevel) { - [Test] - [TestCase(SentryLevel.Debug, LogType.Log)] - [TestCase(SentryLevel.Info, LogType.Log)] - [TestCase(SentryLevel.Warning, LogType.Warning)] - [TestCase(SentryLevel.Error, LogType.Error)] - [TestCase(SentryLevel.Fatal, LogType.Error)] - public void GetUnityLogType_LogTypes_Correspond(SentryLevel sentryLevel, LogType expectedLogType) - { - Assert.AreEqual(expectedLogType, UnityLogger.GetUnityLogType(sentryLevel)); - } - - [Test] - [TestCase(SentryLevel.Info, SentryLevel.Debug)] - [TestCase(SentryLevel.Warning, SentryLevel.Info)] - [TestCase(SentryLevel.Error, SentryLevel.Warning)] - [TestCase(SentryLevel.Fatal, SentryLevel.Error)] - public void Log_LowerLevelThanInitializationLevel_DisablesLogger(SentryLevel initializationLevel, SentryLevel logLevel) - { - LogAssert.ignoreFailingMessages = true; - - var testLogger = new UnityTestLogger(); - var logger = new UnityLogger(new SentryOptions { DiagnosticLevel = initializationLevel }, testLogger); - - const string expectedLog = "Some log"; - - logger.Log(logLevel, expectedLog); - - Assert.False(logger.IsEnabled(logLevel)); - Assert.IsEmpty(testLogger.Logs); - } - - [Test] - public void Log_SetsTag() - { - var testLogger = new UnityTestLogger(); - var logger = new UnityLogger(new SentryOptions { DiagnosticLevel = SentryLevel.Debug }, testLogger); - - logger.Log(SentryLevel.Debug, "TestLog"); - - Assert.AreEqual(1, testLogger.Logs.Count); - // The format is: "(logType, tag, message)" - StringAssert.AreEqualIgnoringCase(UnityLogger.LogTag, testLogger.Logs[0].Item2); - } + LogAssert.ignoreFailingMessages = true; + + var testLogger = new UnityTestLogger(); + var logger = new UnityLogger(new SentryOptions { DiagnosticLevel = initializationLevel }, testLogger); + + const string expectedLog = "Some log"; + + logger.Log(logLevel, expectedLog); + + Assert.False(logger.IsEnabled(logLevel)); + Assert.IsEmpty(testLogger.Logs); + } + + [Test] + public void Log_SetsTag() + { + var testLogger = new UnityTestLogger(); + var logger = new UnityLogger(new SentryOptions { DiagnosticLevel = SentryLevel.Debug }, testLogger); + + logger.Log(SentryLevel.Debug, "TestLog"); + + Assert.AreEqual(1, testLogger.Logs.Count); + // The format is: "(logType, tag, message)" + StringAssert.AreEqualIgnoringCase(UnityLogger.LogTag, testLogger.Logs[0].Item2); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs index 3f0e74011..e3323a7e1 100644 --- a/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs @@ -3,32 +3,31 @@ using Sentry.Unity.Integrations; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class UnitySocketExceptionFilterTests { - public class UnitySocketExceptionFilterTests - { - private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup - private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(0.5f); + private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup + private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(0.5f); - [SetUp] - public void SetUp() => - _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); + [SetUp] + public void SetUp() => + _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); - [Test] - public void Filter_FiltersBadGatewayExceptionsOfTypeException() => - Assert.IsTrue(new UnitySocketExceptionFilter().Filter(new System.Net.Sockets.SocketException(10049))); + [Test] + public void Filter_FiltersBadGatewayExceptionsOfTypeException() => + Assert.IsTrue(new UnitySocketExceptionFilter().Filter(new System.Net.Sockets.SocketException(10049))); - [Test] - public void Init_WithDefaultOptions_DoesNotSendFilteredSocketExceptions() - { - LogAssert.ignoreFailingMessages = true; // The TestHttpClientHandler will complain about timing out (and it should!) + [Test] + public void Init_WithDefaultOptions_DoesNotSendFilteredSocketExceptions() + { + LogAssert.ignoreFailingMessages = true; // The TestHttpClientHandler will complain about timing out (and it should!) - using var _ = SentryTests.InitSentrySdk(testHttpClientHandler: _testHttpClientHandler); + using var _ = SentryTests.InitSentrySdk(testHttpClientHandler: _testHttpClientHandler); - SentrySdk.CaptureException(new System.Net.Sockets.SocketException(10049)); // The requested address is not valid in this context + SentrySdk.CaptureException(new System.Net.Sockets.SocketException(10049)); // The requested address is not valid in this context - var createdEvent = _testHttpClientHandler.GetEvent(UnitySocketExceptionFilter.Message, _eventReceiveTimeout); - Assert.AreEqual(string.Empty, createdEvent); - } + var createdEvent = _testHttpClientHandler.GetEvent(UnitySocketExceptionFilter.Message, _eventReceiveTimeout); + Assert.AreEqual(string.Empty, createdEvent); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs b/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs index 51c3c1a25..e7576e729 100644 --- a/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs +++ b/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs @@ -4,180 +4,179 @@ using UnityEngine; using UnityEngine.SceneManagement; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class UnityViewHierarchyAttachmentTests { - public class UnityViewHierarchyAttachmentTests + private class Fixture { - private class Fixture - { - public SentryUnityOptions Options = new() { AttachViewHierarchy = true }; + public SentryUnityOptions Options = new() { AttachViewHierarchy = true }; - public UnityViewHierarchyAttachmentContent GetSut() => new(Options, SentryMonoBehaviour.Instance); - } + public UnityViewHierarchyAttachmentContent GetSut() => new(Options, SentryMonoBehaviour.Instance); + } - private Fixture _fixture = null!; + private Fixture _fixture = null!; - [SetUp] - public void SetUp() => _fixture = new Fixture(); + [SetUp] + public void SetUp() => _fixture = new Fixture(); - [TearDown] - public void TearDown() + [TearDown] + public void TearDown() + { + if (SentrySdk.IsEnabled) { - if (SentrySdk.IsEnabled) - { - SentryUnity.Close(); - } + SentryUnity.Close(); } + } - [Test] - public void GetStream_IsMainThread_ReturnsStream() - { - var sut = _fixture.GetSut(); + [Test] + public void GetStream_IsMainThread_ReturnsStream() + { + var sut = _fixture.GetSut(); - var stream = sut.GetStream(); + var stream = sut.GetStream(); - Assert.IsNotNull(stream); - } + Assert.IsNotNull(stream); + } + + [Test] + public void GetStream_IsNonMainThread_ReturnsNullStream() + { + var sut = _fixture.GetSut(); - [Test] - public void GetStream_IsNonMainThread_ReturnsNullStream() + new Thread(() => { - var sut = _fixture.GetSut(); + Thread.CurrentThread.IsBackground = true; - new Thread(() => - { - Thread.CurrentThread.IsBackground = true; + var stream = sut.GetStream(); - var stream = sut.GetStream(); + Assert.NotNull(stream); + Assert.AreEqual(Stream.Null, stream); + }).Start(); + } - Assert.NotNull(stream); - Assert.AreEqual(Stream.Null, stream); - }).Start(); - } + [Test] + public void CaptureViewHierarchy_ReturnsNonNullStream() + { + var sut = _fixture.GetSut(); - [Test] - public void CaptureViewHierarchy_ReturnsNonNullStream() - { - var sut = _fixture.GetSut(); + using var stream = sut.CaptureViewHierarchy(); - using var stream = sut.CaptureViewHierarchy(); + Assert.AreNotEqual(Stream.Null, stream); + } - Assert.AreNotEqual(Stream.Null, stream); - } + [Test] + public void CreateViewHierarchy_CapturesSceneAsRoot() + { + var sut = _fixture.GetSut(); - [Test] - public void CreateViewHierarchy_CapturesSceneAsRoot() - { - var sut = _fixture.GetSut(); + var viewHierarchy = sut.CreateViewHierarchy(1, 1, 1); - var viewHierarchy = sut.CreateViewHierarchy(1, 1, 1); + Assert.AreEqual(1, viewHierarchy.Windows.Count); // Sanity check. Root = Currently active scene = 1 + Assert.AreEqual(SceneManager.GetActiveScene().name, viewHierarchy.Windows[0].Type); + } - Assert.AreEqual(1, viewHierarchy.Windows.Count); // Sanity check. Root = Currently active scene = 1 - Assert.AreEqual(SceneManager.GetActiveScene().name, viewHierarchy.Windows[0].Type); + [Test] + public void CreateViewHierarchy_RootGameObjectCountBiggerThanMaxRootGameObjectCount_PrunesCreatedViewHierarchy() + { + var sut = _fixture.GetSut(); + for (var i = 0; i < 5; i++) + { + var _ = new GameObject($"GameObject_{i}"); } - [Test] - public void CreateViewHierarchy_RootGameObjectCountBiggerThanMaxRootGameObjectCount_PrunesCreatedViewHierarchy() - { - var sut = _fixture.GetSut(); - for (var i = 0; i < 5; i++) - { - var _ = new GameObject($"GameObject_{i}"); - } + var viewHierarchy = sut.CreateViewHierarchy(3, 1, 1); - var viewHierarchy = sut.CreateViewHierarchy(3, 1, 1); + Assert.AreEqual(1, viewHierarchy.Windows.Count); // Sanity check. Root = Currently active scene = 1 + Assert.NotNull(viewHierarchy.Windows[0].Children); + Assert.AreEqual(3, viewHierarchy.Windows[0].Children.Count); + } - Assert.AreEqual(1, viewHierarchy.Windows.Count); // Sanity check. Root = Currently active scene = 1 - Assert.NotNull(viewHierarchy.Windows[0].Children); - Assert.AreEqual(3, viewHierarchy.Windows[0].Children.Count); + [Test] + public void CreateViewHierarchy_RootGameObjectCountSmallerThanMaxRootGameObjectCount_CreatesViewHierarchy() + { + var sut = _fixture.GetSut(); + for (var i = 0; i < 3; i++) + { + var _ = new GameObject($"GameObject_{i}"); } - [Test] - public void CreateViewHierarchy_RootGameObjectCountSmallerThanMaxRootGameObjectCount_CreatesViewHierarchy() - { - var sut = _fixture.GetSut(); - for (var i = 0; i < 3; i++) - { - var _ = new GameObject($"GameObject_{i}"); - } + var viewHierarchy = sut.CreateViewHierarchy(100, 1, 1); - var viewHierarchy = sut.CreateViewHierarchy(100, 1, 1); + Assert.AreEqual(1, viewHierarchy.Windows.Count); // Sanity check. Root = Currently active scene = 1 + Assert.NotNull(viewHierarchy.Windows[0].Children); - Assert.AreEqual(1, viewHierarchy.Windows.Count); // Sanity check. Root = Currently active scene = 1 - Assert.NotNull(viewHierarchy.Windows[0].Children); + Assert.AreEqual(SceneManager.GetActiveScene().rootCount, viewHierarchy.Windows[0].Children.Count); + } - Assert.AreEqual(SceneManager.GetActiveScene().rootCount, viewHierarchy.Windows[0].Children.Count); - } + [Test] + public void CreateNode_HierarchyDepthBiggerThanMaxDepth_PrunesCreatedViewHierarchy() + { + var sut = _fixture.GetSut(); + var testHierarchy = new GameObject("GameObject").transform; + CreateTestHierarchy(5, 1, testHierarchy); + var root = new UnityViewHierarchyNode("root"); - [Test] - public void CreateNode_HierarchyDepthBiggerThanMaxDepth_PrunesCreatedViewHierarchy() - { - var sut = _fixture.GetSut(); - var testHierarchy = new GameObject("GameObject").transform; - CreateTestHierarchy(5, 1, testHierarchy); - var root = new UnityViewHierarchyNode("root"); + sut.CreateNode(3, 1, root, testHierarchy); - sut.CreateNode(3, 1, root, testHierarchy); + Assert.AreEqual(0, root.Children[0].Children[0].Children[0].Children.Count); + } - Assert.AreEqual(0, root.Children[0].Children[0].Children[0].Children.Count); - } + [Test] + public void CreateNode_HierarchyDepthSmallerThanMaxDepth_CapturesHierarchy() + { + var sut = _fixture.GetSut(); + var testHierarchy = new GameObject("GameObject").transform; + CreateTestHierarchy(3, 1, testHierarchy); + var root = new UnityViewHierarchyNode("root"); - [Test] - public void CreateNode_HierarchyDepthSmallerThanMaxDepth_CapturesHierarchy() - { - var sut = _fixture.GetSut(); - var testHierarchy = new GameObject("GameObject").transform; - CreateTestHierarchy(3, 1, testHierarchy); - var root = new UnityViewHierarchyNode("root"); + sut.CreateNode(5, 1, root, testHierarchy); - sut.CreateNode(5, 1, root, testHierarchy); + Assert.AreEqual(0, root.Children[0].Children[0].Children[0].Children.Count); + } - Assert.AreEqual(0, root.Children[0].Children[0].Children[0].Children.Count); - } + [Test] + public void CreateNode_MoreChildrenThanMaxChildCount_PrunesCreatedViewHierarchy() + { + var sut = _fixture.GetSut(); + var testHierarchy = new GameObject("GameObject").transform; + CreateTestHierarchy(2, 5, testHierarchy); - [Test] - public void CreateNode_MoreChildrenThanMaxChildCount_PrunesCreatedViewHierarchy() - { - var sut = _fixture.GetSut(); - var testHierarchy = new GameObject("GameObject").transform; - CreateTestHierarchy(2, 5, testHierarchy); + var root = new UnityViewHierarchyNode("root"); + sut.CreateNode(2, 3, root, testHierarchy); - var root = new UnityViewHierarchyNode("root"); - sut.CreateNode(2, 3, root, testHierarchy); + Assert.AreEqual(1, root.Children.Count); // Sanity check + Assert.AreEqual(3, root.Children[0].Children.Count); + } - Assert.AreEqual(1, root.Children.Count); // Sanity check - Assert.AreEqual(3, root.Children[0].Children.Count); - } + [Test] + public void CreateNode_LessChildrenThanMaxChildCount_CapturesViewHierarchy() + { + var sut = _fixture.GetSut(); + var testHierarchy = new GameObject("GameObject").transform; + CreateTestHierarchy(2, 3, testHierarchy); + var root = new UnityViewHierarchyNode("root"); - [Test] - public void CreateNode_LessChildrenThanMaxChildCount_CapturesViewHierarchy() - { - var sut = _fixture.GetSut(); - var testHierarchy = new GameObject("GameObject").transform; - CreateTestHierarchy(2, 3, testHierarchy); - var root = new UnityViewHierarchyNode("root"); + sut.CreateNode(2, 5, root, testHierarchy); - sut.CreateNode(2, 5, root, testHierarchy); + Assert.AreEqual(1, root.Children.Count); // Sanity check + Assert.AreEqual(3, root.Children[0].Children.Count); + } - Assert.AreEqual(1, root.Children.Count); // Sanity check - Assert.AreEqual(3, root.Children[0].Children.Count); + private void CreateTestHierarchy(int remainingDepth, int childCount, Transform parent) + { + remainingDepth--; + if (remainingDepth <= 0) + { + return; } - private void CreateTestHierarchy(int remainingDepth, int childCount, Transform parent) + for (var i = 0; i < childCount; i++) { - remainingDepth--; - if (remainingDepth <= 0) - { - return; - } - - for (var i = 0; i < childCount; i++) - { - var gameObject = new GameObject($"{parent.name}_{i}"); - gameObject.transform.SetParent(parent); - - CreateTestHierarchy(remainingDepth, childCount, gameObject.transform); - } + var gameObject = new GameObject($"{parent.name}_{i}"); + gameObject.transform.SetParent(parent); + + CreateTestHierarchy(remainingDepth, childCount, gameObject.transform); } } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs index 915a08721..e3afcd03d 100644 --- a/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs @@ -3,32 +3,31 @@ using Sentry.Unity.Integrations; using UnityEngine.TestTools; -namespace Sentry.Unity.Tests +namespace Sentry.Unity.Tests; + +public class UnityWebExceptionFilterTests { - public class UnityWebExceptionFilterTests - { - private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup - private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(0.5f); + private TestHttpClientHandler _testHttpClientHandler = null!; // Set in Setup + private readonly TimeSpan _eventReceiveTimeout = TimeSpan.FromSeconds(0.5f); - [SetUp] - public void SetUp() => - _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); + [SetUp] + public void SetUp() => + _testHttpClientHandler = new TestHttpClientHandler("SetupTestHttpClientHandler"); - [Test] - public void Filter_FiltersBadGatewayExceptionsOfTypeException() => - Assert.IsTrue(new UnityWebExceptionFilter().Filter(new System.Net.WebException(UnityWebExceptionFilter.Message))); + [Test] + public void Filter_FiltersBadGatewayExceptionsOfTypeException() => + Assert.IsTrue(new UnityWebExceptionFilter().Filter(new System.Net.WebException(UnityWebExceptionFilter.Message))); - [Test] - public void Init_WithDefaultOptions_DoesNotSendFilteredWebExceptions() - { - LogAssert.ignoreFailingMessages = true; // The TestHttpClientHandler will complain about timing out (and it should!) + [Test] + public void Init_WithDefaultOptions_DoesNotSendFilteredWebExceptions() + { + LogAssert.ignoreFailingMessages = true; // The TestHttpClientHandler will complain about timing out (and it should!) - using var _ = SentryTests.InitSentrySdk(testHttpClientHandler: _testHttpClientHandler); + using var _ = SentryTests.InitSentrySdk(testHttpClientHandler: _testHttpClientHandler); - SentrySdk.CaptureException(new System.Net.WebException(UnityWebExceptionFilter.Message)); + SentrySdk.CaptureException(new System.Net.WebException(UnityWebExceptionFilter.Message)); - var createdEvent = _testHttpClientHandler.GetEvent(UnityWebExceptionFilter.Message, _eventReceiveTimeout); - Assert.AreEqual(string.Empty, createdEvent); - } + var createdEvent = _testHttpClientHandler.GetEvent(UnityWebExceptionFilter.Message, _eventReceiveTimeout); + Assert.AreEqual(string.Empty, createdEvent); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs b/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs index 55e038905..531a8f61d 100644 --- a/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs +++ b/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs @@ -2,32 +2,31 @@ using System.Globalization; using NUnit.Framework; -namespace Sentry.Unity.iOS.Tests +namespace Sentry.Unity.iOS.Tests; + +public class IosNativeScopeObserverTests { - public class IosNativeScopeObserverTests + [Test] + public void GetTimestamp_ReturnStringConformsToISO8601() { - [Test] - public void GetTimestamp_ReturnStringConformsToISO8601() - { - var timestamp = DateTimeOffset.UtcNow; + var timestamp = DateTimeOffset.UtcNow; - var timestampString = NativeScopeObserver.GetTimestamp(timestamp); - var actualTimestamp = DateTimeOffset.ParseExact(timestampString, "o", CultureInfo.InvariantCulture); + var timestampString = NativeScopeObserver.GetTimestamp(timestamp); + var actualTimestamp = DateTimeOffset.ParseExact(timestampString, "o", CultureInfo.InvariantCulture); - Assert.AreEqual(timestamp, actualTimestamp); - } + Assert.AreEqual(timestamp, actualTimestamp); + } - [Test] - [TestCase(BreadcrumbLevel.Debug, 1)] - [TestCase(BreadcrumbLevel.Info, 2)] - [TestCase(BreadcrumbLevel.Warning, 3)] - [TestCase(BreadcrumbLevel.Error, 4)] - [TestCase(BreadcrumbLevel.Critical, 5)] - public void GetBreadcrumbLevel_TestCases(BreadcrumbLevel level, int expectedNativeLevel) - { - var actualLevel = NativeScopeObserver.GetBreadcrumbLevel(level); + [Test] + [TestCase(BreadcrumbLevel.Debug, 1)] + [TestCase(BreadcrumbLevel.Info, 2)] + [TestCase(BreadcrumbLevel.Warning, 3)] + [TestCase(BreadcrumbLevel.Error, 4)] + [TestCase(BreadcrumbLevel.Critical, 5)] + public void GetBreadcrumbLevel_TestCases(BreadcrumbLevel level, int expectedNativeLevel) + { + var actualLevel = NativeScopeObserver.GetBreadcrumbLevel(level); - Assert.AreEqual(actualLevel, expectedNativeLevel); - } + Assert.AreEqual(actualLevel, expectedNativeLevel); } -} +} \ No newline at end of file diff --git a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs index 15026e269..b6d8694b9 100644 --- a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs +++ b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs @@ -2,52 +2,51 @@ using NUnit.Framework; using UnityEngine; -namespace Sentry.Unity.iOS.Tests +namespace Sentry.Unity.iOS.Tests; + +public class SentryNativeCocoaTests { - public class SentryNativeCocoaTests + [Test] + public void Configure_DefaultConfiguration_iOS() { - [Test] - public void Configure_DefaultConfiguration_iOS() - { - var unityInfo = new TestUnityInfo { IL2CPP = false }; - var options = new SentryUnityOptions(); - SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.IPhonePlayer); - Assert.IsAssignableFrom(options.ScopeObserver); - Assert.IsNotNull(options.CrashedLastRun); - Assert.True(options.EnableScopeSync); - } + var unityInfo = new TestUnityInfo { IL2CPP = false }; + var options = new SentryUnityOptions(); + SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.IPhonePlayer); + Assert.IsAssignableFrom(options.ScopeObserver); + Assert.IsNotNull(options.CrashedLastRun); + Assert.True(options.EnableScopeSync); + } - [Test] - public void Configure_NativeSupportDisabled_iOS() - { - var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; - var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; - SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.IPhonePlayer); - Assert.Null(options.ScopeObserver); - Assert.Null(options.CrashedLastRun); - Assert.False(options.EnableScopeSync); - } + [Test] + public void Configure_NativeSupportDisabled_iOS() + { + var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; + var options = new SentryUnityOptions { IosNativeSupportEnabled = false }; + SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.IPhonePlayer); + Assert.Null(options.ScopeObserver); + Assert.Null(options.CrashedLastRun); + Assert.False(options.EnableScopeSync); + } - [Test] - public void Configure_DefaultConfiguration_macOS() - { - var unityInfo = new TestUnityInfo { IL2CPP = false }; - var options = new SentryUnityOptions(); - // Note: can't test macOS - throws because it tries to call SentryCocoaBridgeProxy.Init() - // but the bridge isn't loaded now... - Assert.Throws(() => - SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.OSXPlayer)); - } + [Test] + public void Configure_DefaultConfiguration_macOS() + { + var unityInfo = new TestUnityInfo { IL2CPP = false }; + var options = new SentryUnityOptions(); + // Note: can't test macOS - throws because it tries to call SentryCocoaBridgeProxy.Init() + // but the bridge isn't loaded now... + Assert.Throws(() => + SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.OSXPlayer)); + } - [Test] - public void Configure_NativeSupportDisabled_macOS() - { - var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; - var options = new SentryUnityOptions { MacosNativeSupportEnabled = false }; - SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.OSXPlayer); - Assert.Null(options.ScopeObserver); - Assert.Null(options.CrashedLastRun); - Assert.False(options.EnableScopeSync); - } + [Test] + public void Configure_NativeSupportDisabled_macOS() + { + var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; + var options = new SentryUnityOptions { MacosNativeSupportEnabled = false }; + SentryNativeCocoa.Configure(options, unityInfo, RuntimePlatform.OSXPlayer); + Assert.Null(options.ScopeObserver); + Assert.Null(options.CrashedLastRun); + Assert.False(options.EnableScopeSync); } -} +} \ No newline at end of file diff --git a/test/SharedClasses/TestApplication.cs b/test/SharedClasses/TestApplication.cs index 8d30076bb..8a920372e 100644 --- a/test/SharedClasses/TestApplication.cs +++ b/test/SharedClasses/TestApplication.cs @@ -2,41 +2,40 @@ using Sentry.Unity.Integrations; using UnityEngine; -namespace Sentry.Unity.Tests.Stubs +namespace Sentry.Unity.Tests.Stubs; + +public sealed class TestApplication : IApplication { - public sealed class TestApplication : IApplication + public TestApplication( + bool isEditor = true, + string productName = "", + string version = "", + string buildGUID = "", + string unityVersion = "", + string persistentDataPath = "", + RuntimePlatform platform = RuntimePlatform.WindowsEditor) { - public TestApplication( - bool isEditor = true, - string productName = "", - string version = "", - string buildGUID = "", - string unityVersion = "", - string persistentDataPath = "", - RuntimePlatform platform = RuntimePlatform.WindowsEditor) - { - IsEditor = isEditor; - ProductName = productName; - Version = version; - BuildGUID = buildGUID; - UnityVersion = unityVersion; - PersistentDataPath = persistentDataPath; - Platform = platform; - } + IsEditor = isEditor; + ProductName = productName; + Version = version; + BuildGUID = buildGUID; + UnityVersion = unityVersion; + PersistentDataPath = persistentDataPath; + Platform = platform; + } - public event Application.LogCallback? LogMessageReceived; - public event Action? Quitting; - public string ActiveSceneName => "TestSceneName"; - public bool IsEditor { get; } - public string ProductName { get; } - public string Version { get; } - public string BuildGUID { get; } - public string UnityVersion { get; set; } - public string PersistentDataPath { get; } - public RuntimePlatform Platform { get; set; } - private void OnQuitting() => Quitting?.Invoke(); + public event Application.LogCallback? LogMessageReceived; + public event Action? Quitting; + public string ActiveSceneName => "TestSceneName"; + public bool IsEditor { get; } + public string ProductName { get; } + public string Version { get; } + public string BuildGUID { get; } + public string UnityVersion { get; set; } + public string PersistentDataPath { get; } + public RuntimePlatform Platform { get; set; } + private void OnQuitting() => Quitting?.Invoke(); - private void OnLogMessageReceived(string condition, string stacktrace, LogType type) - => LogMessageReceived?.Invoke(condition, stacktrace, type); - } -} + private void OnLogMessageReceived(string condition, string stacktrace, LogType type) + => LogMessageReceived?.Invoke(condition, stacktrace, type); +} \ No newline at end of file diff --git a/test/SharedClasses/TestLogger.cs b/test/SharedClasses/TestLogger.cs index 45fe06f59..d0ff5b7af 100644 --- a/test/SharedClasses/TestLogger.cs +++ b/test/SharedClasses/TestLogger.cs @@ -3,30 +3,29 @@ using Sentry.Extensibility; using static System.String; -namespace Sentry.Unity.Tests.SharedClasses +namespace Sentry.Unity.Tests.SharedClasses; + +internal sealed class TestLogger : IDiagnosticLogger { - internal sealed class TestLogger : IDiagnosticLogger + private bool _forwardToUnityLog; + + internal TestLogger(bool forwardToUnityLog = false) { - private bool _forwardToUnityLog; + _forwardToUnityLog = forwardToUnityLog; + } - internal TestLogger(bool forwardToUnityLog = false) - { - _forwardToUnityLog = forwardToUnityLog; - } + internal readonly ConcurrentBag<(SentryLevel logLevel, string message, Exception? exception)> Logs = new(); - internal readonly ConcurrentBag<(SentryLevel logLevel, string message, Exception? exception)> Logs = new(); + public bool IsEnabled(SentryLevel level) => true; - public bool IsEnabled(SentryLevel level) => true; + public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args) + { + var log = (logLevel, string.Format(message, args), exception); + Logs.Add(log); - public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args) + if (_forwardToUnityLog) { - var log = (logLevel, string.Format(message, args), exception); - Logs.Add(log); - - if (_forwardToUnityLog) - { - UnityEngine.Debug.Log($"SentryTestLogger({logLevel}) {Format(message, args)} {exception}"); - } + UnityEngine.Debug.Log($"SentryTestLogger({logLevel}) {Format(message, args)} {exception}"); } } -} +} \ No newline at end of file diff --git a/test/SharedClasses/UnityTestLogger.cs b/test/SharedClasses/UnityTestLogger.cs index c34c24556..df64e20df 100644 --- a/test/SharedClasses/UnityTestLogger.cs +++ b/test/SharedClasses/UnityTestLogger.cs @@ -4,110 +4,109 @@ using NUnit.Framework; using UnityEngine; -namespace Sentry.Unity.Tests.SharedClasses +namespace Sentry.Unity.Tests.SharedClasses; + +internal class UnityTestLogger : ILogger { - internal class UnityTestLogger : ILogger - { - public ILogHandler? logHandler { get; set; } - public bool logEnabled { get; set; } - public LogType filterLogType { get; set; } + public ILogHandler? logHandler { get; set; } + public bool logEnabled { get; set; } + public LogType filterLogType { get; set; } - public List<(LogType, string?, string)> Logs { get; private set; } = new(); + public List<(LogType, string?, string)> Logs { get; private set; } = new(); - public void AssertLogContains(SentryLevel sentryLevel, string message) => - CollectionAssert.Contains( - Logs, - (UnityLogger.GetUnityLogType(sentryLevel), UnityLogger.LogTag, $"({sentryLevel.ToString()}) {message} ")); + public void AssertLogContains(SentryLevel sentryLevel, string message) => + CollectionAssert.Contains( + Logs, + (UnityLogger.GetUnityLogType(sentryLevel), UnityLogger.LogTag, $"({sentryLevel.ToString()}) {message} ")); - private static string GetString(object? message) - { - return message switch - { - null => "Null", - IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture), - _ => message.ToString() - }; - } - - // The SDK uses this one method exclusively - public void Log(LogType logType, string tag, object message) + private static string GetString(object? message) + { + return message switch { - Logs.Add((logType, tag, GetString(message))); - } + null => "Null", + IFormattable formattable => formattable.ToString(null, CultureInfo.InvariantCulture), + _ => message.ToString() + }; + } - public void LogException(Exception exception) - { - throw new NotImplementedException(); - } + // The SDK uses this one method exclusively + public void Log(LogType logType, string tag, object message) + { + Logs.Add((logType, tag, GetString(message))); + } - public bool IsLogTypeAllowed(LogType logType) - { - throw new NotImplementedException(); - } + public void LogException(Exception exception) + { + throw new NotImplementedException(); + } - public void Log(LogType logType, object message) - { - throw new NotImplementedException(); - } + public bool IsLogTypeAllowed(LogType logType) + { + throw new NotImplementedException(); + } - public void Log(LogType logType, object message, UnityEngine.Object context) - { - throw new NotImplementedException(); - } + public void Log(LogType logType, object message) + { + throw new NotImplementedException(); + } - public void Log(LogType logType, string tag, object message, UnityEngine.Object context) - { - throw new NotImplementedException(); - } + public void Log(LogType logType, object message, UnityEngine.Object context) + { + throw new NotImplementedException(); + } - public void Log(object message) - { - throw new NotImplementedException(); - } + public void Log(LogType logType, string tag, object message, UnityEngine.Object context) + { + throw new NotImplementedException(); + } - public void Log(string tag, object message) - { - throw new NotImplementedException(); - } + public void Log(object message) + { + throw new NotImplementedException(); + } - public void Log(string tag, object message, UnityEngine.Object context) - { - throw new NotImplementedException(); - } + public void Log(string tag, object message) + { + throw new NotImplementedException(); + } - public void LogWarning(string tag, object message) - { - throw new NotImplementedException(); - } + public void Log(string tag, object message, UnityEngine.Object context) + { + throw new NotImplementedException(); + } - public void LogWarning(string tag, object message, UnityEngine.Object context) - { - throw new NotImplementedException(); - } + public void LogWarning(string tag, object message) + { + throw new NotImplementedException(); + } - public void LogError(string tag, object message) - { - throw new NotImplementedException(); - } + public void LogWarning(string tag, object message, UnityEngine.Object context) + { + throw new NotImplementedException(); + } - public void LogError(string tag, object message, UnityEngine.Object context) - { - throw new NotImplementedException(); - } + public void LogError(string tag, object message) + { + throw new NotImplementedException(); + } - public void LogFormat(LogType logType, string format, params object[] args) - { - throw new NotImplementedException(); - } + public void LogError(string tag, object message, UnityEngine.Object context) + { + throw new NotImplementedException(); + } - public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args) - { - throw new NotImplementedException(); - } + public void LogFormat(LogType logType, string format, params object[] args) + { + throw new NotImplementedException(); + } - public void LogException(Exception exception, UnityEngine.Object context) - { - throw new NotImplementedException(); - } + public void LogFormat(LogType logType, UnityEngine.Object context, string format, params object[] args) + { + throw new NotImplementedException(); + } + + public void LogException(Exception exception, UnityEngine.Object context) + { + throw new NotImplementedException(); } -} +} \ No newline at end of file From 918dc87fda7eb0e5557445d34f9fd73801b9dbe5 Mon Sep 17 00:00:00 2001 From: bitsandfoxes Date: Wed, 10 Jul 2024 14:28:38 +0200 Subject: [PATCH 13/14] . --- src/Sentry.Unity.Android/SentryJava.cs | 2 + .../SentryNativeAndroid.cs | 127 +++++++++--------- 2 files changed, 63 insertions(+), 66 deletions(-) diff --git a/src/Sentry.Unity.Android/SentryJava.cs b/src/Sentry.Unity.Android/SentryJava.cs index 364c1b675..2e362e7e8 100644 --- a/src/Sentry.Unity.Android/SentryJava.cs +++ b/src/Sentry.Unity.Android/SentryJava.cs @@ -141,10 +141,12 @@ public bool IsSentryJavaPresent() internal class ScopeCallback : AndroidJavaProxy { private readonly Action _callback; + public ScopeCallback(Action callback) : base("io.sentry.ScopeCallback") { _callback = callback; } + // Note: defining the method should be enough with the default Invoke(), but in reality it doesn't work: // No such proxy method: Sentry.Unity.Android.SentryJava+ScopeCallback.run(UnityEngine.AndroidJavaObject) // public void run(AndroidJavaObject scope) => UnityEngine.Debug.Log("run() invoked"); diff --git a/src/Sentry.Unity.Android/SentryNativeAndroid.cs b/src/Sentry.Unity.Android/SentryNativeAndroid.cs index e68bb3326..2a4ba3086 100644 --- a/src/Sentry.Unity.Android/SentryNativeAndroid.cs +++ b/src/Sentry.Unity.Android/SentryNativeAndroid.cs @@ -22,83 +22,78 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry if (!options.AndroidNativeSupportEnabled) { - options.DiagnosticLogger?.LogInfo("Attempting to configure native support via the Android SDK"); + options.DiagnosticLogger?.LogDebug("Native support is disabled for Android"); + return; + } + + if (!SentryJava.IsSentryJavaPresent()) + { + options.DiagnosticLogger?.LogError("Android Native Support has been enabled but the " + + "Sentry Java SDK is missing. This could have been caused by a mismatching" + + "build time / runtime configuration. Please make sure you have " + + "Android Native Support enabled during build time."); + return; + } + + JniExecutor ??= new JniExecutor(); - if (!options.AndroidNativeSupportEnabled) + options.NativeContextWriter = new NativeContextWriter(JniExecutor, SentryJava); + options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor); + options.EnableScopeSync = true; + options.CrashedLastRun = () => + { + var crashedLastRun = SentryJava.CrashedLastRun(JniExecutor); + if (crashedLastRun is null) { - options.DiagnosticLogger?.LogDebug("Native support is disabled for Android"); - return; + // Could happen if the Android SDK wasn't initialized before the .NET layer. + options.DiagnosticLogger? + .LogWarning( + "Unclear from the native SDK if the previous run was a crash. Assuming it was not."); + crashedLastRun = false; } - - if (!SentryJava.IsSentryJavaPresent()) + else { - options.DiagnosticLogger?.LogError("Android Native Support has been enabled but the " + - "Sentry Java SDK is missing. This could have been caused by a mismatching" + - "build time / runtime configuration. Please make sure you have " + - "Android Native Support enabled during build time."); - return; + options.DiagnosticLogger? + .LogDebug("Native Android SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); } - JniExecutor ??= new JniExecutor(); + return crashedLastRun.Value; + }; - options.NativeContextWriter = new NativeContextWriter(JniExecutor, SentryJava); - options.ScopeObserver = new AndroidJavaScopeObserver(options, JniExecutor); - options.EnableScopeSync = true; - options.CrashedLastRun = () => - { - var crashedLastRun = SentryJava.CrashedLastRun(JniExecutor); - if (crashedLastRun is null) - { - // Could happen if the Android SDK wasn't initialized before the .NET layer. - options.DiagnosticLogger? - .LogWarning( - "Unclear from the native SDK if the previous run was a crash. Assuming it was not."); - crashedLastRun = false; - } - else - { - options.DiagnosticLogger? - .LogDebug("Native Android SDK reported: 'crashedLastRun': '{0}'", crashedLastRun); - } - - return crashedLastRun.Value; - }; - - try - { - options.DiagnosticLogger?.LogDebug("Reinstalling native backend."); + try + { + options.DiagnosticLogger?.LogDebug("Reinstalling native backend."); - // At this point Unity has taken the signal handler and will not invoke the original handler (Sentry) - // So we register our backend once more to make sure user-defined data is available in the crash report. - SentryNative.ReinstallBackend(); - } - catch (Exception e) - { - options.DiagnosticLogger?.LogError( - e, "Failed to reinstall backend. Captured native crashes will miss scope data and tag."); - } + // At this point Unity has taken the signal handler and will not invoke the original handler (Sentry) + // So we register our backend once more to make sure user-defined data is available in the crash report. + SentryNative.ReinstallBackend(); + } + catch (Exception e) + { + options.DiagnosticLogger?.LogError( + e, "Failed to reinstall backend. Captured native crashes will miss scope data and tag."); + } - options.NativeSupportCloseCallback = () => Close(options.DiagnosticLogger); + options.NativeSupportCloseCallback = () => Close(options.DiagnosticLogger); - options.DefaultUserId = SentryJava.GetInstallationId(JniExecutor); - if (string.IsNullOrEmpty(options.DefaultUserId)) + options.DefaultUserId = SentryJava.GetInstallationId(JniExecutor); + if (string.IsNullOrEmpty(options.DefaultUserId)) + { + // In case we can't get an installation ID we create one and sync that down to the native layer + options.DiagnosticLogger?.LogDebug( + "Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); + + // We fall back to Unity's Analytics Session Info: https://docs.unity3d.com/ScriptReference/Analytics.AnalyticsSessionInfo-userId.html + // It's a randomly generated GUID that gets created immediately after installation helping + // to identify the same instance of the game + options.DefaultUserId = AnalyticsSessionInfo.userId; + if (options.DefaultUserId is not null) + { + options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); + } + else { - // In case we can't get an installation ID we create one and sync that down to the native layer - options.DiagnosticLogger?.LogDebug( - "Failed to fetch 'Installation ID' from the native SDK. Creating new 'Default User ID'."); - - // We fall back to Unity's Analytics Session Info: https://docs.unity3d.com/ScriptReference/Analytics.AnalyticsSessionInfo-userId.html - // It's a randomly generated GUID that gets created immediately after installation helping - // to identify the same instance of the game - options.DefaultUserId = AnalyticsSessionInfo.userId; - if (options.DefaultUserId is not null) - { - options.ScopeObserver.SetUser(new SentryUser { Id = options.DefaultUserId }); - } - else - { - options.DiagnosticLogger?.LogDebug("Failed to create new 'Default User ID'."); - } + options.DiagnosticLogger?.LogDebug("Failed to create new 'Default User ID'."); } } } From f6bcd3e726f322610fda09677a93191b072f6851 Mon Sep 17 00:00:00 2001 From: Sentry Github Bot Date: Wed, 10 Jul 2024 12:33:57 +0000 Subject: [PATCH 14/14] Format code --- .../AndroidJavaScopeObserver.cs | 2 +- .../BreadcrumbExtensions.cs | 2 +- src/Sentry.Unity.Android/IJniExecutor.cs | 2 +- src/Sentry.Unity.Android/JniExecutor.cs | 30 +++--- .../NativeContextWriter.cs | 2 +- .../BuildPostProcess.cs | 2 +- src/Sentry.Unity.Editor.iOS/NativeMain.cs | 2 +- src/Sentry.Unity.Editor.iOS/NativeOptions.cs | 2 +- .../SentryXcodeProject.cs | 2 +- .../Android/AndroidManifestConfiguration.cs | 2 +- .../Android/AndroidUtils.cs | 2 +- .../Android/DebugSymbolUpload.cs | 2 +- .../Android/GradleSetup.cs | 2 +- .../Android/PostBuildCheck.cs | 2 +- .../Android/ProguardSetup.cs | 2 +- .../SentryPerformanceAutoInstrumentation.cs | 2 +- .../SentryPlayerReaderWriter.cs | 2 +- .../ConfigurationWindow/AdvancedTab.cs | 2 +- .../ConfigurationWindow/CoreTab.cs | 2 +- .../ConfigurationWindow/DebugSymbolsTab.cs | 2 +- .../ConfigurationWindow/EnrichmentTab.cs | 2 +- .../OptionsConfigurationTab.cs | 2 +- .../SentryEditorWindowInstrumentation.cs | 2 +- .../ConfigurationWindow/SentryTestWindow.cs | 2 +- .../ConfigurationWindow/SentryWindow.cs | 2 +- .../ConfigurationWindow/TransportTab.cs | 2 +- .../ConfigurationWindow/Wizard.cs | 2 +- .../ConfigurationWindow/WizardApi.cs | 2 +- .../Il2CppBuildPreProcess.cs | 2 +- .../Native/BuildPostProcess.cs | 2 +- .../ScriptableSentryUnityOptionsEditor.cs | 2 +- src/Sentry.Unity.Editor/SentryCli.cs | 2 +- .../SentryCliOptionsEditor.cs | 2 +- src/Sentry.Unity.Editor/SentryFileUtil.cs | 2 +- src/Sentry.Unity.Editor/SentryPackageInfo.cs | 2 +- .../SentryScriptableObject.cs | 2 +- src/Sentry.Unity.Editor/SentryUnityVersion.cs | 2 +- .../UnityCommandLineArguments.cs | 2 +- .../NativeContextWriter.cs | 2 +- .../NativeScopeObserver.cs | 2 +- src/Sentry.Unity.Native/SentryNative.cs | 2 +- src/Sentry.Unity.Native/SentryNativeBridge.cs | 2 +- src/Sentry.Unity.iOS/NativeScopeObserver.cs | 2 +- .../SentryCocoaBridgeProxy.cs | 2 +- src/Sentry.Unity.iOS/SentryNativeCocoa.cs | 2 +- src/Sentry.Unity/ContextWriter.cs | 2 +- src/Sentry.Unity/EventCapture.cs | 2 +- src/Sentry.Unity/Extensions/JsonExtensions.cs | 2 +- src/Sentry.Unity/ISentryUnityInfo.cs | 2 +- src/Sentry.Unity/Il2CppEventProcessor.cs | 2 +- .../Integrations/AnrIntegration.cs | 2 +- src/Sentry.Unity/Integrations/IApplication.cs | 2 +- .../Integrations/ISceneManager.cs | 2 +- .../Integrations/SceneManagerIntegration.cs | 2 +- .../Integrations/SessionIntegration.cs | 2 +- .../UnityBadGatewayExceptionFilter.cs | 2 +- .../UnityBeforeSceneLoadIntegration.cs | 2 +- .../UnityLogHandlerIntegration.cs | 2 +- .../Integrations/UnityScopeIntegration.cs | 2 +- .../UnitySocketExceptionFilter.cs | 2 +- .../Integrations/UnityWebExceptionFilter.cs | 2 +- src/Sentry.Unity/Json/SafeSerializer.cs | 2 +- src/Sentry.Unity/NativeUtils/CFunctions.cs | 2 +- src/Sentry.Unity/NativeUtils/ContextWriter.cs | 2 +- src/Sentry.Unity/Protocol/Unity.cs | 2 +- src/Sentry.Unity/ScopeObserver.cs | 2 +- src/Sentry.Unity/ScreenshotAttachment.cs | 2 +- .../ScriptableSentryUnityOptions.cs | 2 +- .../SentryBuildTimeOptionsConfiguration.cs | 2 +- src/Sentry.Unity/SentryCliOptions.cs | 2 +- src/Sentry.Unity/SentryMonoBehaviour.cs | 2 +- .../SentryRuntimeOptionsConfiguration.cs | 2 +- src/Sentry.Unity/SentryUnity.cs | 2 +- src/Sentry.Unity/SentryUnityOptions.cs | 2 +- .../SentryUnityOptionsExtensions.cs | 2 +- src/Sentry.Unity/SentryUnitySDK.cs | 2 +- src/Sentry.Unity/SystemInfoAdapter.cs | 2 +- src/Sentry.Unity/TimeDebounceBase.cs | 2 +- src/Sentry.Unity/UnityEventProcessor.cs | 2 +- src/Sentry.Unity/UnityLogger.cs | 2 +- .../UnityViewHierarchyAttachmentContent.cs | 2 +- src/Sentry.Unity/UnityViewHierarchyNode.cs | 2 +- src/Sentry.Unity/UnityWebRequestTransport.cs | 2 +- src/Sentry.Unity/WebGL/SentryWebGL.cs | 2 +- .../SentryNativeAndroidTests.cs | 92 +++++++++---------- .../TestJniExecutor.cs | 2 +- .../AndroidManifestConfigurationTests.cs | 2 +- .../Android/DebugSymbolUploadTests.cs | 2 +- .../Android/GradleSetupTests.cs | 2 +- .../Android/ProguardSetupTests.cs | 2 +- .../EditorModeTests.cs | 2 +- .../EmbeddedResourcesTests.cs | 2 +- .../Il2CppBuildPreProcess.cs | 2 +- .../ScriptableSentryUnityOptionsTests.cs | 2 +- .../SentryCliTests.cs | 2 +- .../SentryScriptableObjectTests.cs | 2 +- .../SentryUnityVersionTests.cs | 2 +- test/Sentry.Unity.Editor.Tests/WizardTests.cs | 2 +- .../BuildPostProcessorTests.cs | 2 +- .../NativeMainTests.cs | 2 +- .../NativeOptionsTests.cs | 2 +- .../SentryXcodeProjectTests.cs | 2 +- test/Sentry.Unity.Tests/AnrDetectionTests.cs | 2 +- test/Sentry.Unity.Tests/ContextWriterTests.cs | 2 +- test/Sentry.Unity.Tests/DebouncerTests.cs | 2 +- test/Sentry.Unity.Tests/IntegrationTests.cs | 2 +- .../Json/SafeSerializerTests.cs | 2 +- .../Sentry.Unity.Tests/Protocol/UnityTests.cs | 2 +- .../SceneManagerIntegrationTests.cs | 2 +- .../ScreenshotAttachmentContentTests.cs | 2 +- .../ScriptableSentryUnityOptionsTests.cs | 2 +- .../SentryMonoBehaviourTests.cs | 2 +- test/Sentry.Unity.Tests/SentryTests.cs | 2 +- .../SentryUnityOptionsExtensionsTests.cs | 2 +- .../SentryUnityOptionsTests.cs | 2 +- test/Sentry.Unity.Tests/SentryUnityTests.cs | 2 +- .../SessionIntegrationTests.cs | 2 +- test/Sentry.Unity.Tests/Stubs/TestHub.cs | 2 +- .../TestBehaviours/TestMonoBehaviour.cs | 2 +- .../TestHttpClientHandler.cs | 2 +- .../UnityBadGatewayExceptionFilterTests.cs | 2 +- .../UnityBeforeSceneLoadIntegrationTests.cs | 2 +- .../UnityEventScopeTests.cs | 2 +- ...UnityIl2CppEventExceptionProcessorTests.cs | 2 +- .../UnityLogHandlerIntegrationTests.cs | 2 +- test/Sentry.Unity.Tests/UnityLoggerTests.cs | 2 +- .../UnitySocketExceptionFilterTests.cs | 2 +- .../UnityViewHierarchyAttachmentTests.cs | 2 +- .../UnityWebExceptionFilterTests.cs | 2 +- .../NativeScopeObserverTests.cs | 2 +- .../SentryNativeIosTests.cs | 2 +- test/SharedClasses/TestApplication.cs | 2 +- test/SharedClasses/TestLogger.cs | 2 +- test/SharedClasses/UnityTestLogger.cs | 2 +- 134 files changed, 193 insertions(+), 193 deletions(-) diff --git a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs index 94ca19eae..ef54bb937 100644 --- a/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs +++ b/src/Sentry.Unity.Android/AndroidJavaScopeObserver.cs @@ -88,4 +88,4 @@ public override void UnsetUserImpl() sentry.CallStatic("setUser", null); }); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Android/BreadcrumbExtensions.cs b/src/Sentry.Unity.Android/BreadcrumbExtensions.cs index 38ce84bbd..c4d0ba69b 100644 --- a/src/Sentry.Unity.Android/BreadcrumbExtensions.cs +++ b/src/Sentry.Unity.Android/BreadcrumbExtensions.cs @@ -25,4 +25,4 @@ public static AndroidJavaObject ToJavaSentryLevel(this BreadcrumbLevel level) _ => javaSentryLevel.GetStatic("INFO") }; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Android/IJniExecutor.cs b/src/Sentry.Unity.Android/IJniExecutor.cs index 02076b552..a12c4f1bc 100644 --- a/src/Sentry.Unity.Android/IJniExecutor.cs +++ b/src/Sentry.Unity.Android/IJniExecutor.cs @@ -6,4 +6,4 @@ internal interface IJniExecutor : IDisposable { public TResult? Run(Func jniOperation); public void Run(Action jniOperation); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Android/JniExecutor.cs b/src/Sentry.Unity.Android/JniExecutor.cs index c02f4d914..f141ad8d3 100644 --- a/src/Sentry.Unity.Android/JniExecutor.cs +++ b/src/Sentry.Unity.Android/JniExecutor.cs @@ -45,23 +45,23 @@ private void DoWork() switch (_currentTask) { case Action action: - { - action.Invoke(); - _taskCompletionSource?.SetResult(null); - break; - } + { + action.Invoke(); + _taskCompletionSource?.SetResult(null); + break; + } case Func func1: - { - var result = func1.Invoke(); - _taskCompletionSource?.SetResult(result); - break; - } + { + var result = func1.Invoke(); + _taskCompletionSource?.SetResult(result); + break; + } case Func func2: - { - var result = func2.Invoke(); - _taskCompletionSource?.SetResult(result); - break; - } + { + var result = func2.Invoke(); + _taskCompletionSource?.SetResult(result); + break; + } default: throw new ArgumentException("Invalid type for _currentTask."); } diff --git a/src/Sentry.Unity.Android/NativeContextWriter.cs b/src/Sentry.Unity.Android/NativeContextWriter.cs index e5fca4959..d1db73e4e 100644 --- a/src/Sentry.Unity.Android/NativeContextWriter.cs +++ b/src/Sentry.Unity.Android/NativeContextWriter.cs @@ -94,4 +94,4 @@ protected override void WriteScope( UnityCopyTextureSupport, UnityRenderingThreadingMode); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs index fe19e1893..4fb8fe7cd 100644 --- a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs @@ -193,4 +193,4 @@ internal static void CopyFile(string sourcePath, string targetPath, IDiagnosticL throw new FileNotFoundException($"Failed to copy '{sourcePath}' to '{targetPath}'"); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor.iOS/NativeMain.cs b/src/Sentry.Unity.Editor.iOS/NativeMain.cs index 28ece36be..e480c4c8e 100644 --- a/src/Sentry.Unity.Editor.iOS/NativeMain.cs +++ b/src/Sentry.Unity.Editor.iOS/NativeMain.cs @@ -59,4 +59,4 @@ internal static string AddSentryToMain(string main) throw new ArgumentException($"Failed to add Sentry to main.\n{main}", nameof(main)); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs index 74a9a16af..abbc91e6c 100644 --- a/src/Sentry.Unity.Editor.iOS/NativeOptions.cs +++ b/src/Sentry.Unity.Editor.iOS/NativeOptions.cs @@ -88,4 +88,4 @@ private static string GetFailedRequestStatusCodesArray(IList public void Dispose() => _pbxProjectType.GetMethod("WriteToFile", BindingFlags.Public | BindingFlags.Instance) .Invoke(_project, new[] { _projectPath }); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs index 9a4a3e848..72e8891bf 100644 --- a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs +++ b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs @@ -470,4 +470,4 @@ private XmlAttribute CreateAndroidAttribute(string key, string value) attr.Value = value; return attr; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Android/AndroidUtils.cs b/src/Sentry.Unity.Editor/Android/AndroidUtils.cs index 28784249c..4ecf6edef 100644 --- a/src/Sentry.Unity.Editor/Android/AndroidUtils.cs +++ b/src/Sentry.Unity.Editor/Android/AndroidUtils.cs @@ -34,4 +34,4 @@ internal static bool ShouldUploadMapping() return false; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs b/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs index 675aa2d4b..04b979f99 100644 --- a/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs +++ b/src/Sentry.Unity.Editor/Android/DebugSymbolUpload.cs @@ -307,4 +307,4 @@ private string GetMappingFilePath(IApplication? application) var mappingPath = mappingPathFormat.Replace("{0}", buildType); return mappingPath; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Android/GradleSetup.cs b/src/Sentry.Unity.Editor/Android/GradleSetup.cs index 9efd5509e..cfee30120 100644 --- a/src/Sentry.Unity.Editor/Android/GradleSetup.cs +++ b/src/Sentry.Unity.Editor/Android/GradleSetup.cs @@ -66,4 +66,4 @@ internal static string LoadGradleScript(string path) } return File.ReadAllText(path); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs b/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs index bb040ee0b..374cf7c2e 100644 --- a/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs +++ b/src/Sentry.Unity.Editor/Android/PostBuildCheck.cs @@ -152,4 +152,4 @@ private void LogFileContent(string title, string fileContent, bool hasError) logFunction(fileContent.Substring(i, chunkLength)); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Android/ProguardSetup.cs b/src/Sentry.Unity.Editor/Android/ProguardSetup.cs index 8bb9affd5..f2dc0f123 100644 --- a/src/Sentry.Unity.Editor/Android/ProguardSetup.cs +++ b/src/Sentry.Unity.Editor/Android/ProguardSetup.cs @@ -108,4 +108,4 @@ private string LoadGradleScript() } return File.ReadAllText(_gradleScriptPath); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs index 6c6b5607b..4259cf651 100644 --- a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs +++ b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPerformanceAutoInstrumentation.cs @@ -115,4 +115,4 @@ private static void ModifyPlayerAssembly(IDiagnosticLogger logger, SentryPlayerR logger.LogInfo("Applying Auto Instrumentation."); playerReaderWriter.Write(); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs index b5fee75bf..b267416da 100644 --- a/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs +++ b/src/Sentry.Unity.Editor/AutoInstrumentation/SentryPlayerReaderWriter.cs @@ -166,4 +166,4 @@ private static MethodDefinition GetMethodDefinition(TypeDefinition typeDefinitio return matchingMethod; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs index ff7398ff6..897b968a2 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/AdvancedTab.cs @@ -204,4 +204,4 @@ internal static void Display(ScriptableSentryUnityOptions options, SentryCliOpti } } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs index 7cc570b57..cc4143427 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/CoreTab.cs @@ -122,4 +122,4 @@ internal static void Display(ScriptableSentryUnityOptions options) EditorGUI.EndDisabledGroup(); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs index 5946d42f6..b60addc0d 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/DebugSymbolsTab.cs @@ -48,4 +48,4 @@ internal static void Display(SentryCliOptions cliOptions) "The project name in Sentry"), cliOptions.Project); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs index 1ac11b504..c61e4681f 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/EnrichmentTab.cs @@ -162,4 +162,4 @@ internal static void Display(ScriptableSentryUnityOptions options) EditorGUILayout.EndToggleGroup(); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs index c21b0c8a0..a5318ae9f 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/OptionsConfigurationTab.cs @@ -175,4 +175,4 @@ internal static void SetScript(string scriptNameWithoutExtension) options.RuntimeOptionsConfiguration ??= optionsConfigurationObject as SentryRuntimeOptionsConfiguration; } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs index af3738405..81773234c 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryEditorWindowInstrumentation.cs @@ -70,4 +70,4 @@ public static bool TryGetValue(this Dictionary dict, String key, } return true; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs index d7eae8841..573b0e18a 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryTestWindow.cs @@ -20,4 +20,4 @@ public void Dispose() Close(); // calls 'OnLostFocus' implicitly AssetDatabase.DeleteAsset(ScriptableSentryUnityOptions.GetConfigPath(SentryOptionsAssetName)); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs index 8fbc73222..054b8e28a 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/SentryWindow.cs @@ -234,4 +234,4 @@ public ValidationError(string propertyName, string reason) public override string ToString() => $"[{PropertyName}] Reason: {Reason}"; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs index 88d27fc29..92803c88b 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/TransportTab.cs @@ -60,4 +60,4 @@ internal static void Display(ScriptableSentryUnityOptions options) ); options.MaxQueueItems = Math.Max(0, options.MaxQueueItems); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs index e462eed5c..fae83dfc0 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/Wizard.cs @@ -353,4 +353,4 @@ private Task RunOnUiThread(Action callback) }; return tcs.Task; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs b/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs index f02bb5dba..9c2f8191e 100644 --- a/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs +++ b/src/Sentry.Unity.Editor/ConfigurationWindow/WizardApi.cs @@ -51,4 +51,4 @@ internal class Organization { public string? name; public string? slug; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs index bce4849a6..e7283753d 100644 --- a/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs +++ b/src/Sentry.Unity.Editor/Il2CppBuildPreProcess.cs @@ -59,4 +59,4 @@ internal static void SetAdditionalIl2CppArguments(SentryUnityOptions options, Fu } } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs index 179725d5b..50419b80b 100644 --- a/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor/Native/BuildPostProcess.cs @@ -270,4 +270,4 @@ private static void UploadDebugSymbols(IDiagnosticLogger logger, BuildTarget tar File.Delete(propertiesFile); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs b/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs index ce63207ce..e746e6643 100644 --- a/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs +++ b/src/Sentry.Unity.Editor/ScriptableSentryUnityOptionsEditor.cs @@ -108,4 +108,4 @@ public override void OnInspectorGUI() EditorGUI.EndDisabledGroup(); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/SentryCli.cs b/src/Sentry.Unity.Editor/SentryCli.cs index 546c2425a..a1f35afec 100644 --- a/src/Sentry.Unity.Editor/SentryCli.cs +++ b/src/Sentry.Unity.Editor/SentryCli.cs @@ -124,4 +124,4 @@ private static string ToCliLogLevel(this SentryLevel level) _ => throw new ArgumentOutOfRangeException(nameof(level), level, null) }; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs b/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs index 54e3ed170..9646057c0 100644 --- a/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs +++ b/src/Sentry.Unity.Editor/SentryCliOptionsEditor.cs @@ -22,4 +22,4 @@ public override void OnInspectorGUI() EditorGUI.EndDisabledGroup(); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/SentryFileUtil.cs b/src/Sentry.Unity.Editor/SentryFileUtil.cs index 66106bb44..506dbbe7f 100644 --- a/src/Sentry.Unity.Editor/SentryFileUtil.cs +++ b/src/Sentry.Unity.Editor/SentryFileUtil.cs @@ -27,4 +27,4 @@ internal static void CopyDirectory(string sourceDirectory, string destinationDir CopyDirectory(subDirectory.FullName, newDestinationDir); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/SentryPackageInfo.cs b/src/Sentry.Unity.Editor/SentryPackageInfo.cs index e84f4cfc8..882278bc3 100644 --- a/src/Sentry.Unity.Editor/SentryPackageInfo.cs +++ b/src/Sentry.Unity.Editor/SentryPackageInfo.cs @@ -25,4 +25,4 @@ internal static string GetName() } internal static bool IsDevPackage => GetName() == PackageNameDev; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/SentryScriptableObject.cs b/src/Sentry.Unity.Editor/SentryScriptableObject.cs index 4b3c672c7..0b3a40c7a 100644 --- a/src/Sentry.Unity.Editor/SentryScriptableObject.cs +++ b/src/Sentry.Unity.Editor/SentryScriptableObject.cs @@ -48,4 +48,4 @@ internal static (SentryUnityOptions?, SentryCliOptions?) ConfiguredBuildTimeOpti return (options, cliOptions); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/SentryUnityVersion.cs b/src/Sentry.Unity.Editor/SentryUnityVersion.cs index 23240f3de..896abb414 100644 --- a/src/Sentry.Unity.Editor/SentryUnityVersion.cs +++ b/src/Sentry.Unity.Editor/SentryUnityVersion.cs @@ -17,4 +17,4 @@ public static bool IsNewerOrEqualThan(string version, IApplication? application var unityVersion = Regex.Replace(application.UnityVersion, "^([0-9]+\\.[0-9]+\\.[0-9]+)[a-z].*$", "$1"); return new Version(unityVersion); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs b/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs index 413bc2eb5..9369529c8 100644 --- a/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs +++ b/src/Sentry.Unity.Editor/UnityCommandLineArguments.cs @@ -25,4 +25,4 @@ internal static Dictionary Parse() } return commandLineArguments; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Native/NativeContextWriter.cs b/src/Sentry.Unity.Native/NativeContextWriter.cs index b3de0731f..1c686abb1 100644 --- a/src/Sentry.Unity.Native/NativeContextWriter.cs +++ b/src/Sentry.Unity.Native/NativeContextWriter.cs @@ -81,4 +81,4 @@ protected override void WriteScope( UnityCopyTextureSupport, UnityRenderingThreadingMode); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Native/NativeScopeObserver.cs b/src/Sentry.Unity.Native/NativeScopeObserver.cs index 3c8618e76..5075febca 100644 --- a/src/Sentry.Unity.Native/NativeScopeObserver.cs +++ b/src/Sentry.Unity.Native/NativeScopeObserver.cs @@ -45,4 +45,4 @@ private static string GetTimestamp(DateTimeOffset timestamp) => // "o": Using ISO 8601 to make sure the timestamp makes it to the bridge correctly. // https://docs.microsoft.com/en-gb/dotnet/standard/base-types/standard-date-and-time-format-strings#Roundtrip timestamp.ToString("o"); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Native/SentryNative.cs b/src/Sentry.Unity.Native/SentryNative.cs index e0394e093..3fec6e556 100644 --- a/src/Sentry.Unity.Native/SentryNative.cs +++ b/src/Sentry.Unity.Native/SentryNative.cs @@ -83,4 +83,4 @@ public static void Configure(SentryUnityOptions options, ISentryUnityInfo sentry } public static void ReinstallBackend() => SentryNativeBridge.ReinstallBackend(); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.Native/SentryNativeBridge.cs b/src/Sentry.Unity.Native/SentryNativeBridge.cs index f6af73ae0..7d67d3325 100644 --- a/src/Sentry.Unity.Native/SentryNativeBridge.cs +++ b/src/Sentry.Unity.Native/SentryNativeBridge.cs @@ -280,4 +280,4 @@ private static void WithMarshalledStruct(T structure, Action action) [DllImport("sentry")] private static extern void sentry_reinstall_backend(); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.iOS/NativeScopeObserver.cs b/src/Sentry.Unity.iOS/NativeScopeObserver.cs index 7f035822a..4f6a02d76 100644 --- a/src/Sentry.Unity.iOS/NativeScopeObserver.cs +++ b/src/Sentry.Unity.iOS/NativeScopeObserver.cs @@ -42,4 +42,4 @@ internal static int GetBreadcrumbLevel(BreadcrumbLevel breadcrumbLevel) => BreadcrumbLevel.Critical => 5, _ => 0 }; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs index 6bf59d7ec..1d280d54e 100644 --- a/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs +++ b/src/Sentry.Unity.iOS/SentryCocoaBridgeProxy.cs @@ -104,4 +104,4 @@ public static bool Init(SentryUnityOptions options) [DllImport("__Internal", EntryPoint = "SentryNativeBridgeGetInstallationId")] public static extern string GetInstallationId(); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs index 92d4fc345..c191822de 100644 --- a/src/Sentry.Unity.iOS/SentryNativeCocoa.cs +++ b/src/Sentry.Unity.iOS/SentryNativeCocoa.cs @@ -86,4 +86,4 @@ public static void Close(IDiagnosticLogger? logger = null) logger?.LogDebug("Closing the sentry-cocoa SDK"); SentryCocoaBridgeProxy.Close(); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/ContextWriter.cs b/src/Sentry.Unity/ContextWriter.cs index 1e6fced8a..40cf729c4 100644 --- a/src/Sentry.Unity/ContextWriter.cs +++ b/src/Sentry.Unity/ContextWriter.cs @@ -91,4 +91,4 @@ protected abstract void WriteScope( string? UnityCopyTextureSupport, string? UnityRenderingThreadingMode ); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/EventCapture.cs b/src/Sentry.Unity/EventCapture.cs index 35dd777f1..fabd9dfe0 100644 --- a/src/Sentry.Unity/EventCapture.cs +++ b/src/Sentry.Unity/EventCapture.cs @@ -3,4 +3,4 @@ namespace Sentry.Unity; internal interface IEventCapture { SentryId Capture(SentryEvent sentryEvent); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Extensions/JsonExtensions.cs b/src/Sentry.Unity/Extensions/JsonExtensions.cs index 4fd87e562..26ef4d68d 100644 --- a/src/Sentry.Unity/Extensions/JsonExtensions.cs +++ b/src/Sentry.Unity/Extensions/JsonExtensions.cs @@ -43,4 +43,4 @@ internal static class JsonExtensions return value; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/ISentryUnityInfo.cs b/src/Sentry.Unity/ISentryUnityInfo.cs index a5788c4eb..3decda350 100644 --- a/src/Sentry.Unity/ISentryUnityInfo.cs +++ b/src/Sentry.Unity/ISentryUnityInfo.cs @@ -38,4 +38,4 @@ public delegate void Il2CppNativeStackTrace( out int numFrames, out string? imageUUID, out string? imageName); -public delegate void Il2CppFree(IntPtr ptr); \ No newline at end of file +public delegate void Il2CppFree(IntPtr ptr); diff --git a/src/Sentry.Unity/Il2CppEventProcessor.cs b/src/Sentry.Unity/Il2CppEventProcessor.cs index adf7218df..aa7554b1c 100644 --- a/src/Sentry.Unity/Il2CppEventProcessor.cs +++ b/src/Sentry.Unity/Il2CppEventProcessor.cs @@ -351,4 +351,4 @@ internal class NativeStackTrace public IntPtr[] Frames { get; set; } = Array.Empty(); public string? ImageUuid { get; set; } public string? ImageName { get; set; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/AnrIntegration.cs b/src/Sentry.Unity/Integrations/AnrIntegration.cs index 4b7b4236e..8d3212dab 100644 --- a/src/Sentry.Unity/Integrations/AnrIntegration.cs +++ b/src/Sentry.Unity/Integrations/AnrIntegration.cs @@ -198,4 +198,4 @@ internal class ApplicationNotResponding : Exception internal ApplicationNotResponding() : base() { } internal ApplicationNotResponding(string message) : base(message) { } internal ApplicationNotResponding(string message, Exception innerException) : base(message, innerException) { } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/IApplication.cs b/src/Sentry.Unity/Integrations/IApplication.cs index c8492705c..876cbd279 100644 --- a/src/Sentry.Unity/Integrations/IApplication.cs +++ b/src/Sentry.Unity/Integrations/IApplication.cs @@ -56,4 +56,4 @@ private void OnLogMessageReceived(string condition, string stackTrace, LogType t private void OnQuitting() => Quitting?.Invoke(); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/ISceneManager.cs b/src/Sentry.Unity/Integrations/ISceneManager.cs index 343a2fa10..4b0d365bb 100644 --- a/src/Sentry.Unity/Integrations/ISceneManager.cs +++ b/src/Sentry.Unity/Integrations/ISceneManager.cs @@ -40,4 +40,4 @@ private SceneManagerAdapter() new SceneAdapter(sceneTo.name)); }; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs b/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs index f71f2d1c5..071fe0b16 100644 --- a/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs +++ b/src/Sentry.Unity/Integrations/SceneManagerIntegration.cs @@ -60,4 +60,4 @@ void SceneManagerOnActiveSceneChanged(SceneAdapter fromScene, SceneAdapter toSce category: "scene.changed"); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/SessionIntegration.cs b/src/Sentry.Unity/Integrations/SessionIntegration.cs index 4202cc920..d02d2c0c2 100644 --- a/src/Sentry.Unity/Integrations/SessionIntegration.cs +++ b/src/Sentry.Unity/Integrations/SessionIntegration.cs @@ -32,4 +32,4 @@ public void Register(IHub hub, SentryOptions options) hub.PauseSession(); }; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs b/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs index f5b8f3832..4dad18f82 100644 --- a/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs +++ b/src/Sentry.Unity/Integrations/UnityBadGatewayExceptionFilter.cs @@ -10,4 +10,4 @@ internal class UnityBadGatewayExceptionFilter : IExceptionFilter public bool Filter(Exception ex) => ex.GetType() == typeof(Exception) && ex.Message.StartsWith(Message); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs b/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs index 5deb1631d..8bf5bedfe 100644 --- a/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityBeforeSceneLoadIntegration.cs @@ -18,4 +18,4 @@ public void Register(IHub hub, SentryOptions options) hub.AddBreadcrumb(message: "BeforeSceneLoad", category: "scene.beforeload", data: data); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs index d34ad03d5..6147ea8cb 100644 --- a/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityLogHandlerIntegration.cs @@ -184,4 +184,4 @@ private static BreadcrumbLevel ToBreadcrumbLevel(LogType logType) LogType.Warning => BreadcrumbLevel.Warning, _ => BreadcrumbLevel.Info }; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs index 9164a4cde..26c26e479 100644 --- a/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs +++ b/src/Sentry.Unity/Integrations/UnityScopeIntegration.cs @@ -179,4 +179,4 @@ private void PopulateUser(Scope scope) } } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs b/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs index e650dcb62..fde19bb34 100644 --- a/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs +++ b/src/Sentry.Unity/Integrations/UnitySocketExceptionFilter.cs @@ -10,4 +10,4 @@ internal class UnitySocketExceptionFilter : IExceptionFilter public bool Filter(Exception ex) => ex.GetType() == typeof(System.Net.Sockets.SocketException) && ex.Message.Equals(Message); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs b/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs index 0de74a360..df388f9ce 100644 --- a/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs +++ b/src/Sentry.Unity/Integrations/UnityWebExceptionFilter.cs @@ -10,4 +10,4 @@ internal class UnityWebExceptionFilter : IExceptionFilter public bool Filter(Exception ex) => ex.GetType() == typeof(System.Net.WebException) && ex.Message.Equals(Message); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Json/SafeSerializer.cs b/src/Sentry.Unity/Json/SafeSerializer.cs index 59f2cf685..035a84062 100644 --- a/src/Sentry.Unity/Json/SafeSerializer.cs +++ b/src/Sentry.Unity/Json/SafeSerializer.cs @@ -29,4 +29,4 @@ internal static class SafeSerializer return null; } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/NativeUtils/CFunctions.cs b/src/Sentry.Unity/NativeUtils/CFunctions.cs index 75f3db7d6..3af0f1b69 100644 --- a/src/Sentry.Unity/NativeUtils/CFunctions.cs +++ b/src/Sentry.Unity/NativeUtils/CFunctions.cs @@ -215,4 +215,4 @@ internal struct sentry_value_t internal double _double; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/NativeUtils/ContextWriter.cs b/src/Sentry.Unity/NativeUtils/ContextWriter.cs index 5971667c9..ec8fe9473 100644 --- a/src/Sentry.Unity/NativeUtils/ContextWriter.cs +++ b/src/Sentry.Unity/NativeUtils/ContextWriter.cs @@ -94,4 +94,4 @@ internal static void WriteUnity( C.SetValueIfNotNull(obj, "rendering_threading_mode", UnityRenderingThreadingMode); C.sentry_set_context(Protocol.Unity.Type, obj); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/Protocol/Unity.cs b/src/Sentry.Unity/Protocol/Unity.cs index a15fc1f21..554aa7ba1 100644 --- a/src/Sentry.Unity/Protocol/Unity.cs +++ b/src/Sentry.Unity/Protocol/Unity.cs @@ -115,4 +115,4 @@ public string ToJsonString(IDiagnosticLogger? logger = null) return Encoding.UTF8.GetString(stream.ToArray()); } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/ScopeObserver.cs b/src/Sentry.Unity/ScopeObserver.cs index f3cddfddb..1a7f0996b 100644 --- a/src/Sentry.Unity/ScopeObserver.cs +++ b/src/Sentry.Unity/ScopeObserver.cs @@ -84,4 +84,4 @@ public void SetUser(SentryUser? user) public abstract void SetUserImpl(SentryUser user); public abstract void UnsetUserImpl(); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/ScreenshotAttachment.cs b/src/Sentry.Unity/ScreenshotAttachment.cs index ef6751b61..452ae0028 100644 --- a/src/Sentry.Unity/ScreenshotAttachment.cs +++ b/src/Sentry.Unity/ScreenshotAttachment.cs @@ -114,4 +114,4 @@ internal byte[] CaptureScreenshot(int width, int height) "Screenshot captured at {0}x{1}: {2} bytes", null, width, height, bytes.Length); return bytes; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index ca37843f6..71cf0fdc4 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -289,4 +289,4 @@ internal bool ShouldDebug(bool isEditorPlayer) return Debug; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs b/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs index d80b4826a..1171a8cd0 100644 --- a/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs +++ b/src/Sentry.Unity/SentryBuildTimeOptionsConfiguration.cs @@ -11,4 +11,4 @@ public abstract class SentryBuildTimeOptionsConfiguration : ScriptableObject /// /// public abstract void Configure(SentryUnityOptions options, SentryCliOptions cliOptions); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryCliOptions.cs b/src/Sentry.Unity/SentryCliOptions.cs index 2398be11f..a36f2da8a 100644 --- a/src/Sentry.Unity/SentryCliOptions.cs +++ b/src/Sentry.Unity/SentryCliOptions.cs @@ -71,4 +71,4 @@ public bool IsValid(IDiagnosticLogger? logger, bool isDevelopmentBuild) return validated; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryMonoBehaviour.cs b/src/Sentry.Unity/SentryMonoBehaviour.cs index 2fabf2296..95895f252 100644 --- a/src/Sentry.Unity/SentryMonoBehaviour.cs +++ b/src/Sentry.Unity/SentryMonoBehaviour.cs @@ -176,4 +176,4 @@ internal void CollectData() MainThreadData.StartTime = null; } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs b/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs index f5b96da39..3840d4016 100644 --- a/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs +++ b/src/Sentry.Unity/SentryRuntimeOptionsConfiguration.cs @@ -11,4 +11,4 @@ public abstract class SentryRuntimeOptionsConfiguration : ScriptableObject /// /// public abstract void Configure(SentryUnityOptions options); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryUnity.cs b/src/Sentry.Unity/SentryUnity.cs index b8996d659..ce7043852 100644 --- a/src/Sentry.Unity/SentryUnity.cs +++ b/src/Sentry.Unity/SentryUnity.cs @@ -47,4 +47,4 @@ public static void Close() UnitySdk?.Close(); UnitySdk = null; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 59bb4b2df..3430c61b1 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -353,4 +353,4 @@ public enum ScreenshotQuality /// Low quality /// Low -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs index b9d6fcb97..c444c42c5 100644 --- a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs +++ b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs @@ -105,4 +105,4 @@ public static void DisableWebExceptionFilter(this SentryUnityOptions options) => /// public static void DisableSocketExceptionFilter(this SentryUnityOptions options) => options.RemoveExceptionFilter(); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SentryUnitySDK.cs b/src/Sentry.Unity/SentryUnitySDK.cs index 35a77fd03..1e58053cd 100644 --- a/src/Sentry.Unity/SentryUnitySDK.cs +++ b/src/Sentry.Unity/SentryUnitySDK.cs @@ -111,4 +111,4 @@ public void Close() "Exception while releasing the lockfile on the config directory.", ex); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/SystemInfoAdapter.cs b/src/Sentry.Unity/SystemInfoAdapter.cs index cbc5fb2c9..bdb8e479e 100644 --- a/src/Sentry.Unity/SystemInfoAdapter.cs +++ b/src/Sentry.Unity/SystemInfoAdapter.cs @@ -112,4 +112,4 @@ public string? DeviceName public Lazy CopyTextureSupport => new(() => SystemInfo.copyTextureSupport.ToString()); public Lazy RenderingThreadingMode => new(() => SystemInfo.renderingThreadingMode.ToString()); public Lazy? StartTime => new(() => DateTimeOffset.UtcNow.AddSeconds(-Time.realtimeSinceStartup)); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/TimeDebounceBase.cs b/src/Sentry.Unity/TimeDebounceBase.cs index 6e0756c13..ba6a6f42c 100644 --- a/src/Sentry.Unity/TimeDebounceBase.cs +++ b/src/Sentry.Unity/TimeDebounceBase.cs @@ -52,4 +52,4 @@ internal sealed class ErrorTimeDebounce : TimeDebounceBase internal sealed class WarningTimeDebounce : TimeDebounceBase { public WarningTimeDebounce(TimeSpan debounceOffset) => DebounceOffset = debounceOffset; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/UnityEventProcessor.cs b/src/Sentry.Unity/UnityEventProcessor.cs index d4fc83f46..c8a2c73d0 100644 --- a/src/Sentry.Unity/UnityEventProcessor.cs +++ b/src/Sentry.Unity/UnityEventProcessor.cs @@ -95,4 +95,4 @@ private void PopulateSdkIntegrations(SdkVersion sdkVersion) internal static class TagValueNormalizer { internal static string ToTagValue(this bool value) => value ? "true" : "false"; -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/UnityLogger.cs b/src/Sentry.Unity/UnityLogger.cs index 7c2936cbe..4f7ef6b0a 100644 --- a/src/Sentry.Unity/UnityLogger.cs +++ b/src/Sentry.Unity/UnityLogger.cs @@ -45,4 +45,4 @@ internal static LogType GetUnityLogType(SentryLevel logLevel) } public override string ToString() => nameof(UnityLogger); -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs b/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs index 3024df30f..fc98144e9 100644 --- a/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs +++ b/src/Sentry.Unity/UnityViewHierarchyAttachmentContent.cs @@ -98,4 +98,4 @@ internal void CreateNode(int remainingDepth, int maxChildCount, ViewHierarchyNod CreateNode(remainingDepth, maxChildCount, node, transform.GetChild(i)); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/UnityViewHierarchyNode.cs b/src/Sentry.Unity/UnityViewHierarchyNode.cs index 1c14bd097..08da6e468 100644 --- a/src/Sentry.Unity/UnityViewHierarchyNode.cs +++ b/src/Sentry.Unity/UnityViewHierarchyNode.cs @@ -51,4 +51,4 @@ protected override void WriteAdditionalProperties(Utf8JsonWriter writer, IDiagno writer.WriteEndArray(); } } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/UnityWebRequestTransport.cs b/src/Sentry.Unity/UnityWebRequestTransport.cs index 816c27556..a72dad527 100644 --- a/src/Sentry.Unity/UnityWebRequestTransport.cs +++ b/src/Sentry.Unity/UnityWebRequestTransport.cs @@ -119,4 +119,4 @@ private UnityWebRequest CreateWebRequest(HttpRequestMessage message) response.Content = new StringContent(www.downloadHandler.text); return response; } -} \ No newline at end of file +} diff --git a/src/Sentry.Unity/WebGL/SentryWebGL.cs b/src/Sentry.Unity/WebGL/SentryWebGL.cs index 7a33f68ec..c46f68888 100644 --- a/src/Sentry.Unity/WebGL/SentryWebGL.cs +++ b/src/Sentry.Unity/WebGL/SentryWebGL.cs @@ -44,4 +44,4 @@ public static void Configure(SentryUnityOptions options) // Indicate that this platform doesn't support running background threads. options.MultiThreading = false; } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs index a36de49de..6e10d8328 100644 --- a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs +++ b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs @@ -17,15 +17,15 @@ public SentryNativeAndroidTests() [SetUp] public void SetUp() { - _originalReinstallSentryNativeBackendStrategy = - Interlocked.Exchange(ref SentryNative.ReinstallSentryNativeBackendStrategy, - _fakeReinstallSentryNativeBackendStrategy); - _reinstallCalled = false; - _sentryUnityInfo = new TestUnityInfo { IL2CPP = false }; + _originalReinstallSentryNativeBackendStrategy = + Interlocked.Exchange(ref SentryNative.ReinstallSentryNativeBackendStrategy, + _fakeReinstallSentryNativeBackendStrategy); + _reinstallCalled = false; + _sentryUnityInfo = new TestUnityInfo { IL2CPP = false }; - SentryNativeAndroid.JniExecutor = new TestJniExecutor(); - SentryNativeAndroid.SentryJava = new TestSentryJava(); - } + SentryNativeAndroid.JniExecutor = new TestJniExecutor(); + SentryNativeAndroid.SentryJava = new TestSentryJava(); + } [TearDown] public void TearDown() => @@ -36,73 +36,73 @@ public void TearDown() => [Test] public void Configure_DefaultConfiguration_SetsScopeObserver() { - var options = new SentryUnityOptions(); - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.IsAssignableFrom(options.ScopeObserver); - } + var options = new SentryUnityOptions(); + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.IsAssignableFrom(options.ScopeObserver); + } [Test] public void Configure_DefaultConfiguration_SetsCrashedLastRun() { - var options = new SentryUnityOptions(); - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.IsNotNull(options.CrashedLastRun); - } + var options = new SentryUnityOptions(); + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.IsNotNull(options.CrashedLastRun); + } [Test] public void Configure_NativeAndroidSupportDisabled_ObserverIsNull() { - var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.Null(options.ScopeObserver); - } + var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.Null(options.ScopeObserver); + } [Test] public void Configure_DefaultConfiguration_EnablesScopeSync() { - var options = new SentryUnityOptions(); - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.True(options.EnableScopeSync); - } + var options = new SentryUnityOptions(); + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.True(options.EnableScopeSync); + } [Test] public void Configure_NativeAndroidSupportDisabled_DisabledScopeSync() { - var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.False(options.EnableScopeSync); - } + var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.False(options.EnableScopeSync); + } [Test] [TestCase(true, true)] [TestCase(false, true)] public void Configure_IL2CPP_ReInitializesNativeBackend(bool il2cpp, bool expectedReinstall) { - _sentryUnityInfo.IL2CPP = il2cpp; - Assert.False(_reinstallCalled); // Sanity check + _sentryUnityInfo.IL2CPP = il2cpp; + Assert.False(_reinstallCalled); // Sanity check - SentryNativeAndroid.Configure(new(), _sentryUnityInfo); + SentryNativeAndroid.Configure(new(), _sentryUnityInfo); - Assert.AreEqual(expectedReinstall, _reinstallCalled); - } + Assert.AreEqual(expectedReinstall, _reinstallCalled); + } [Test] public void Configure_NativeAndroidSupportDisabled_DoesNotReInitializeNativeBackend() { - var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.False(_reinstallCalled); - } + var options = new SentryUnityOptions { AndroidNativeSupportEnabled = false }; + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.False(_reinstallCalled); + } [Test] public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() { - var options = new SentryUnityOptions(); - var sentryJava = SentryNativeAndroid.SentryJava as TestSentryJava; - Assert.NotNull(sentryJava); - sentryJava!.InstallationId = string.Empty; - - SentryNativeAndroid.Configure(options, _sentryUnityInfo); - Assert.False(string.IsNullOrEmpty(options.DefaultUserId)); - } -} \ No newline at end of file + var options = new SentryUnityOptions(); + var sentryJava = SentryNativeAndroid.SentryJava as TestSentryJava; + Assert.NotNull(sentryJava); + sentryJava!.InstallationId = string.Empty; + + SentryNativeAndroid.Configure(options, _sentryUnityInfo); + Assert.False(string.IsNullOrEmpty(options.DefaultUserId)); + } +} diff --git a/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs b/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs index b6d1ab839..2e3fbf049 100644 --- a/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs +++ b/test/Sentry.Unity.Android.Tests/TestJniExecutor.cs @@ -17,4 +17,4 @@ public void Dispose() { // TODO release managed resources here } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs b/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs index a47d7f5c8..29c98fa12 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/AndroidManifestConfigurationTests.cs @@ -426,4 +426,4 @@ public struct SentryJavaLevel public SentryLevel SentryLevel; public string JavaLevel; } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs b/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs index b64b16617..5d0d5cc6c 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/DebugSymbolUploadTests.cs @@ -255,4 +255,4 @@ public static void SetupFakeProject(string fakeProjectPath) } private string GetGradleFilePath() => Path.Combine(_fixture.GradleProjectPath, "launcher/build.gradle"); -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs b/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs index cf6cf0917..5d7ec7ea3 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/GradleSetupTests.cs @@ -105,4 +105,4 @@ public void InsertIntoScope_ResultMatchesExpected(string testCaseFileName) StringAssert.AreEqualIgnoringCase(actualResult, expectedGradleContent); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs b/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs index e2eb27a40..3b2583dc0 100644 --- a/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs +++ b/test/Sentry.Unity.Editor.Tests/Android/ProguardSetupTests.cs @@ -130,4 +130,4 @@ public void RemovesRuleFromGradleScript(string testCaseFileName) StringAssert.AreEqualIgnoringCase(File.ReadAllText(expectedPath), File.ReadAllText(actualOutputPath)); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs b/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs index 19b5bf526..3d5e506db 100644 --- a/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs +++ b/test/Sentry.Unity.Editor.Tests/EditorModeTests.cs @@ -74,4 +74,4 @@ public void ValidateDsn_CorrectFormat_CreatesNoError() // assert Assert.AreEqual(0, validationErrors.Count); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs b/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs index bdaaa1664..a567a8a26 100644 --- a/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs +++ b/test/Sentry.Unity.Editor.Tests/EmbeddedResourcesTests.cs @@ -14,4 +14,4 @@ public void Resources_Embedded() Assert.Contains("Sentry.Unity.Editor.Resources.SentryLogoLight.png", resourceNames); Assert.Contains("Sentry.Unity.Editor.Resources.SentryLogoDark.png", resourceNames); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs index 738b3f0fc..78bcd81d1 100644 --- a/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs +++ b/test/Sentry.Unity.Editor.Tests/Il2CppBuildPreProcess.cs @@ -82,4 +82,4 @@ public void SetAdditionalArguments_Il2CppDisabledAndArgumentAlreadyAdded_Removes Assert.That(resultingArguments, Does.Contain(expectedArgument)); Assert.That(resultingArguments, Does.Not.Contain(Il2CppBuildPreProcess.SourceMappingArgument)); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs b/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs index 640137a6b..37f2e0ee8 100644 --- a/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Editor.Tests/ScriptableSentryUnityOptionsTests.cs @@ -55,4 +55,4 @@ public void ScriptableSentryUnityOptions_Creation_AllPropertiesPresent() AssetDatabase.DeleteAsset(testOptionsPath); AssetDatabase.Refresh(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs b/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs index a93ecc982..9bbedce78 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs @@ -137,4 +137,4 @@ public void UrlOverride() Assert.AreEqual("https://example.com", SentryCli.UrlOverride("https://key@example.com/12345", null)); Assert.AreEqual("http://localhost:8000", SentryCli.UrlOverride("http://key@localhost:8000/12345", null)); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs b/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs index fe827085e..595e4389e 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryScriptableObjectTests.cs @@ -76,4 +76,4 @@ public void Load_SentryCliOptionsExist_LoadsSavedOptionsAsset() Assert.NotNull(actualOptions); Assert.AreEqual(expectedAuth, actualOptions!.Auth); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs b/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs index f075836d7..bafe12726 100644 --- a/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs +++ b/test/Sentry.Unity.Editor.Tests/SentryUnityVersionTests.cs @@ -35,4 +35,4 @@ public void IsNewerOrEqual(string currentUnityVersion, string versionToCheck, bo Assert.AreEqual(expected, actual); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.Tests/WizardTests.cs b/test/Sentry.Unity.Editor.Tests/WizardTests.cs index 8cf74ba60..248f9f18e 100644 --- a/test/Sentry.Unity.Editor.Tests/WizardTests.cs +++ b/test/Sentry.Unity.Editor.Tests/WizardTests.cs @@ -50,4 +50,4 @@ public void Step2Response() Assert.NotNull(key.dsn); Assert.AreEqual("dsn-public", key.dsn!.@public); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs index 710e1d27f..c5aed861e 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs @@ -230,4 +230,4 @@ public void CopyFile_FailedToCopyFile_ThrowsFileNotFoundException() => Assert.Throws(() => BuildPostProcess.CopyFile("non-existent-path.m", Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs index e4ba3f6f8..63a26dad2 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/NativeMainTests.cs @@ -78,4 +78,4 @@ private string GetFileContents(string fileName) return File.ReadAllText(mainPath); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs index 5c009b3b2..21feb313c 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/NativeOptionsTests.cs @@ -116,4 +116,4 @@ public void CreateOptionsFile_FilterBadGatewayDisabled_DoesNotAddFiltering() File.Delete(testOptionsFileName); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs index dcc96891d..2e0d8ee74 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs @@ -164,4 +164,4 @@ public void AddBuildPhaseSymbolUpload_PhaseAlreadyAdded_LogsAndDoesNotAddAgain() log.message.Contains("already added."))); // Sanity check Assert.AreEqual(expectedBuildPhaseOccurence, actualBuildPhaseOccurence); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/AnrDetectionTests.cs b/test/Sentry.Unity.Tests/AnrDetectionTests.cs index 8d8629c18..179121e1b 100644 --- a/test/Sentry.Unity.Tests/AnrDetectionTests.cs +++ b/test/Sentry.Unity.Tests/AnrDetectionTests.cs @@ -142,4 +142,4 @@ public IEnumerator IsNotAffectedByTimeScale() Assert.IsNull(anr); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index 830b08775..1d49809f3 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -228,4 +228,4 @@ protected override void WriteScope( this.UnityRenderingThreadingMode = UnityRenderingThreadingMode; SyncFinished.Set(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/DebouncerTests.cs b/test/Sentry.Unity.Tests/DebouncerTests.cs index 812a27685..1602e1845 100644 --- a/test/Sentry.Unity.Tests/DebouncerTests.cs +++ b/test/Sentry.Unity.Tests/DebouncerTests.cs @@ -54,4 +54,4 @@ private IEnumerator AssertDefaultDebounce(TimeDebounceBase debouncer) // pass Assert.IsTrue(debouncer.Debounced()); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/IntegrationTests.cs b/test/Sentry.Unity.Tests/IntegrationTests.cs index b650ccf19..45cc411a8 100644 --- a/test/Sentry.Unity.Tests/IntegrationTests.cs +++ b/test/Sentry.Unity.Tests/IntegrationTests.cs @@ -342,4 +342,4 @@ private sealed class SentryDisposable : IDisposable { public void Dispose() => SentrySdk.Close(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs b/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs index b4d7d6ad0..9ae42bcf6 100644 --- a/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs +++ b/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs @@ -67,4 +67,4 @@ private class SerializationTestClass { public string Member => throw new DivideByZeroException(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/Protocol/UnityTests.cs b/test/Sentry.Unity.Tests/Protocol/UnityTests.cs index d6af787a3..e51c54a0d 100644 --- a/test/Sentry.Unity.Tests/Protocol/UnityTests.cs +++ b/test/Sentry.Unity.Tests/Protocol/UnityTests.cs @@ -71,4 +71,4 @@ public void SerializeObject_TestCase_SerializesAsExpected((Unity.Protocol.Unity new object[] { (new Unity.Protocol.Unity { RenderingThreadingMode = "NativeGraphicsJobs" }, "{\"type\":\"unity\",\"rendering_threading_mode\":\"NativeGraphicsJobs\"}") }, new object[] { (new Unity.Protocol.Unity { TargetFrameRate = "30" }, "{\"type\":\"unity\",\"target_frame_rate\":\"30\"}") } }; -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs b/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs index ecd6927df..bae4b4af2 100644 --- a/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/SceneManagerIntegrationTests.cs @@ -128,4 +128,4 @@ private class FakeSceneManager : ISceneManager public void OnSceneUnloaded(SceneAdapter scene) => SceneUnloaded?.Invoke(scene); public void OnActiveSceneChanged(SceneAdapter fromScene, SceneAdapter toScene) => ActiveSceneChanged?.Invoke(fromScene, toScene); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs b/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs index 2e33e2ba9..010141aac 100644 --- a/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs +++ b/test/Sentry.Unity.Tests/ScreenshotAttachmentContentTests.cs @@ -93,4 +93,4 @@ public void CaptureScreenshot_QualitySetToFull_ScreenshotInFullSize() Assert.IsTrue(texture.width == testScreenSize && texture.height == testScreenSize); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs index d7d916a81..f16a298b2 100644 --- a/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs @@ -168,4 +168,4 @@ private static string GetTestOptionsFilePath() Assert.NotNull(assemblyFolderPath); return Path.Combine(assemblyFolderPath!, TestSentryOptionsFileName); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs b/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs index bc4aab423..268bb2fcb 100644 --- a/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs +++ b/test/Sentry.Unity.Tests/SentryMonoBehaviourTests.cs @@ -78,4 +78,4 @@ public void UpdatePauseStatus_ResumedTwice_ApplicationResumingInvokedOnlyOnce() Assert.AreEqual(1, counter); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SentryTests.cs b/test/Sentry.Unity.Tests/SentryTests.cs index 99e9d30c2..d0d8494c1 100644 --- a/test/Sentry.Unity.Tests/SentryTests.cs +++ b/test/Sentry.Unity.Tests/SentryTests.cs @@ -24,4 +24,4 @@ private sealed class SentryDisposable : IDisposable { public void Dispose() => SentrySdk.Close(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs index f040e2019..226ad0a03 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsExtensionsTests.cs @@ -129,4 +129,4 @@ public void SetupLogging_DiagnosticLoggerSet_LeavesOrRemovesDiagnosticLogger(boo Assert.AreEqual(debug, options.DiagnosticLogger is not null); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs index 9046dd4da..943f45ac3 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs @@ -74,4 +74,4 @@ public void Ctor_Release_IgnoresDotOnlyProductNames() [Test] public void Ctor_IsEnvironmentUser_IsFalse() => Assert.AreEqual(false, _fixture.GetSut().IsEnvironmentUser); -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SentryUnityTests.cs b/test/Sentry.Unity.Tests/SentryUnityTests.cs index 4adea2f32..8f543ee88 100644 --- a/test/Sentry.Unity.Tests/SentryUnityTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityTests.cs @@ -104,4 +104,4 @@ public void Init_MultipleTimes_LogsWarning() log.logLevel == SentryLevel.Warning && log.message.Contains("The SDK has already been initialized."))); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/SessionIntegrationTests.cs b/test/Sentry.Unity.Tests/SessionIntegrationTests.cs index 656f72c1b..6fb7aee3f 100644 --- a/test/Sentry.Unity.Tests/SessionIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/SessionIntegrationTests.cs @@ -40,4 +40,4 @@ private sealed class SentryDisposable : IDisposable { public void Dispose() => SentrySdk.Close(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/Stubs/TestHub.cs b/test/Sentry.Unity.Tests/Stubs/TestHub.cs index 6eb4cd442..8ca84ee34 100644 --- a/test/Sentry.Unity.Tests/Stubs/TestHub.cs +++ b/test/Sentry.Unity.Tests/Stubs/TestHub.cs @@ -168,4 +168,4 @@ public SentryId CaptureEvent(SentryEvent evt, SentryHint? hint, Action co { throw new NotImplementedException(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs b/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs index bbc12fc7b..b77e89ad2 100644 --- a/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs +++ b/test/Sentry.Unity.Tests/TestBehaviours/TestMonoBehaviour.cs @@ -26,4 +26,4 @@ public void DebugLogExceptionInTask(string message) => Task.Run(() => LogAssert.ignoreFailingMessages = true; DebugLogException(message); }); -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/TestHttpClientHandler.cs b/test/Sentry.Unity.Tests/TestHttpClientHandler.cs index e8460c259..be2014a00 100644 --- a/test/Sentry.Unity.Tests/TestHttpClientHandler.cs +++ b/test/Sentry.Unity.Tests/TestHttpClientHandler.cs @@ -73,4 +73,4 @@ public string GetEvent(string identifier, TimeSpan timeout) Debug.LogError($"{UnityLogger.LogTag}{name} timed out waiting for an event."); return string.Empty; } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs index da057ec45..b854a16d6 100644 --- a/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnityBadGatewayExceptionFilterTests.cs @@ -54,4 +54,4 @@ private sealed class SentryDisposable : IDisposable { public void Dispose() => SentrySdk.Close(); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs index 2437acf8c..dadae1413 100644 --- a/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/UnityBeforeSceneLoadIntegrationTests.cs @@ -43,4 +43,4 @@ public void Register_Breadcrumb_Added() Assert.AreEqual(1, _hub.ConfigureScopeCalls.Count); Assert.AreEqual("scene.beforeload", breadcrumb.Category); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index d48453fec..ceae96a62 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -603,4 +603,4 @@ internal sealed class TestSentrySystemInfo : ISentrySystemInfo public Lazy? CopyTextureSupport { get; set; } public Lazy? RenderingThreadingMode { get; set; } public Lazy? StartTime { get; set; } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs b/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs index 418364ebd..a54e3d68d 100644 --- a/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs +++ b/test/Sentry.Unity.Tests/UnityIl2CppEventExceptionProcessorTests.cs @@ -17,4 +17,4 @@ public void NormalizeUuid_ReturnValueMatchesExpected(string input, string expect Assert.AreEqual(actual, expected); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs index 3cd9cbed8..0a30d573c 100644 --- a/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs +++ b/test/Sentry.Unity.Tests/UnityLogHandlerIntegrationTests.cs @@ -256,4 +256,4 @@ public void Register_RegisteredASecondTime_LogsWarningAndReturns() log.logLevel == SentryLevel.Warning && log.message.Contains("UnityLogHandlerIntegration has already been registered."))); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityLoggerTests.cs b/test/Sentry.Unity.Tests/UnityLoggerTests.cs index 951acf9dd..daad31632 100644 --- a/test/Sentry.Unity.Tests/UnityLoggerTests.cs +++ b/test/Sentry.Unity.Tests/UnityLoggerTests.cs @@ -50,4 +50,4 @@ public void Log_SetsTag() // The format is: "(logType, tag, message)" StringAssert.AreEqualIgnoringCase(UnityLogger.LogTag, testLogger.Logs[0].Item2); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs index e3323a7e1..3bd8445b6 100644 --- a/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnitySocketExceptionFilterTests.cs @@ -30,4 +30,4 @@ public void Init_WithDefaultOptions_DoesNotSendFilteredSocketExceptions() var createdEvent = _testHttpClientHandler.GetEvent(UnitySocketExceptionFilter.Message, _eventReceiveTimeout); Assert.AreEqual(string.Empty, createdEvent); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs b/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs index e7576e729..9ff8cda43 100644 --- a/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs +++ b/test/Sentry.Unity.Tests/UnityViewHierarchyAttachmentTests.cs @@ -179,4 +179,4 @@ private void CreateTestHierarchy(int remainingDepth, int childCount, Transform p CreateTestHierarchy(remainingDepth, childCount, gameObject.transform); } } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs b/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs index e3afcd03d..ae33598b6 100644 --- a/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs +++ b/test/Sentry.Unity.Tests/UnityWebExceptionFilterTests.cs @@ -30,4 +30,4 @@ public void Init_WithDefaultOptions_DoesNotSendFilteredWebExceptions() var createdEvent = _testHttpClientHandler.GetEvent(UnityWebExceptionFilter.Message, _eventReceiveTimeout); Assert.AreEqual(string.Empty, createdEvent); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs b/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs index 531a8f61d..0323e6173 100644 --- a/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs +++ b/test/Sentry.Unity.iOS.Tests/NativeScopeObserverTests.cs @@ -29,4 +29,4 @@ public void GetBreadcrumbLevel_TestCases(BreadcrumbLevel level, int expectedNati Assert.AreEqual(actualLevel, expectedNativeLevel); } -} \ No newline at end of file +} diff --git a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs index b6d8694b9..cd6fd5b10 100644 --- a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs +++ b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs @@ -49,4 +49,4 @@ public void Configure_NativeSupportDisabled_macOS() Assert.Null(options.CrashedLastRun); Assert.False(options.EnableScopeSync); } -} \ No newline at end of file +} diff --git a/test/SharedClasses/TestApplication.cs b/test/SharedClasses/TestApplication.cs index 8a920372e..4d542b529 100644 --- a/test/SharedClasses/TestApplication.cs +++ b/test/SharedClasses/TestApplication.cs @@ -38,4 +38,4 @@ public TestApplication( private void OnLogMessageReceived(string condition, string stacktrace, LogType type) => LogMessageReceived?.Invoke(condition, stacktrace, type); -} \ No newline at end of file +} diff --git a/test/SharedClasses/TestLogger.cs b/test/SharedClasses/TestLogger.cs index d0ff5b7af..867223663 100644 --- a/test/SharedClasses/TestLogger.cs +++ b/test/SharedClasses/TestLogger.cs @@ -28,4 +28,4 @@ public void Log(SentryLevel logLevel, string message, Exception? exception = nul UnityEngine.Debug.Log($"SentryTestLogger({logLevel}) {Format(message, args)} {exception}"); } } -} \ No newline at end of file +} diff --git a/test/SharedClasses/UnityTestLogger.cs b/test/SharedClasses/UnityTestLogger.cs index df64e20df..5e4182075 100644 --- a/test/SharedClasses/UnityTestLogger.cs +++ b/test/SharedClasses/UnityTestLogger.cs @@ -109,4 +109,4 @@ public void LogException(Exception exception, UnityEngine.Object context) { throw new NotImplementedException(); } -} \ No newline at end of file +}