Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions test/Sentry.AspNetCore.TestUtils/SentrySdkTestFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ protected virtual void ConfigureBuilder(WebHostBuilder builder)

public void Dispose()
{
HttpClient?.Dispose();
TestServer?.Dispose();
SentrySdk.Close();
}
}
191 changes: 191 additions & 0 deletions test/Sentry.AspNetCore.Tests/IntegrationsTests.BlockingDetection.cs
Comment thread
z0rimo marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Sentry.AspNetCore.TestUtils;
using Sentry.Ben.BlockingDetector;

namespace Sentry.AspNetCore.Tests;

public partial class IntegrationsTests
{
private const string BlockingCallDetectorMechanism = "BlockingCallDetector";

[Fact]
public async Task InvokeAsync_CaptureBlockingCallsEnabled_ReusesListenerAcrossRequests()
{
var middlewareInstances = new ConcurrentQueue<SentryMiddleware>();

Configure = options => options.CaptureBlockingCalls = true;
AfterConfigureBuilder = builder => builder.ConfigureServices(services =>
{
services.RemoveAll<SentryMiddleware>();
services.AddTransient(serviceProvider =>
{
var middleware = ActivatorUtilities.CreateInstance<SentryMiddleware>(serviceProvider);
middlewareInstances.Enqueue(middleware);
return middleware;
});
});

Build();

_ = await HttpClient.GetAsync("/");
_ = await HttpClient.GetAsync("/");

var instances = middlewareInstances.ToArray();
Assert.Equal(2, instances.Length);
Assert.NotSame(instances[0], instances[1]);
Assert.NotNull(instances[0].Monitor);
Assert.NotNull(instances[0].Listener);
Assert.Same(instances[0].Monitor, instances[1].Monitor);
Assert.Same(instances[0].Listener, instances[1].Listener);
Assert.Same(ServiceProvider.GetRequiredService<IBlockingMonitor>(), instances[0].Monitor);
Assert.Same(ServiceProvider.GetRequiredService<TaskBlockingListener>(), instances[0].Listener);
}

[Fact]
public async Task InvokeAsync_BlockingCallDetectionEnabled_CapturesBlockingCallEvent()
{
var events = new ConcurrentQueue<SentryEvent>();
Configure = options =>
{
options.CaptureBlockingCalls = true;
options.SetBeforeSend(@event =>
{
events.Enqueue(@event);
return @event;
});
};
Handlers =
[
new RequestHandler
{
Path = "/blocking",
Handler = _ =>
{
Task.Delay(25).Wait();
return Task.CompletedTask;
}
}
];

Build();

_ = await HttpClient.GetAsync("/blocking");

var blockingEvent = Assert.Single(events, IsBlockingCallDetectorEvent);
var mechanism = blockingEvent.SentryExceptions!.Single(exception => exception.Mechanism?.Type == BlockingCallDetectorMechanism).Mechanism;
Assert.Equal("EventListener", mechanism!.Source);
}

[Fact]
public async Task InvokeAsync_BlockingCallDetectionEnabled_SynchronizationContextWait_CapturesBlockingCallEvent()
{
var events = new ConcurrentQueue<SentryEvent>();
Configure = options =>
{
options.CaptureBlockingCalls = true;
options.SetBeforeSend(@event =>
{
events.Enqueue(@event);
return @event;
});
};
Handlers =
[
new RequestHandler
{
Path = "/blocking",
Handler = _ =>
{
using var manualResetEvent = new ManualResetEvent(false);
manualResetEvent.WaitOne(25);
return Task.CompletedTask;
}
}
];

Build();

_ = await HttpClient.GetAsync("/blocking");

var blockingEvent = Assert.Single(events, IsBlockingCallDetectorEvent);
var mechanism = blockingEvent.SentryExceptions!.Single(exception => exception.Mechanism?.Type == BlockingCallDetectorMechanism).Mechanism;
Assert.Equal("SynchronizationContext", mechanism!.Source);
}

[Fact]
public async Task InvokeAsync_BlockingCallDetectionDisabled_DoesNotCaptureOrInstantiateDetector()
{
var events = new ConcurrentQueue<SentryEvent>();
var middlewareCount = 0;
var monitorCount = 0;
var listenerCount = 0;
Configure = options =>
{
options.CaptureBlockingCalls = false;
options.SetBeforeSend(@event =>
{
events.Enqueue(@event);
return @event;
});
};
ConfigureCountingBlockingDetectionServices(
() => Interlocked.Increment(ref middlewareCount),
() => Interlocked.Increment(ref monitorCount),
() => Interlocked.Increment(ref listenerCount));
Handlers =
[
new RequestHandler
{
Path = "/blocking",
Handler = _ =>
{
Task.Delay(25).Wait();
return Task.CompletedTask;
}
}
];

Build();

_ = await HttpClient.GetAsync("/blocking");

Assert.Equal(1, middlewareCount);
Assert.Equal(0, monitorCount);
Assert.Equal(0, listenerCount);
Assert.DoesNotContain(events, IsBlockingCallDetectorEvent);
}

private void ConfigureCountingBlockingDetectionServices(
Action middlewareCreated,
Action monitorCreated,
Action listenerCreated)
{
AfterConfigureBuilder = builder => builder.ConfigureServices(services =>
{
services.RemoveAll<SentryMiddleware>();
services.AddTransient(serviceProvider =>
{
middlewareCreated();
return ActivatorUtilities.CreateInstance<SentryMiddleware>(serviceProvider);
});

services.RemoveAll<IBlockingMonitor>();
services.AddSingleton<IBlockingMonitor>(serviceProvider =>
{
monitorCreated();
return ActivatorUtilities.CreateInstance<BlockingMonitor>(serviceProvider);
});

services.RemoveAll<TaskBlockingListener>();
services.AddSingleton(serviceProvider =>
{
listenerCreated();
return ActivatorUtilities.CreateInstance<TaskBlockingListener>(serviceProvider);
});
});
}

private static bool IsBlockingCallDetectorEvent(SentryEvent @event) =>
@event.SentryExceptions?.Any(exception => exception.Mechanism?.Type == BlockingCallDetectorMechanism) == true;
}
3 changes: 3 additions & 0 deletions test/Sentry.AspNetCore.Tests/Sentry.AspNetCore.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
<Compile Update="IntegrationsTests.EventProcessor.cs">
<DependentUpon>IntegrationsTests.cs</DependentUpon>
</Compile>
<Compile Update="IntegrationsTests.BlockingDetection.cs">
<DependentUpon>IntegrationsTests.cs</DependentUpon>
</Compile>
</ItemGroup>

</Project>
Loading