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

Test logs get mixed across test cases #2377

Closed
AArnott opened this issue Sep 9, 2021 · 12 comments
Closed

Test logs get mixed across test cases #2377

AArnott opened this issue Sep 9, 2021 · 12 comments

Comments

@AArnott
Copy link
Contributor

AArnott commented Sep 9, 2021

Description

dotnet test (and Test Explorer in VS) both collect messages logged by tests to the wrong tests.
I am using xunit's ITestOutputHelper to log messages, and yet these messages which are meant to be collected under "Standard Output" on a per-test basis are getting mixed up, which makes analyzing test failures very difficult or impossible.

It seems to depend on logging taking place after the test class's DisposeAsync method completes, such that test 1 is actually logging after it allegedly finished, thus overlapping into test 2's time slice.
That said, since concurrent test execution is supported, it seems that logging is already distinguishable between concurrent tests. So why do the logs get mixed up when one test logs after its disposal?

The tested product may have async shutdown behavior that the test's DisposeAsync method cannot wait on, which is why logging happens beyond the DisposeAsync method. But I would like to capture such logs (if the test runner is still running) and associate them with the right test.

We decided this is an xunit bug rather than in the VSTest platform because we found evidence that the corruption happens within Xunit.Sdk.TestOutputHelper, and by the time the xunit.runner.visualstudio library hands off to the VS Test platform with the test result, the corruption has already occurred.
image

Steps to reproduce

Take this minimal repro:
TestProject1.zip

dotnet test -d log.txt -l "trx;LogFileName=console.trx"

Open the console.trx file in VS.
Go through each of the 5 tests to look at their logged messages.
Each message is prefixed with a unique test ID (and elapsed time) to record which test invocation it came from.

Expected behavior

Each of the tests have their own logged messages. This would manifest as each message starting with the same number as every other line.

Actual behavior

I see some test logs include multiple of these unique ID prefixes:
image

It also reproduces when tests are run in Test Explorer:
image

Diagnostic logs

logs.zip

Environment

Windows
.NET SDK 5.0.400

@drvic10k
Copy link

why was this issue closed? was it fixed?

@AArnott
Copy link
Contributor Author

AArnott commented Oct 13, 2022

@drvic10k Why do you say it was closed? It's active now, and the history on the issue doesn't suggest it was ever closed. Perhaps you were confused by the link from a closed issue that appears in this issue's history?

@drvic10k
Copy link

@AArnott you are right, that is indeed a label from the linked issue

@slawomirbrys
Copy link

Hi @AArnott,
Do you know if there is any progress on that bug?
I've just encountered the same bug on my local tests. I tried as much as possible to have concrete logs per my test (that is quite extensive in logging) and now seems that everything is mixed up 🥴. It will be very hard to quickly diagnose issues with tests on CI/CD until someone will run single test on local machine and have proper logs.

@AArnott
Copy link
Contributor Author

AArnott commented Mar 13, 2023

None that I've heard of.

@slawomirbrys
Copy link

Ok, thank you.

@bradwilson
Copy link
Member

This bug comes because we resolve class constructor arguments once per class, so there is a shared instance of the test output helper being used for all tests in the same class:

if (parameter.ParameterType == typeof(ITestOutputHelper))
{
argumentValue = new TestOutputHelper();
return true;
}

It gets emptied and reset between tests:

if (testOutputHelper != null)
testOutputHelper.Initialize(MessageBus, Test);
var executionTime = await InvokeTestMethodAsync(aggregator);
if (testOutputHelper != null)
{
output = testOutputHelper.Output;
testOutputHelper.Uninitialize();
}

By using it outside of its intended scope, you're writing to another test, since we're reusing the instance.

What would you prefer be done here @AArnott?

  • Ignore the output that's done beyond scope
  • Report the attempt to write output beyond scope as a fatal error

@bradwilson
Copy link
Member

I should point out that in v3 this is already fixed and it takes the "ignore" stance.

@bradwilson
Copy link
Member

No dispose messages anywhere (this is your test project, updated to v3)

Recording.2024-06-16.150259.mp4

@bradwilson
Copy link
Member

The "ignore" behavior is available in 2.8.2-pre.15 https://xunit.net/docs/using-ci-builds

To be clear, your call to logger.WriteLine() will now be throwing, but since nobody is observing the task you spun off, the exception will be ignored (and the test will continue to pass):

Recording.2024-06-16.163019.mp4

@AArnott
Copy link
Contributor Author

AArnott commented Jun 17, 2024

Great. Matching behavior between v2 and v3 seems like goodness.

Between silently failing and throwing, I'd prefer throwing so that the problem will usually be discoverable sooner than when we have some other test failure and we have to search the logs only to find that they're incomplete.

@bradwilson
Copy link
Member

I'll add a new issue to make sure all the runners are subscribed to AppDomain.CurrentDomain.UnhandledException.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants