diff --git a/docs/recording.md b/docs/recording.md index d0b953faaf..5c6f0c90ad 100644 --- a/docs/recording.md +++ b/docs/recording.md @@ -117,7 +117,7 @@ public Task SameKey() return Verify("TheValue"); } ``` -snippet source | anchor +snippet source | anchor Results in: @@ -186,7 +186,7 @@ public Task Case() return Verify("TheValue"); } ``` -snippet source | anchor +snippet source | anchor Results in: @@ -305,7 +305,7 @@ public Task Clear() return Verify(); } ``` -snippet source | anchor +snippet source | anchor Results in: @@ -341,7 +341,7 @@ public Task PauseResume() return Verify(); } ``` -snippet source | anchor +snippet source | anchor Results in: diff --git a/src/Verify.Tests/RecordingTests.StartIdentifier1.verified.txt b/src/Verify.Tests/RecordingTests.StartIdentifier1.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/Verify.Tests/RecordingTests.StartIdentifier1.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Verify.Tests/RecordingTests.StopIdentifier2.verified.txt b/src/Verify.Tests/RecordingTests.StopIdentifier2.verified.txt new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/src/Verify.Tests/RecordingTests.StopIdentifier2.verified.txt @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/Verify.Tests/RecordingTests.StopIdentifierOverlap.verified.txt b/src/Verify.Tests/RecordingTests.StopIdentifierOverlap.verified.txt new file mode 100644 index 0000000000..d09ad9b9f3 --- /dev/null +++ b/src/Verify.Tests/RecordingTests.StopIdentifierOverlap.verified.txt @@ -0,0 +1,5 @@ +[ + { + name: value + } +] \ No newline at end of file diff --git a/src/Verify.Tests/RecordingTests.cs b/src/Verify.Tests/RecordingTests.cs index 0d70109f38..d854cf5879 100644 --- a/src/Verify.Tests/RecordingTests.cs +++ b/src/Verify.Tests/RecordingTests.cs @@ -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] diff --git a/src/Verify/Recording/Recording_Named.cs b/src/Verify/Recording/Recording_Named.cs index a9b436e66f..9575bacd62 100644 --- a/src/Verify/Recording/Recording_Named.cs +++ b/src/Verify/Recording/Recording_Named.cs @@ -2,7 +2,7 @@ public static partial class Recording { - static ConcurrentDictionary namedState = new(StringComparer.OrdinalIgnoreCase); + static AsyncLocal?> asyncLocalNamed = new(); public static void Add(string identifier, string name, object item) => CurrentStateNamed(identifier) @@ -10,15 +10,25 @@ public static void Add(string identifier, string name, object 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; } @@ -40,10 +50,14 @@ public static bool TryStop( string identifier, [NotNullWhen(true)] out IReadOnlyCollection? 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; @@ -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; } @@ -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"); } @@ -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(); } @@ -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(); } @@ -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(); }