Skip to content

Commit

Permalink
Attempt to fix race condition reported in visualstudio.xunit/issues#396
Browse files Browse the repository at this point in the history
  • Loading branch information
bradwilson committed Dec 18, 2023
1 parent 348c56d commit 0eb76d2
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 3 deletions.
16 changes: 13 additions & 3 deletions src/xunit.runner.utility/Sinks/ExecutionSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class ExecutionSink : LongLivedMarshalByRefObject, IExecutionSink
DateTime lastTestActivity;
volatile int skipCount;
ManualResetEvent stopEvent;
bool stopRequested;

#if !NET35
readonly XElement errorsElement;
Expand Down Expand Up @@ -226,8 +227,7 @@ void HandleTestAssemblyFinished(MessageHandlerArgs<ITestAssemblyFinished> args)
}
#endif

Finished.Set();
stopEvent?.Set();
stopRequested = true;
}

void HandleTestAssemblyStarting(MessageHandlerArgs<ITestAssemblyStarting> args)
Expand Down Expand Up @@ -441,10 +441,20 @@ public bool OnMessageWithTypes(IMessageSinkMessage message, HashSet<string> mess
#endif

// Dispatch to the reporter handler
return
result =
innerSink.OnMessageWithTypes(message, messageTypes)
&& result
&& (options.CancelThunk == null || !options.CancelThunk());

// Don't request stop until after the inner handler has had a chance to process the message
// per https://github.com/xunit/visualstudio.xunit/issues/396
if (stopRequested)
{
Finished.Set();
stopEvent?.Set();
}

return result;
}

void SendLongRunningMessage()
Expand Down
28 changes: 28 additions & 0 deletions test/test.xunit.runner.utility/Sinks/ExecutionSinkTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,34 @@ public bool OnMessage(IMessageSinkMessage message)
}
}

public class Messages
{
public class TestAssemblyFinished
{
[Fact]
public void EnsureInnerHandlerIsCalledBeforeFinishedIsSet()
{
var innerSink = Substitute.For<IMessageSinkWithTypes>();
var sink = new ExecutionSink(innerSink, new ExecutionSinkOptions());
bool? isFinishedDuringDispatch = default;
innerSink
.OnMessageWithTypes(Arg.Any<IMessageSinkMessage>(), Arg.Any<HashSet<string>>())
.Returns(callInfo =>
{
if (callInfo.Arg<IMessageSinkMessage>() is ITestAssemblyFinished)
isFinishedDuringDispatch = sink.Finished.WaitOne(0);
return true;
});

sink.OnMessageWithTypes(Substitute.For<ITestAssemblyFinished>(), null);
var isFinishedAfterDispatch = sink.Finished.WaitOne(0);

Assert.False(isFinishedDuringDispatch);
Assert.True(isFinishedAfterDispatch);
}
}
}

public class XmlCreation
{
readonly IMessageSinkWithTypes innerSink;
Expand Down

0 comments on commit 0eb76d2

Please sign in to comment.