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

Stop failing when tests have straggling tasks #126

Merged
merged 1 commit into from
Feb 6, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions src/Xunit.StaFact/Sdk/UISynchronizationContext.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Andrew Arnott. All rights reserved.
// Licensed under the Ms-PL license. See LICENSE file in the project root for full license information.

using System.Diagnostics;
using System.Runtime.ExceptionServices;

namespace Xunit.Sdk;
Expand Down Expand Up @@ -67,10 +66,12 @@ public void PumpMessages(Task untilCompleted)
},
TaskScheduler.Default);

// Now run the message loop until the task completes.
while (!untilCompleted.IsCompleted)
// Now run the message loop until the task completes
// and drain all messages from the queue.
bool empty = false;
while (!untilCompleted.IsCompleted || !empty)
{
this.TryOneWorkItem();
empty = !this.TryOneWorkItem(untilCompleted.IsCompleted);
}
}
finally
Expand Down Expand Up @@ -115,9 +116,20 @@ public override void Post(SendOrPostCallback d, object? state)
{
if (this.pumpingEnded)
{
// Include the stack trace in the exception message itself because in test runs,
// the only thing reported for diagnosing this failure is the exception message.
throw new InvalidOperationException($"The message pump in '{this.name}' isn't running any more. {new StackTrace()}");
if (Environment.GetEnvironmentVariable("XUNIT_STAFACT_STRICT") == "1")
{
// Fail fast if the message pump has already ended.
// This can be a maddeningly difficult bug to diagnose in tests otherwise.
// We really need a dump of the test process to diagnose this so we can see all the threads
// at the moment this happened.
Environment.FailFast($"The message pump in '{this.name}' isn't running any more.");
}

// The message pump isn't running any more. The test is over.
// Anyone requesting the main thread hopefully doesn't matter to the test.
// We'll run the delegate anyways, just on the threadpool.
Task.Run(() => d(state));
return;
}

lock (this.messageQueue)
Expand Down Expand Up @@ -186,14 +198,18 @@ private void VerifyState()
}
}

private bool TryOneWorkItem()
private bool TryOneWorkItem(bool doNotWait)
{
KeyValuePair<SendOrPostCallback, object?> work = default;
lock (this.messageQueue)
{
if (this.messageQueue.Count == 0)
{
Monitor.Wait(this.messageQueue);
if (!doNotWait)
{
Monitor.Wait(this.messageQueue);
}

return false;
}

Expand Down