Skip to content

Commit

Permalink
Fix bug where Identifier recording can overlap between tests (#1254)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonCropp authored Jul 25, 2024
1 parent 57ac11f commit 801841b
Show file tree
Hide file tree
Showing 6 changed files with 70 additions and 20 deletions.
8 changes: 4 additions & 4 deletions docs/recording.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public Task SameKey()
return Verify("TheValue");
}
```
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L281-L292' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingSameKey' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L288-L299' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingSameKey' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Results in:
Expand Down Expand Up @@ -186,7 +186,7 @@ public Task Case()
return Verify("TheValue");
}
```
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L303-L314' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingIgnoreCase' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L310-L321' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingIgnoreCase' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Results in:
Expand Down Expand Up @@ -305,7 +305,7 @@ public Task Clear()
return Verify();
}
```
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L201-L213' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingClear' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L208-L220' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingClear' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Results in:
Expand Down Expand Up @@ -341,7 +341,7 @@ public Task PauseResume()
return Verify();
}
```
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L225-L240' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingPauseResume' title='Start of snippet'>anchor</a></sup>
<sup><a href='/src/Verify.Tests/RecordingTests.cs#L232-L247' title='Snippet source file'>snippet source</a> | <a href='#snippet-RecordingPauseResume' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Results in:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[
{
name: value
}
]
15 changes: 11 additions & 4 deletions src/Verify.Tests/RecordingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,18 @@ public Task StopNotInResult()
#endregion

[Fact]
public Task StopIdentifier()
public void StartIdentifierThatDoesntFinish()
{
Recording.Start("identifier");
Recording.Add("identifier", "name", "value");
return Verify(Recording.Stop("identifier"));
Recording.Start("identifierOverlap");
Recording.Add("identifierOverlap", "name", "value");
}

[Fact]
public Task StopIdentifierOverlap()
{
Recording.Start("identifierOverlap");
Recording.Add("identifierOverlap", "name", "value");
return Verify(Recording.Stop("identifierOverlap"));
}

[Fact]
Expand Down
60 changes: 48 additions & 12 deletions src/Verify/Recording/Recording_Named.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,33 @@

public static partial class Recording
{
static ConcurrentDictionary<string, State> namedState = new(StringComparer.OrdinalIgnoreCase);
static AsyncLocal<ConcurrentDictionary<string, State>?> asyncLocalNamed = new();

public static void Add(string identifier, string name, object item) =>
CurrentStateNamed(identifier)
.Add(name, item);

public static void TryAdd(string identifier, string name, object item)
{
if (namedState.TryGetValue(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states != null)
{
state.Add(name, item);
if (states.TryGetValue(identifier, out var state))
{
state.Add(name, item);
}
}
}

public static bool IsRecording(string identifier)
{
if (!namedState.TryGetValue(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states == null)
{
return false;
}

if (!states.TryGetValue(identifier, out var state))
{
return false;
}
Expand All @@ -40,10 +50,14 @@ public static bool TryStop(
string identifier,
[NotNullWhen(true)] out IReadOnlyCollection<ToAppend>? recorded)
{
if (namedState.TryRemove(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states != null)
{
recorded = state.Items;
return true;
if (states.TryRemove(identifier, out var state))
{
recorded = state.Items;
return true;
}
}

recorded = null;
Expand All @@ -52,7 +66,9 @@ public static bool TryStop(

static State CurrentStateNamed(string identifier, [CallerMemberName] string caller = "")
{
if (namedState.TryGetValue(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states != null &&
states.TryGetValue(identifier, out var state))
{
return state;
}
Expand All @@ -62,7 +78,9 @@ static State CurrentStateNamed(string identifier, [CallerMemberName] string call

public static IDisposable Start(string identifier)
{
if (!namedState.TryAdd(identifier, new()))
var states = asyncLocalNamed.Value ??= new(StringComparer.OrdinalIgnoreCase);

if (!states.TryAdd(identifier, new()))
{
throw new("Recording already started");
}
Expand All @@ -83,7 +101,13 @@ public static void Pause(string identifier) =>

public static void TryPause(string identifier)
{
if (namedState.TryGetValue(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states == null)
{
return;
}

if (states.TryGetValue(identifier, out var state))
{
state.Pause();
}
Expand All @@ -95,7 +119,13 @@ public static void Resume(string identifier) =>

public static void TryResume(string identifier)
{
if (namedState.TryGetValue(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states == null)
{
return;
}

if (states.TryGetValue(identifier, out var state))
{
state.Resume();
}
Expand All @@ -107,7 +137,13 @@ public static void Clear(string identifier) =>

public static void TryClear(string identifier)
{
if (namedState.TryGetValue(identifier, out var state))
var states = asyncLocalNamed.Value;
if (states == null)
{
return;
}

if (states.TryGetValue(identifier, out var state))
{
state.Clear();
}
Expand Down

0 comments on commit 801841b

Please sign in to comment.