From a15322327245b6bec3a121e3ccef41fa8f71cd62 Mon Sep 17 00:00:00 2001 From: Jan-Willem de Bruyn Date: Wed, 12 Jun 2024 16:55:41 +0200 Subject: [PATCH] Change Queue to ConcurrentQueue --- .../MockHttpMessageHandlerTests.cs | 26 +++++++++++++++++++ .../MockHttpMessageHandler.cs | 26 ++++++++++++++++--- 2 files changed, 48 insertions(+), 4 deletions(-) diff --git a/RichardSzalay.MockHttp.Tests/MockHttpMessageHandlerTests.cs b/RichardSzalay.MockHttp.Tests/MockHttpMessageHandlerTests.cs index 5aa504c..59afae7 100644 --- a/RichardSzalay.MockHttp.Tests/MockHttpMessageHandlerTests.cs +++ b/RichardSzalay.MockHttp.Tests/MockHttpMessageHandlerTests.cs @@ -1,6 +1,9 @@ using System; +using System.Collections.Concurrent; +using System.Linq; using System.Net; using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -232,6 +235,29 @@ public void Should_not_match_expect_out_of_order() Assert.Equal(HttpStatusCode.NotFound, result.StatusCode); } + [Fact] + public async Task Should_work_with_multiple_concurrent_writes_to_queue() + { + var mockHandler = new MockHttpMessageHandler(); + var tasks = new ConcurrentQueue(); + var readyEvent = new ManualResetEvent(false); + + for (int i = 0; i < 100; i++) + { + tasks.Enqueue(Task.Run(async () => + { + await Task.Yield(); + readyEvent.WaitOne(); + mockHandler + .Expect("/test") + .Respond("application/json", "{'status' : 'First'}"); + })); + } + readyEvent.Set(); + + await Task.WhenAll(tasks).ConfigureAwait(false); + } + [Fact] public void VerifyNoOutstandingExpectation_should_fail_if_outstanding_expectation() { diff --git a/RichardSzalay.MockHttp/MockHttpMessageHandler.cs b/RichardSzalay.MockHttp/MockHttpMessageHandler.cs index 20885c7..bf1e1cd 100644 --- a/RichardSzalay.MockHttp/MockHttpMessageHandler.cs +++ b/RichardSzalay.MockHttp/MockHttpMessageHandler.cs @@ -1,5 +1,6 @@ using RichardSzalay.MockHttp.Matchers; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Net.Http; @@ -13,7 +14,7 @@ namespace RichardSzalay.MockHttp; /// public class MockHttpMessageHandler : HttpMessageHandler { - private Queue requestExpectations = new(); + private ConcurrentQueue requestExpectations = new(); private List backendDefinitions = new(); private Dictionary matchCounts = new(); private object lockObject = new(); @@ -108,15 +109,25 @@ private IMockedRequest FindHandler(HttpRequestMessage request) if (requestExpectations.Count > 0) { - var handler = requestExpectations.Peek(); - var handlerResult = EvaluateMockedRequest(handler, request); + var peeked = requestExpectations.TryPeek(out var peekedHandler); + if (!peeked) + { + throw new InvalidOperationException("Concurrent read/write on mock queue"); + } + + var handlerResult = EvaluateMockedRequest(peekedHandler!, request); results.RequestExpectationResult = handlerResult; results.UnevaluatedRequestExpectations.AddRange(requestExpectations.Skip(1)); if (handlerResult.Success) { - requestExpectations.Dequeue(); + requestExpectations.TryDequeue(out var handler); + + if (peekedHandler != handler) + { + throw new Exception("Queue order changed between evaluation and result"); + } results.Handler = handler; } @@ -366,7 +377,14 @@ public void VerifyNoOutstandingExpectation() /// public void ResetExpectations() { +#if NET5_0_OR_GREATER requestExpectations.Clear(); +#else + while (!requestExpectations.IsEmpty) + { + requestExpectations.TryDequeue(out IMockedRequest _); + } +#endif } ///