diff --git a/CHANGELOG.md b/CHANGELOG.md index 40dd39dc1..456c261a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ **Migration**: Update your `using` directives from `using Sentry;` to `using Sentry.Unity;`. IDEs like Rider can automatically import the missing references. In some cases, you may need both `using Sentry.Unity;` (for the static API) and `using Sentry;` (for types like `SentryId`). No changes are required to your actual SDK method calls (e.g., `SentrySdk.CaptureException()` - remains the same). ([#2227](https://github.com/getsentry/sentry-unity/pull/2227)) + remains the same). ([#2227](https://github.com/getsentry/sentry-unity/pull/2227), [#2239](https://github.com/getsentry/sentry-unity/pull/2239)) ### Features diff --git a/src/Sentry.Unity/Il2CppEventProcessor.cs b/src/Sentry.Unity/Il2CppEventProcessor.cs index 68247ef77..00886084b 100644 --- a/src/Sentry.Unity/Il2CppEventProcessor.cs +++ b/src/Sentry.Unity/Il2CppEventProcessor.cs @@ -21,6 +21,7 @@ public UnityIl2CppEventExceptionProcessor(SentryUnityOptions options) { Options = options; UnityInfo = options.UnityInfo; + // We're throwing here but this should never happen. We're validating UnityInfo before adding the processor. _il2CppMethods = UnityInfo.Il2CppMethods ?? throw new ArgumentNullException(nameof(UnityInfo.Il2CppMethods), "Unity IL2CPP methods are not available."); diff --git a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs index c2fe884d3..2eefd17e9 100644 --- a/src/Sentry.Unity/ScriptableSentryUnityOptions.cs +++ b/src/Sentry.Unity/ScriptableSentryUnityOptions.cs @@ -115,28 +115,31 @@ public static string GetConfigPath(string? notDefaultConfigName = null) [field: SerializeField] public SentryLevel DiagnosticLevel { get; set; } = SentryLevel.Warning; /// - /// Loads the ScriptableSentryUnityOptions from `Resource`. + /// Loads the ScriptableSentryUnityOptions from Resource. /// - /// The SentryUnityOptions generated from the ScriptableSentryUnityOptions + /// The SentryUnityOptions generated from the ScriptableSentryUnityOptions /// - /// Used for loading the SentryUnityOptions from the ScriptableSentryUnityOptions during runtime. + /// This gets called from SentryInitialization during the game's startup. /// public static SentryUnityOptions? LoadSentryUnityOptions() { var scriptableOptions = Resources.Load($"{ConfigRootFolder}/{ConfigName}"); if (scriptableOptions is not null) { - return scriptableOptions.ToSentryUnityOptions(false); + return scriptableOptions.ToSentryUnityOptions(); } return null; } - internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityInfo? unityInfo = null, IApplication? application = null) + internal SentryUnityOptions ToSentryUnityOptions( + ISentryUnityInfo? unityInfo = null, + IApplication? application = null, + bool isBuilding = false) { application ??= ApplicationAdapter.Instance; - var options = new SentryUnityOptions(isBuilding, application, unityInfo) + var options = new SentryUnityOptions(unityInfo, application, isBuilding: isBuilding) { Enabled = Enabled, Dsn = Dsn, @@ -186,6 +189,12 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn PerformanceAutoInstrumentationEnabled = AutoAwakeTraces, }; + // By default, the cacheDirectoryPath gets set on known platforms. We're overwriting this behaviour here. + if (!EnableOfflineCaching) + { + options.CacheDirectoryPath = null; + } + if (!string.IsNullOrWhiteSpace(ReleaseOverride)) { options.Release = ReleaseOverride; @@ -227,23 +236,11 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn // Without setting up here we might miss out on logs between option-loading (now) and Init - i.e. native configuration options.SetupUnityLogging(); - if (options.AttachViewHierarchy) - { - options.AddEventProcessor(new ViewHierarchyEventProcessor(options)); - } - if (options.AttachScreenshot) - { - options.AddEventProcessor(new ScreenshotEventProcessor(options)); - } - - if (!application.IsEditor && options.Il2CppLineNumberSupportEnabled && unityInfo?.Il2CppMethods != null) - { - options.AddIl2CppExceptionProcessor(); - } - - HandlePlatformRestrictedOptions(options, application); + // ExceptionFilters are added by default to the options. HandleExceptionFilter(options); + // The AnrDetectionIntegration is added by default. Since it is a ScriptableUnityOptions-only property we have to + // remove the integration when creating the options through here if (!AnrDetectionEnabled) { options.DisableAnrIntegration(); @@ -252,41 +249,6 @@ internal SentryUnityOptions ToSentryUnityOptions(bool isBuilding, ISentryUnityIn return options; } - internal void HandlePlatformRestrictedOptions(SentryUnityOptions options, IApplication application) - { - if (!options.UnityInfo.IsKnownPlatform()) - { - options.DisableFileWrite = true; - - // 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 it 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; - } - - return; - } - - // Only assign the cache directory path if we're on a "known" platform. Accessing `Application.persistentDataPath` - // implicitly creates a directory and leads to crashes i.e. on the Switch. - options.CacheDirectoryPath = EnableOfflineCaching ? application.PersistentDataPath : null; - } - private void HandleExceptionFilter(SentryUnityOptions options) { if (!options.FilterBadGatewayExceptions) diff --git a/src/Sentry.Unity/SentryUnityOptions.cs b/src/Sentry.Unity/SentryUnityOptions.cs index 0aba6aa4a..34144e7cc 100644 --- a/src/Sentry.Unity/SentryUnityOptions.cs +++ b/src/Sentry.Unity/SentryUnityOptions.cs @@ -298,14 +298,14 @@ internal string? DefaultUserId internal ISentryUnityInfo UnityInfo { get; private set; } internal Action? PlatformConfiguration { get; private set; } - public SentryUnityOptions() : this(false, ApplicationAdapter.Instance) { } - - internal SentryUnityOptions(bool isBuilding, IApplication application, ISentryUnityInfo? unityInfo = null) : - this(SentryMonoBehaviour.Instance, application, isBuilding, unityInfo) - { } + public SentryUnityOptions() : this(isBuilding: false) { } // For testing - internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication application, bool isBuilding, ISentryUnityInfo? unityInfo = null) + internal SentryUnityOptions( + ISentryUnityInfo? unityInfo = null, + IApplication? application = null, + SentryMonoBehaviour? behaviour = null, + bool isBuilding = false) { // NOTE: 'SentryPlatformServices.UnityInfo' throws when the UnityInfo has not been set. This should not happen. // The PlatformServices are set through the RuntimeLoad attribute in 'SentryInitialization.cs' and are required @@ -313,30 +313,33 @@ internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication applicat UnityInfo = unityInfo ?? SentryPlatformServices.UnityInfo; PlatformConfiguration = SentryPlatformServices.PlatformConfiguration; + application ??= ApplicationAdapter.Instance; + behaviour ??= SentryMonoBehaviour.Instance; + // IL2CPP doesn't support Process.GetCurrentProcess().StartupTime DetectStartupTime = StartupTimeDetectionMode.Fast; - this.AddInAppExclude("UnityEngine"); - this.AddInAppExclude("UnityEditor"); + AddInAppExclude("UnityEngine"); + AddInAppExclude("UnityEditor"); var processor = new UnityEventProcessor(this); - this.AddEventProcessor(processor); - this.AddTransactionProcessor(processor); - this.AddExceptionProcessor(new UnityExceptionProcessor()); - - this.AddIntegration(new UnityLogHandlerIntegration(this)); - this.AddIntegration(new UnityApplicationLoggingIntegration()); - this.AddIntegration(new StartupTracingIntegration()); - this.AddIntegration(new AnrIntegration(behaviour)); - this.AddIntegration(new UnityScopeIntegration(application, unityInfo)); - this.AddIntegration(new UnityBeforeSceneLoadIntegration()); - this.AddIntegration(new SceneManagerIntegration()); - this.AddIntegration(new SceneManagerTracingIntegration()); - this.AddIntegration(new SessionIntegration(behaviour)); - this.AddIntegration(new TraceGenerationIntegration(behaviour)); - - this.AddExceptionFilter(new UnityBadGatewayExceptionFilter()); - this.AddExceptionFilter(new UnityWebExceptionFilter()); - this.AddExceptionFilter(new UnitySocketExceptionFilter()); + AddEventProcessor(processor); + AddTransactionProcessor(processor); + AddExceptionProcessor(new UnityExceptionProcessor()); + + AddIntegration(new UnityLogHandlerIntegration(this)); + AddIntegration(new UnityApplicationLoggingIntegration()); + AddIntegration(new StartupTracingIntegration()); + AddIntegration(new AnrIntegration(behaviour)); + AddIntegration(new UnityScopeIntegration(application, unityInfo)); + AddIntegration(new UnityBeforeSceneLoadIntegration()); + AddIntegration(new SceneManagerIntegration()); + AddIntegration(new SceneManagerTracingIntegration()); + AddIntegration(new SessionIntegration(behaviour)); + AddIntegration(new TraceGenerationIntegration(behaviour)); + + AddExceptionFilter(new UnityBadGatewayExceptionFilter()); + AddExceptionFilter(new UnityWebExceptionFilter()); + AddExceptionFilter(new UnitySocketExceptionFilter()); IsGlobalModeEnabled = true; @@ -373,6 +376,13 @@ internal SentryUnityOptions(SentryMonoBehaviour behaviour, IApplication applicat { LogType.Error, true}, { LogType.Exception, true}, }; + + // Only assign the cache directory path if we're on a "known" platform. Accessing `Application.persistentDataPath` + // implicitly creates a directory and leads to crashes i.e. on the Switch. + if (unityInfo?.IsKnownPlatform() ?? false) + { + CacheDirectoryPath = application.PersistentDataPath; + } } public override string ToString() diff --git a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs index ea97618f5..6793e0116 100644 --- a/src/Sentry.Unity/SentryUnityOptionsExtensions.cs +++ b/src/Sentry.Unity/SentryUnityOptionsExtensions.cs @@ -63,11 +63,6 @@ internal static void SetupUnityLogging(this SentryUnityOptions options) } } - internal static void AddIl2CppExceptionProcessor(this SentryUnityOptions options) - { - options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options)); - } - /// /// Disables the capture of errors through . /// diff --git a/src/Sentry.Unity/SentryUnitySdk.cs b/src/Sentry.Unity/SentryUnitySdk.cs index 052139275..c590cbbda 100644 --- a/src/Sentry.Unity/SentryUnitySdk.cs +++ b/src/Sentry.Unity/SentryUnitySdk.cs @@ -11,7 +11,7 @@ internal class SentryUnitySdk { private readonly SentryUnityOptions _options; private IDisposable _dotnetSdk = null!; - private FileStream? _lockFile; + private FileStream? LockFile; private SentryUnitySdk(SentryUnityOptions options) { @@ -30,30 +30,14 @@ private SentryUnitySdk(SentryUnityOptions options) MainThreadData.CollectData(); - // 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) - { - 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; - } - } + // Some integrations are controlled through a flag and opt-in. Adding these integrations late so we have equal + // behaviour whether the options got created through the ScriptableObject or the SDK gets manually initialized + AddIntegrations(options); + SetUpWindowsPlayerCaching(unitySdk, options); - unitySdk._dotnetSdk = Sentry.SentrySdk.Init(options); + ConfigureUnsupportedPlatformFallbacks(options); - // We can safely call this during initialization. If the SDK self-initialized we're right on time. If the SDK - // was initialized manually, the RuntimeOnLoad attributes already triggered, making this call a no-op. - StartupTracingIntegration.StartTracing(); + unitySdk._dotnetSdk = Sentry.SentrySdk.Init(options); // We can safely call this during initialization. If the SDK self-initialized we're right on time. If the SDK // was initialized manually, the RuntimeOnLoad attributes already triggered, making this call a no-op. @@ -99,7 +83,7 @@ public void Close() try { // We don't really need to close, Windows would release the lock anyway, but let's be nice. - _lockFile?.Close(); + LockFile?.Close(); } catch (Exception ex) { @@ -141,4 +125,77 @@ public void CaptureFeedback(string message, string? email, string? name, bool ad Sentry.SentrySdk.CurrentHub.CaptureFeedback(message, email, name, hint: hint); } + + internal static void SetUpWindowsPlayerCaching(SentryUnitySdk unitySdk, SentryUnityOptions options) + { + // On Windows-Standalone, we disable cache dir in case multiple app instances run over the same path. + // Note: we cannot use a named Mutex, because Unity 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 not RuntimePlatform.WindowsPlayer || + options.CacheDirectoryPath is null) + { + return; + } + + 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; + } + } + + internal static void AddIntegrations(SentryUnityOptions options) + { + if (options.AttachViewHierarchy) + { + options.AddEventProcessor(new ViewHierarchyEventProcessor(options)); + } + if (options.AttachScreenshot) + { + options.AddEventProcessor(new ScreenshotEventProcessor(options)); + } + + if (!ApplicationAdapter.Instance.IsEditor && + options.UnityInfo.IL2CPP && + options.Il2CppLineNumberSupportEnabled) + { + if (options.UnityInfo.Il2CppMethods is not null) + { + options.AddExceptionProcessor(new UnityIl2CppEventExceptionProcessor(options)); + } + else + { + options.DiagnosticLogger?.LogWarning("Failed to find required IL2CPP methods - Skipping line number support"); + } + } + } + + internal static void ConfigureUnsupportedPlatformFallbacks(SentryUnityOptions options) + { + if (!options.UnityInfo.IsKnownPlatform()) + { + options.DisableFileWrite = true; + + // 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; + } + + // 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); + } + } + } } diff --git a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs index ab485b68b..1496407f9 100644 --- a/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs +++ b/test/Sentry.Unity.Android.Tests/SentryNativeAndroidTests.cs @@ -41,7 +41,7 @@ public void TearDown() => [Test] public void Configure_DefaultConfiguration_SetsScopeObserver() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); SentryNativeAndroid.Configure(options); @@ -51,7 +51,7 @@ public void Configure_DefaultConfiguration_SetsScopeObserver() [Test] public void Configure_DefaultConfiguration_SetsCrashedLastRun() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); SentryNativeAndroid.Configure(options); @@ -61,7 +61,7 @@ public void Configure_DefaultConfiguration_SetsCrashedLastRun() [Test] public void Configure_NativeAndroidSupportDisabled_ObserverIsNull() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); options.AndroidNativeSupportEnabled = false; SentryNativeAndroid.Configure(options); @@ -72,7 +72,7 @@ public void Configure_NativeAndroidSupportDisabled_ObserverIsNull() [Test] public void Configure_DefaultConfiguration_EnablesScopeSync() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); SentryNativeAndroid.Configure(options); @@ -82,7 +82,7 @@ public void Configure_DefaultConfiguration_EnablesScopeSync() [Test] public void Configure_NativeAndroidSupportDisabled_DisabledScopeSync() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); options.AndroidNativeSupportEnabled = false; SentryNativeAndroid.Configure(options); @@ -96,7 +96,7 @@ public void Configure_NativeAndroidSupportDisabled_DisabledScopeSync() public void Configure_IL2CPP_ReInitializesNativeBackend(bool il2cpp, bool expectedReinstall) { _sentryUnityInfo.IL2CPP = il2cpp; - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); Assert.False(_reinstallCalled); // Sanity check @@ -108,7 +108,7 @@ public void Configure_IL2CPP_ReInitializesNativeBackend(bool il2cpp, bool expect [Test] public void Configure_NativeAndroidSupportDisabled_DoesNotReInitializeNativeBackend() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); options.AndroidNativeSupportEnabled = false; SentryNativeAndroid.Configure(options); @@ -119,7 +119,7 @@ public void Configure_NativeAndroidSupportDisabled_DoesNotReInitializeNativeBack [Test] public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo); + var options = new SentryUnityOptions(_sentryUnityInfo); _testSentryJava.InstallationId = string.Empty; SentryNativeAndroid.Configure(options); @@ -130,7 +130,7 @@ public void Configure_NoInstallationIdReturned_SetsNewDefaultUserId() [Test] public void Configure_DefaultConfigurationSentryJavaNotPresent_LogsErrorAndReturns() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo) + var options = new SentryUnityOptions(_sentryUnityInfo) { Debug = true, DiagnosticLevel = SentryLevel.Debug, @@ -150,7 +150,7 @@ public void Configure_DefaultConfigurationSentryJavaNotPresent_LogsErrorAndRetur [Test] public void Configure_NativeAlreadyInitialized_LogsAndConfigures() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo) + var options = new SentryUnityOptions(_sentryUnityInfo) { Debug = true, DiagnosticLevel = SentryLevel.Debug, @@ -171,7 +171,7 @@ public void Configure_NativeAlreadyInitialized_LogsAndConfigures() [Test] public void Configure_NativeInitFails_LogsErrorAndReturns() { - var options = new SentryUnityOptions(false, new TestApplication(), _sentryUnityInfo) + var options = new SentryUnityOptions(_sentryUnityInfo) { Debug = true, DiagnosticLevel = SentryLevel.Debug, diff --git a/test/Sentry.Unity.Tests/ContextWriterTests.cs b/test/Sentry.Unity.Tests/ContextWriterTests.cs index b432c5ea0..eca1dd663 100644 --- a/test/Sentry.Unity.Tests/ContextWriterTests.cs +++ b/test/Sentry.Unity.Tests/ContextWriterTests.cs @@ -66,7 +66,7 @@ public void Arguments() }; var context = new MockContextWriter(); - var options = new SentryUnityOptions(_sentryMonoBehaviour, _testApplication, false) + var options = new SentryUnityOptions(application: _testApplication, behaviour: _sentryMonoBehaviour) { Dsn = "http://publickey@localhost/12345", Enabled = true, diff --git a/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs index d0ecaf59e..e858ae8cc 100644 --- a/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/ScriptableSentryUnityOptionsTests.cs @@ -94,7 +94,7 @@ public void ToSentryUnityOptions_ValueMapping_AreEqual(bool isBuilding, bool ena scriptableOptions.DebugOnlyInEditor = false; // Affects Debug otherwise scriptableOptions.DiagnosticLevel = expectedOptions.DiagnosticLevel; - var optionsActual = scriptableOptions.ToSentryUnityOptions(isBuilding, _fixture.UnityInfo, _fixture.Application); + var optionsActual = scriptableOptions.ToSentryUnityOptions(_fixture.UnityInfo, _fixture.Application, isBuilding); AssertOptions(expectedOptions, optionsActual); } @@ -122,30 +122,18 @@ public void ToSentryUnityOptions_HasOptionsConfiguration_GetsCalled(bool isBuild var scriptableOptions = ScriptableObject.CreateInstance(); scriptableOptions.RuntimeOptionsConfiguration = optionsConfiguration; - scriptableOptions.ToSentryUnityOptions(isBuilding, _fixture.UnityInfo); + scriptableOptions.ToSentryUnityOptions(_fixture.UnityInfo, isBuilding: isBuilding); Assert.AreEqual(optionsConfiguration.GotCalled, !isBuilding); } - [Test] - public void ToSentryUnityOptions_UnknownPlatforms_DoesNotAccessDisk() - { - var scriptableOptions = ScriptableObject.CreateInstance(); - _fixture.UnityInfo = new TestUnityInfo(false); - - var options = scriptableOptions.ToSentryUnityOptions(false, _fixture.UnityInfo, _fixture.Application); - - Assert.IsNull(options.CacheDirectoryPath); - Assert.IsFalse(options.AutoSessionTracking); - } - [Test] public void ToSentryUnityOptions_WebExceptionFilterAdded() { var scriptableOptions = ScriptableObject.CreateInstance(); _fixture.UnityInfo = new TestUnityInfo(true); - var options = scriptableOptions.ToSentryUnityOptions(false, _fixture.UnityInfo, _fixture.Application); + var options = scriptableOptions.ToSentryUnityOptions(_fixture.UnityInfo, _fixture.Application); var exceptionFiltersPropertyInfo = typeof(SentryOptions).GetProperty("ExceptionFilters", BindingFlags.NonPublic | BindingFlags.Instance); var filters = exceptionFiltersPropertyInfo.GetValue(options) as List; @@ -158,7 +146,7 @@ public void ToSentryUnityOptions_UnitySocketExceptionFilterAdded() var scriptableOptions = ScriptableObject.CreateInstance(); _fixture.UnityInfo = new TestUnityInfo(true); - var options = scriptableOptions.ToSentryUnityOptions(false, _fixture.UnityInfo, _fixture.Application); + var options = scriptableOptions.ToSentryUnityOptions(_fixture.UnityInfo, _fixture.Application); var exceptionFiltersPropertyInfo = typeof(SentryOptions).GetProperty("ExceptionFilters", BindingFlags.NonPublic | BindingFlags.Instance); var filters = exceptionFiltersPropertyInfo.GetValue(options) as List; @@ -171,49 +159,13 @@ public void ToSentryUnityOptions_UnityBadGatewayExceptionFilterAdded() var scriptableOptions = ScriptableObject.CreateInstance(); _fixture.UnityInfo = new TestUnityInfo(true); - var options = scriptableOptions.ToSentryUnityOptions(false, _fixture.UnityInfo, _fixture.Application); + var options = scriptableOptions.ToSentryUnityOptions(_fixture.UnityInfo, _fixture.Application); var exceptionFiltersPropertyInfo = typeof(SentryOptions).GetProperty("ExceptionFilters", BindingFlags.NonPublic | BindingFlags.Instance); var filters = exceptionFiltersPropertyInfo.GetValue(options) as List; Assert.True(filters.OfType().Any()); } - [Test] - public void HandlePlatformRestrictedOptions_UnknownPlatform_SetsRestrictedOptions() - { - _fixture.UnityInfo = new TestUnityInfo(false); - - var scriptableOptions = ScriptableObject.CreateInstance(); - scriptableOptions.EnableOfflineCaching = true; - - var options = new SentryUnityOptions(false, _fixture.Application, _fixture.UnityInfo) - { - DisableFileWrite = false, - CacheDirectoryPath = "some/path", - AutoSessionTracking = true - }; - - scriptableOptions.HandlePlatformRestrictedOptions(options, _fixture.Application); - - Assert.IsTrue(options.DisableFileWrite); - Assert.IsNull(options.CacheDirectoryPath); - Assert.IsFalse(options.AutoSessionTracking); - Assert.IsTrue(options.BackgroundWorker is WebBackgroundWorker); - } - - [Test] - public void HandlePlatformRestrictedOptions_KnownPlatform_SetsRestrictedOptions() - { - var scriptableOptions = ScriptableObject.CreateInstance(); - scriptableOptions.EnableOfflineCaching = true; - - var options = new SentryUnityOptions(false, _fixture.Application, _fixture.UnityInfo); - - scriptableOptions.HandlePlatformRestrictedOptions(options, _fixture.Application); - - Assert.AreEqual(options.CacheDirectoryPath, _fixture.Application.PersistentDataPath); - } - public static void AssertOptions(SentryUnityOptions expected, SentryUnityOptions actual) { Assert.AreEqual(expected.Enabled, actual.Enabled); diff --git a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs index 943f45ac3..91f1a433f 100644 --- a/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityOptionsTests.cs @@ -7,6 +7,7 @@ public sealed class SentryUnityOptionsTests { class Fixture { + public TestUnityInfo UnityInfo { get; set; } = new(); public TestApplication Application { get; set; } = new( productName: "TestApplication", version: "0.1.0", @@ -14,7 +15,7 @@ class Fixture persistentDataPath: "test/persistent/data/path"); public bool IsBuilding { get; set; } - public SentryUnityOptions GetSut() => new SentryUnityOptions(IsBuilding, Application); + public SentryUnityOptions GetSut() => new(UnityInfo, Application, isBuilding: IsBuilding); } [SetUp] @@ -37,7 +38,17 @@ public void Ctor_Environment_IsNull(bool isEditor, bool isBuilding, string expec } [Test] - public void Ctor_CacheDirectoryPath_IsNull() => Assert.IsNull(_fixture.GetSut().CacheDirectoryPath); + [TestCase(true, "some/path", "some/path")] + [TestCase(false, "some/path", null)] + public void Ctor_IfPlatformIsKnown_SetsCacheDirectoryPath(bool isKnownPlatform, string applicationDataPath, string? expectedCacheDirectoryPath) + { + _fixture.UnityInfo = new TestUnityInfo(isKnownPlatform: isKnownPlatform); + _fixture.Application.PersistentDataPath = applicationDataPath; + + var sut = _fixture.GetSut(); + + Assert.AreEqual(sut.CacheDirectoryPath, expectedCacheDirectoryPath); + } [Test] public void Ctor_IsGlobalModeEnabled_IsTrue() => Assert.IsTrue(_fixture.GetSut().IsGlobalModeEnabled); diff --git a/test/Sentry.Unity.Tests/SentryUnityTests.cs b/test/Sentry.Unity.Tests/SentryUnityTests.cs index e3326cad6..9e1dd58d5 100644 --- a/test/Sentry.Unity.Tests/SentryUnityTests.cs +++ b/test/Sentry.Unity.Tests/SentryUnityTests.cs @@ -6,6 +6,7 @@ using Debug = UnityEngine.Debug; using Sentry.Extensibility; using Sentry.Unity.Tests.SharedClasses; +using Sentry.Unity.Tests.Stubs; namespace Sentry.Unity.Tests; @@ -173,4 +174,21 @@ public void GetLastRunState_WithNullDelegate_ReturnsUnknown() // Assert Assert.AreEqual(SentrySdk.CrashedLastRun.Unknown, result); } + + [Test] + public void ConfigureUnsupportedPlatformFallbacks() + { + var unityInfo = new TestUnityInfo(false); + var options = new SentryUnityOptions(unityInfo) + { + DisableFileWrite = false, + AutoSessionTracking = true + }; + + SentryUnitySdk.ConfigureUnsupportedPlatformFallbacks(options); + + Assert.IsTrue(options.DisableFileWrite); + Assert.IsFalse(options.AutoSessionTracking); + Assert.IsTrue(options.BackgroundWorker is WebBackgroundWorker); + } } diff --git a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs index 2085e1682..17deedc88 100644 --- a/test/Sentry.Unity.Tests/UnityEventScopeTests.cs +++ b/test/Sentry.Unity.Tests/UnityEventScopeTests.cs @@ -113,7 +113,7 @@ public void SentrySdkCaptureEvent(bool captureOnUiThread) RenderingThreadingMode = new Lazy(() => "MultiThreaded"), StartTime = new(() => DateTimeOffset.UtcNow), }; - var options = new SentryUnityOptions(_sentryMonoBehaviour, _testApplication, false, new TestUnityInfo { IL2CPP = true }) + var options = new SentryUnityOptions(new TestUnityInfo { IL2CPP = true }, _testApplication, _sentryMonoBehaviour) { Dsn = "https://b8fd848b31444e80aa102e96d2a6a648@o510466.ingest.sentry.io/5606182", Enabled = true, diff --git a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs index 6bb16cc49..4e66b4bfb 100644 --- a/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs +++ b/test/Sentry.Unity.iOS.Tests/SentryNativeIosTests.cs @@ -10,7 +10,7 @@ public class SentryNativeCocoaTests [Test] public void Configure_DefaultConfiguration_iOS() { - var options = new SentryUnityOptions(false, new TestApplication(), new TestUnityInfo { IL2CPP = false }); + var options = new SentryUnityOptions(new TestUnityInfo { IL2CPP = false }); // Note: can't test iOS - throws because it tries to call SentryCocoaBridgeProxy.Init() // but the bridge isn't loaded now... @@ -22,7 +22,7 @@ public void Configure_DefaultConfiguration_iOS() public void Configure_NativeSupportDisabled_iOS() { var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; - var options = new SentryUnityOptions(false, new TestApplication(), unityInfo) { IosNativeSupportEnabled = false }; + var options = new SentryUnityOptions(unityInfo) { IosNativeSupportEnabled = false }; SentryNativeCocoa.Configure(options, RuntimePlatform.IPhonePlayer); Assert.Null(options.ScopeObserver); Assert.Null(options.CrashedLastRun); @@ -32,7 +32,7 @@ public void Configure_NativeSupportDisabled_iOS() [Test] public void Configure_DefaultConfiguration_macOS() { - var options = new SentryUnityOptions(false, new TestApplication(), new TestUnityInfo { IL2CPP = false }); + var options = new SentryUnityOptions(new TestUnityInfo { IL2CPP = false }); // Note: can't test macOS - throws because it tries to call SentryCocoaBridgeProxy.Init() // but the bridge isn't loaded now... Assert.Throws(() => @@ -43,7 +43,7 @@ public void Configure_DefaultConfiguration_macOS() public void Configure_NativeSupportDisabled_macOS() { var unityInfo = new TestUnityInfo(true, false, false) { IL2CPP = false }; - var options = new SentryUnityOptions(false, new TestApplication(), unityInfo) { MacosNativeSupportEnabled = false }; + var options = new SentryUnityOptions(unityInfo) { MacosNativeSupportEnabled = false }; SentryNativeCocoa.Configure(options, RuntimePlatform.OSXPlayer); Assert.Null(options.ScopeObserver); Assert.Null(options.CrashedLastRun); diff --git a/test/SharedClasses/TestApplication.cs b/test/SharedClasses/TestApplication.cs index d41a46826..c8551297d 100644 --- a/test/SharedClasses/TestApplication.cs +++ b/test/SharedClasses/TestApplication.cs @@ -32,7 +32,7 @@ public TestApplication( public string Version { get; } public string BuildGUID { get; } public string UnityVersion { get; set; } - public string PersistentDataPath { get; } + public string PersistentDataPath { get; set; } public RuntimePlatform Platform { get; set; } private void OnQuitting() => Quitting?.Invoke();