Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add Scope.Clear and Scope.ClearBreadcrumbs methods #2284

Merged
merged 4 commits into from
Apr 6, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Add Scope.Clear and Scope.ClearBreadcrumbs methods ([#2284](https://github.com/getsentry/sentry-dotnet/pull/2284))

### Features

- Add `FileDiagnosticLogger` to assist with debugging the SDK ([#2242](https://github.com/getsentry/sentry-dotnet/pull/2242))
Expand Down
40 changes: 40 additions & 0 deletions src/Sentry/Scope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,11 @@ public User User
/// <inheritdoc />
public IReadOnlyList<string> Fingerprint { get; set; } = Array.Empty<string>();

#if NETSTANDARD2_0 || NETFRAMEWORK
private ConcurrentQueue<Breadcrumb> _breadcrumbs = new();
#else
private readonly ConcurrentQueue<Breadcrumb> _breadcrumbs = new();
#endif

/// <inheritdoc />
public IReadOnlyCollection<Breadcrumb> Breadcrumbs => _breadcrumbs;
Expand Down Expand Up @@ -305,6 +309,28 @@ public void UnsetTag(string key)
/// </summary>
public void AddAttachment(Attachment attachment) => _attachments.Add(attachment);

/// <summary>
/// Resets all the properties and collections within the scope to their default values.
/// </summary>
public void Clear()
{
Level = default;
Request = new();
Contexts.Clear();
User = new();
Platform = default;
Release = default;
Distribution = default;
Environment = default;
TransactionName = default;
Transaction = default;
Fingerprint = Array.Empty<string>();
ClearBreadcrumbs();
_extra.Clear();
_tags.Clear();
ClearAttachments();
}

/// <summary>
/// Clear all Attachments.
/// </summary>
Expand All @@ -317,6 +343,20 @@ public void ClearAttachments()
#endif
}

/// <summary>
/// Removes all Breadcrumbs from the scope.
/// </summary>
public void ClearBreadcrumbs()
{
#if NETSTANDARD2_0 || NETFRAMEWORK
// No Clear method on ConcurrentQueue for these target frameworks
Interlocked.Exchange(ref _breadcrumbs, new());
#else
_breadcrumbs.Clear();
#endif
}


/// <summary>
/// Applies the data from this scope to another event-like object.
/// </summary>
Expand Down
2 changes: 2 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Core3_1.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,9 @@ namespace Sentry
public void Apply(Sentry.IEventLike other) { }
public void Apply(Sentry.Scope other) { }
public void Apply(object state) { }
public void Clear() { }
public void ClearAttachments() { }
public void ClearBreadcrumbs() { }
public Sentry.Scope Clone() { }
public Sentry.ISpan? GetSpan() { }
public void SetExtra(string key, object? value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,9 @@ namespace Sentry
public void Apply(Sentry.IEventLike other) { }
public void Apply(Sentry.Scope other) { }
public void Apply(object state) { }
public void Clear() { }
public void ClearAttachments() { }
public void ClearBreadcrumbs() { }
public Sentry.Scope Clone() { }
public Sentry.ISpan? GetSpan() { }
public void SetExtra(string key, object? value) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,9 @@ namespace Sentry
public void Apply(Sentry.IEventLike other) { }
public void Apply(Sentry.Scope other) { }
public void Apply(object state) { }
public void Clear() { }
public void ClearAttachments() { }
public void ClearBreadcrumbs() { }
public Sentry.Scope Clone() { }
public Sentry.ISpan? GetSpan() { }
public void SetExtra(string key, object? value) { }
Expand Down
2 changes: 2 additions & 0 deletions test/Sentry.Tests/ApiApprovalTests.Run.Net4_8.verified.txt
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,9 @@ namespace Sentry
public void Apply(Sentry.IEventLike other) { }
public void Apply(Sentry.Scope other) { }
public void Apply(object state) { }
public void Clear() { }
public void ClearAttachments() { }
public void ClearBreadcrumbs() { }
public Sentry.Scope Clone() { }
public Sentry.ISpan? GetSpan() { }
public void SetExtra(string key, object? value) { }
Expand Down
40 changes: 40 additions & 0 deletions test/Sentry.Tests/Internals/SentryScopeManagerTests.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using FluentAssertions.Execution;
using Sentry.Internal.ScopeStack;

namespace Sentry.Tests.Internals;
Expand Down Expand Up @@ -206,6 +207,44 @@ public void Scope_DisposedOutOfOrder()
Assert.Equal(root, sut.GetCurrent());
}

[Fact]
public void Scope_Clear_DoesntAffectParentScope()
{
var sut = _fixture.GetSut();

//Set some values on the scope (and store these to compare later)
sut.GetCurrent().Key.ApplyFakeValues();
Scope fakeValues = sut.GetCurrent().Key.Clone();
fakeValues.Transaction.Should().NotBeNull(); // Sanity check... make sure it's not nulls everywhere

//Push a new scope
var root = sut.PushScope();

// Ensure the values are there
using (new AssertionScope())
{
sut.GetCurrent().Key.ShouldBeEquivalentTo(fakeValues);
}

// Clear the child scope
sut.GetCurrent().Key.Clear();

//Ensure the values are gone
using (new AssertionScope())
{
sut.GetCurrent().Key.ShouldBeEquivalentTo(new Scope());
}

//Pop the scope
root.Dispose();

//Ensure the value have returned
using (new AssertionScope())
{
sut.GetCurrent().Key.ShouldBeEquivalentTo(fakeValues);
}
}

[Fact]
public async Task AsyncTasks_IsolatedScopes()
{
Expand Down Expand Up @@ -330,6 +369,7 @@ public void GlobalMode_PushScope_SameScope()
client1.Should().BeSameAs(client2);
}


[Fact]
public void GlobalMode_Disabled_Uses_AsyncLocalScopeStackContainer()
{
Expand Down
74 changes: 74 additions & 0 deletions test/Sentry.Tests/ScopeTests.cs
mattjohnsonpint marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using FluentAssertions.Execution;
namespace Sentry.Tests;

public class ScopeTests
Expand Down Expand Up @@ -232,6 +233,22 @@ public void AddAttachment_AddAttachments()
scope.Attachments.Should().Contain(attachment2, "Attachment2 was not found.");
}

[Fact]
public void Clear_SetsPropertiesToDefaultValues()
{
// Arrange
mattjohnsonpint marked this conversation as resolved.
Show resolved Hide resolved
_sut.ApplyFakeValues();

// Act
_sut.Clear();

// Assert
using (new AssertionScope())
jamescrosswell marked this conversation as resolved.
Show resolved Hide resolved
{
_sut.ShouldBeEquivalentTo(new Scope());
}
}

[Fact]
public void ClearAttachments_HasAttachments_EmptyList()
{
Expand All @@ -250,6 +267,23 @@ public void ClearAttachments_HasAttachments_EmptyList()
scope.Attachments.Should().BeEmpty();
}

[Fact]
public void ClearBreadcrumbs_Breadcrumbs_EmptyList()
{
// Arrange
for (var i = 0; i < 5; i++)
{
_sut.AddBreadcrumb(new Breadcrumb());
}
_sut.Breadcrumbs.Should().NotBeEmpty("Sanity check: Arrange failed to configure Breadcrumbs");

// Act
_sut.ClearBreadcrumbs();

// Assert
_sut.Breadcrumbs.Should().BeEmpty();
}

[Theory]
[InlineData(0, -2, 0)]
[InlineData(0, -1, 0)]
Expand Down Expand Up @@ -429,3 +463,43 @@ public void AddBreadcrumb_ObserverExist_ObserverAddsBreadcrumbIfEnabled(bool obs
observer.Received(expectedCount).AddBreadcrumb(Arg.Is(breadcrumb));
}
}

public static class ScopeTestExtensions
{
public static void ApplyFakeValues(this Scope scope, string salt = "fake")
{
scope.Request = new() { Data = $"{salt} request" };
scope.Contexts.Add($"{salt} context", "{}");
scope.User = new User() { Username = $"{salt} username" };
scope.Platform = $"{salt} platform";
scope.Release = $"{salt} release";
scope.Distribution = $"{salt} distribution";
scope.Environment = $"{salt} environment";
scope.TransactionName = $"{salt} transaction";
scope.Transaction = Substitute.For<ITransaction>();
scope.Fingerprint = new[] { $"{salt} fingerprint" };
scope.AddBreadcrumb(new(message: $"{salt} breadcrumb"));
scope.SetExtra("extra", $"{salt} extra");
scope.SetTag("tag", $"{salt} tag");
scope.AddAttachment(new Attachment(default, default, default, $"{salt} attachment"));
}

public static void ShouldBeEquivalentTo(this Scope source, Scope target)
{
source.Level.Should().Be(target.Level);
source.Request.Should().BeEquivalentTo(target.Request);
source.Contexts.Should().BeEquivalentTo(target.Contexts);
source.User.Should().BeEquivalentTo(target.User);
source.Platform.Should().Be(target.Platform);
source.Release.Should().Be(target.Release);
source.Distribution.Should().Be(target.Distribution);
source.Environment.Should().Be(target.Environment);
source.TransactionName.Should().Be(target.TransactionName);
source.Transaction.Should().Be(target.Transaction);
source.Fingerprint.Should().BeEquivalentTo(target.Fingerprint);
source.Breadcrumbs.Should().BeEquivalentTo(target.Breadcrumbs);
source.Extra.Should().BeEquivalentTo(target.Extra);
source.Tags.Should().BeEquivalentTo(target.Tags);
source.Attachments.Should().BeEquivalentTo(target.Attachments);
}
}