diff --git a/CHANGELOG.md b/CHANGELOG.md index e6b899e623..1787cb908c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Fixes +- Source context for class libraries when running on Android in Release mode ([#4294](https://github.com/getsentry/sentry-dotnet/pull/4294)) - Native AOT: don't load SentryNative on unsupported platforms ([#4347](https://github.com/getsentry/sentry-dotnet/pull/4347)) ### Dependencies diff --git a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs index f88e0ca237..e49fbf7147 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AndroidAssemblyStoreReaderV2.cs @@ -100,11 +100,19 @@ public static bool TryReadStore(string inputFile, IList supportedAbis, D return assembly; } + // If the assembly name ends with .dll or .exe, try to find it without the extension. if ((IsFileType(".dll") || IsFileType(".exe")) && FindBestAssembly(name[..^4], out assembly)) { return assembly; } + // Conversely, if there is no extension, try with the dll extension (sometimes required for class libraries). + // See: https://github.com/getsentry/sentry-dotnet/issues/4278#issuecomment-2986009125 + if (!IsFileType(".dll") && !IsFileType(".exe") && FindBestAssembly(name + ".dll", out assembly)) + { + return assembly; + } + return null; bool IsFileType(string extension) @@ -119,10 +127,12 @@ private bool FindBestAssembly(string name, out ExplorerStoreItem? explorerAssemb { if (explorer.AssembliesByName?.TryGetValue(name, out var assembly) is true) { + _logger?.Invoke("Found best assembly {0} in APK AssemblyStore for target arch {1}", name, explorer.TargetArch); explorerAssembly = new(explorer, assembly); return true; } } + _logger?.Invoke("No best assembly for {0} in APK AssemblyStore", name); explorerAssembly = null; return false; } diff --git a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs index de2148ddeb..424109bcee 100644 --- a/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs +++ b/src/Sentry.Android.AssemblyReader/V2/AssemblyStoreExplorer.cs @@ -33,6 +33,7 @@ private AssemblyStoreExplorer(Stream storeStream, string path, DebugLogger? logg { foreach (var item in Assemblies) { + logger?.Invoke("Assembly {0} indexed from AssemblyStore {1}", item.Name, path); dict.Add(item.Name, item); } } diff --git a/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs b/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs index 5f38609d16..2f22bcbbbd 100644 --- a/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs +++ b/src/Sentry.Maui.CommunityToolkit.Mvvm/SentryOptionsExtensions.cs @@ -12,7 +12,7 @@ public static class SentryOptionsExtensions /// public static SentryMauiOptions AddCommunityToolkitIntegration(this SentryMauiOptions options) { - options.AddDefaultEventBinder(); + options.AddIntegrationEventBinder(); return options; } } diff --git a/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs b/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs index a8fe7fe85f..6931d98bd2 100644 --- a/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs +++ b/src/Sentry.Maui/Internal/MauiButtonEventsBinder.cs @@ -21,6 +21,7 @@ public void Bind(VisualElement element, Action addBreadcrumb) /// public void UnBind(VisualElement element) { + _addBreadcrumbCallback = null; if (element is Button button) { button.Clicked -= OnButtonOnClicked; diff --git a/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs b/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs index 70f50005c3..fb3e14e62e 100644 --- a/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs +++ b/src/Sentry.Maui/Internal/MauiImageButtonEventsBinder.cs @@ -21,6 +21,7 @@ public void Bind(VisualElement element, Action addBreadcrumb) /// public void UnBind(VisualElement element) { + addBreadcrumbCallback = null; if (element is ImageButton image) { image.Clicked -= OnButtonOnClicked; diff --git a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs index 7d71288365..38c51d6281 100644 --- a/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs +++ b/src/Sentry.Maui/SentryMauiAppBuilderExtensions.cs @@ -56,10 +56,16 @@ public static MauiAppBuilder UseSentry(this MauiAppBuilder builder, services.AddSingleton, SentryMauiOptionsSetup>(); services.AddSingleton(); - // Resolve the configured options and register any element event binders from these + // Add default event binders + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + + // Resolve the configured options and register any event binders that have been injected by integrations var options = new SentryMauiOptions(); configureOptions?.Invoke(options); - foreach (var eventBinder in options.DefaultEventBinders) + foreach (var eventBinder in options.IntegrationEventBinders) { eventBinder.Register(services); } diff --git a/src/Sentry.Maui/SentryMauiOptions.cs b/src/Sentry.Maui/SentryMauiOptions.cs index f152744ecf..c27109bebc 100644 --- a/src/Sentry.Maui/SentryMauiOptions.cs +++ b/src/Sentry.Maui/SentryMauiOptions.cs @@ -23,18 +23,14 @@ public SentryMauiOptions() #if !PLATFORM_NEUTRAL CacheDirectoryPath = Microsoft.Maui.Storage.FileSystem.CacheDirectory; #endif - AddDefaultEventBinder(); - AddDefaultEventBinder(); - AddDefaultEventBinder(); - AddDefaultEventBinder(); } - internal List DefaultEventBinders { get; } = []; + internal List IntegrationEventBinders { get; } = []; - internal void AddDefaultEventBinder<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TEventBinder>() + internal void AddIntegrationEventBinder<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TEventBinder>() where TEventBinder : class, IMauiElementEventBinder { - DefaultEventBinders.Add(new MauiElementEventBinderRegistration()); + IntegrationEventBinders.Add(new MauiElementEventBinderRegistration()); } /// diff --git a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj index d6d124e287..15beeb7e92 100644 --- a/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj +++ b/test/Sentry.Maui.Device.TestApp/Sentry.Maui.Device.TestApp.csproj @@ -100,6 +100,9 @@ + diff --git a/test/Sentry.Maui.Device.TestApp/Startup.cs b/test/Sentry.Maui.Device.TestApp/Startup.cs index 9f3e614d9c..7aad2863b9 100644 --- a/test/Sentry.Maui.Device.TestApp/Startup.cs +++ b/test/Sentry.Maui.Device.TestApp/Startup.cs @@ -19,7 +19,7 @@ public static MauiApp CreateMauiApp() typeof(Sentry.Maui.CommunityToolkit.Mvvm.Tests.MauiCommunityToolkitMvvmEventsBinderTests).Assembly, #endif #if ANDROID - typeof(Sentry.Android.AssemblyReader.Tests.AndroidAssemblyReaderTests).Assembly, + typeof(Sentry.Android.AssemblyReader.Tests.AndroidAssemblyReaderTests).Assembly, #endif ]); var appBuilder = MauiApp.CreateBuilder() diff --git a/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs b/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs index 55d1ab909a..e1eb1dd564 100644 --- a/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiOptionsTests.cs @@ -4,45 +4,50 @@ namespace Sentry.Maui.Tests; public class SentryMauiOptionsTests { + private static SentryMauiOptions GetSut() => new() + { + NetworkStatusListener = FakeReliableNetworkStatusListener.Instance + }; + [Fact] public void IncludeTextInBreadcrumbs_Default() { - var options = new SentryMauiOptions(); + var options = GetSut(); Assert.False(options.IncludeTextInBreadcrumbs); } [Fact] public void IncludeTitleInBreadcrumbs_Default() { - var options = new SentryMauiOptions(); + var options = GetSut(); Assert.False(options.IncludeTitleInBreadcrumbs); } [Fact] public void IncludeBackgroundingStateInBreadcrumbs_Default() { - var options = new SentryMauiOptions(); + var options = GetSut(); Assert.False(options.IncludeBackgroundingStateInBreadcrumbs); } [Fact] public void AutoSessionTracking_Default() { - var options = new SentryMauiOptions(); + var options = GetSut(); Assert.True(options.AutoSessionTracking); } [Fact] public void DetectStartupTime_Default() { - var options = new SentryMauiOptions(); + var options = GetSut(); Assert.Equal(StartupTimeDetectionMode.Fast, options.DetectStartupTime); } [Fact] public void CacheDirectoryPath_Default() { - var options = new SentryMauiOptions(); + var options = GetSut(); #if PLATFORM_NEUTRAL Assert.Null(options.CacheDirectoryPath); @@ -58,7 +63,7 @@ public void HandlerStrategy_Default() { // Arrange var expected = Android.LogCatIntegrationType.None; - var options = new SentryMauiOptions(); + var options = GetSut(); // Assert Assert.Equal(expected, options.Android.LogCatIntegration); @@ -69,7 +74,7 @@ public void HandlerStrategy_Set() { // Arrange var expected = Android.LogCatIntegrationType.None; - var options = new SentryMauiOptions(); + var options = GetSut(); // Act options.Android.LogCatIntegration = Android.LogCatIntegrationType.All; @@ -83,7 +88,7 @@ public void HandlerStrategy_Set() public void BeforeCaptureScreenshot_Set() { // Arrange - var options = new SentryMauiOptions(); + var options = GetSut(); options.AttachScreenshot = true; // Act @@ -94,18 +99,16 @@ public void BeforeCaptureScreenshot_Set() // Assert Assert.NotNull(options.BeforeCaptureInternal); - } [Fact] public void BeforeCaptureScreenshot_NotSet() { // Arrange - var options = new SentryMauiOptions(); + var options = GetSut(); options.AttachScreenshot = true; // Assert Assert.Null(options.BeforeCaptureInternal); - } } diff --git a/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs b/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs index 970558eab3..2d915d8503 100644 --- a/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs +++ b/test/Sentry.Maui.Tests/SentryMauiScreenshotTests.cs @@ -53,7 +53,7 @@ public async Task CaptureException_WhenAttachScreenshots_ContainsScreenshotAttac var builder = _fixture.Builder.UseSentry(); // Act - using var app = builder.Build(); + await using var app = builder.Build(); var client = app.Services.GetRequiredService(); var sentryId = client.CaptureException(new Exception()); await client.FlushAsync(); @@ -94,13 +94,11 @@ public async Task CaptureException_RemoveScreenshot_NotContainsScreenshotAttachm )); // Act - using var app = builder.Build(); + await using var app = builder.Build(); var client = app.Services.GetRequiredService(); var sentryId = client.CaptureException(new Exception()); await client.FlushAsync(); - var options = app.Services.GetRequiredService>().Value; - var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); envelope.Should().NotBeNull(); @@ -118,20 +116,16 @@ public async Task CaptureException_BeforeCaptureScreenshot_DisableCaptureAsync() #endif // Arrange - var builder = _fixture.Builder.UseSentry(options => options.SetBeforeScreenshotCapture((e, hint) => - { - return false; - } + var builder = _fixture.Builder.UseSentry(options => options.SetBeforeScreenshotCapture( + (_, _) => false )); // Act - using var app = builder.Build(); + await using var app = builder.Build(); var client = app.Services.GetRequiredService(); var sentryId = client.CaptureException(new Exception()); await client.FlushAsync(); - var options = app.Services.GetRequiredService>().Value; - var envelope = _fixture.Transport.GetSentEnvelopes().FirstOrDefault(e => e.TryGetEventId() == sentryId); envelope.Should().NotBeNull(); @@ -157,10 +151,8 @@ public async Task CaptureException_BeforeCaptureScreenshot_DefaultAsync() #endif // Arrange - var builder = _fixture.Builder.UseSentry(options => options.SetBeforeScreenshotCapture((e, hint) => - { - return true; - } + var builder = _fixture.Builder.UseSentry(options => options.SetBeforeScreenshotCapture( + (_, _) => true )); // Act @@ -184,19 +176,20 @@ public async Task CaptureException_BeforeCaptureScreenshot_DefaultAsync() } else { - envelopeItem.Should().NotBeNull(); + envelopeItem.Should().NotBeNull(); envelopeItem!.TryGetFileName().Should().Be("screenshot.jpg"); } } - [Fact] + [SkippableFact] public async Task CaptureException_AttachScreenshot_Threadsafe() { +#if ANDROID + Skip.If(TestEnvironment.IsGitHubActions, "Flaky in CI on Android"); +#endif + // Arrange - var builder = _fixture.Builder.UseSentry(options => - { - options.AttachScreenshot = true; - }); + var builder = _fixture.Builder.UseSentry(options => options.AttachScreenshot = true); await using var app = builder.Build(); var client = app.Services.GetRequiredService();