Skip to content

Fix flaky DrainDataAsync_Loop_ShouldFail (unblocks PR #9355 CI) - #9366

Merged
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
evangelink-fix-pr-9355-build
Jun 23, 2026
Merged

Fix flaky DrainDataAsync_Loop_ShouldFail (unblocks PR #9355 CI)#9366
Amaury Levé (Evangelink) merged 1 commit into
mainfrom
evangelink-fix-pr-9355-build

Conversation

@Evangelink

Copy link
Copy Markdown
Member

Problem

The CI build for #9355 failed on the Build Windows Debug leg, but the failure is unrelated to that PR's changes (its analyzer tests all pass). The failing test is:

failed DrainDataAsync_Loop_ShouldFail (179ms)
Assertion failed. Expected exception of exact type InvalidOperationException but no exception was thrown.
from Microsoft.Testing.Platform.UnitTests.exe (net48|x64)

This is a known, intermittently-flaky test (historically tracked by #6892) and only fails on net462 under heavy CI load.

Root cause

DrainDataAsync_Loop_ShouldFail sets up two consumers that produce messages each other reacts to, creating a publisher/consumer loop. Detection relies on the background consumer tasks (started via ITask.Run) ping-ponging messages so that AsynchronousMessageBus.DrainDataAsync keeps observing newly received payloads across its drain rounds and ultimately throws.

On .NET Framework, under the assembly's method-level parallelism (32 workers competing for the slowly-growing ThreadPool), those background tasks can be starved long enough that the drain completes its attempts without observing the loop — so no exception is thrown and the assertion fails. The drain/marker logic itself is deterministic; the flakiness comes purely from ThreadPool contention with the other concurrently-running test methods.

Fix

Mark the single test [DoNotParallelize] (the same pattern already used by TerminalOutputDeviceTests) so it runs without competing for threads, keeping loop detection deterministic. This is a test-only change with no production impact.

Verified locally on net462/net8.0/net9.0: the project builds clean and all 5 AsynchronousMessageBusTests pass.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

The test depends on background consumer tasks ping-ponging messages so the drain detects the publisher/consumer loop. On .NET Framework under the assembly's method-level parallelism, those tasks can be starved by ThreadPool contention long enough that the loop isn't observed, causing intermittent 'no exception was thrown' failures (e.g. PR #9355's Build Windows Debug leg). Marking the test [DoNotParallelize] removes that contention and keeps loop detection deterministic.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
CopilotAI review requested due to automatic review settings June 23, 2026 08:26

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses intermittent flakiness in the Microsoft.Testing.Platform unit test suite on .NET Framework by ensuring a specific loop-detection test does not compete with other test methods for ThreadPool resources under method-level parallel execution.

Changes:

  • Added an explanatory comment documenting why DrainDataAsync_Loop_ShouldFail can flake under .NET Framework ThreadPool contention.
  • Marked DrainDataAsync_Loop_ShouldFail with [DoNotParallelize] to eliminate cross-test contention and stabilize loop detection.
Show a summary per file
FileDescription
test/UnitTests/Microsoft.Testing.Platform.UnitTests/Messages/AsynchronousMessageBusTests.csDocuments the known flake cause and opts the single loop-detection test out of method-level parallel execution via [DoNotParallelize].

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 0

@EvangelinkAmaury Levé (Evangelink) added the state/needs-review Awaiting review from the team. label Jun 23, 2026

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note

🤖 Automated review by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Expert Code Review workflow. To request a follow-up action, reply by tagging @copilot directly.

Expert Review — PR #9366 · Fix flaky DrainDataAsync_Loop_ShouldFail

Reviewed against all 22 dimensions. Changed file lives under test/UnitTests/ → priority dimensions: Test Isolation, Assertion Quality, Flakiness Patterns, Threading & Concurrency, Code Structure.


Verdict table

#DimensionResultNotes
1Algorithmic Correctness✅ LGTMFix addresses root cause (ThreadPool starvation), not a symptom
2Threading & Concurrency✅ LGTMChange itself introduces no new shared-state; see observation below
3Security & IPC Contract Safety➖ N/ATest-only change
4Public API & Binary Compatibility➖ N/ANo public surface touched
5Performance & Allocations➖ N/ATest code
6Cross-TFM Compatibility✅ LGTM[DoNotParallelize] available on all supported MSTest TFMs; fix specifically targets the net462/net48 regression path
7Resource & IDisposable Management✅ LGTMPre-existing (no using on asynchronousMessageBus in the loop test); not introduced here
8Defensive Coding at Boundaries➖ N/A
9Localization & Resources➖ N/A
10Test Isolation✅ LGTMMethod-level [DoNotParallelize] is the correct, minimal scope — more precise than the class-level usage in TerminalOutputDeviceTests; isolates only this test
11Assertion Quality✅ LGTMAssert.ThrowsExactlyAsync<InvalidOperationException> + Assert.Contains on message content are appropriate; MSTest Assert family is the correct library for this project
12Flakiness Patterns✅ LGTMPR removes the flakiness trigger; no Task.Delay/Thread.Sleep-for-sync patterns introduced
13Test Completeness & Coverage✅ LGTMFix preserves existing test intent; no new branches to cover
14Data-Driven Test Coverage➖ N/A
15Code Structure & Simplification✅ LGTMChange is minimal and coherent
16Naming & Conventions✅ LGTM
17Documentation Accuracy✅ LGTMComment explains why (not just what), references tracking issue #6892 — exactly the right level of detail
18Analyzer & Code Fix Quality➖ N/A
19IPC Wire Compatibility➖ N/A
20Build Infrastructure & Dependencies➖ N/A
21Scope & PR Discipline✅ LGTMSingle concern, references #6892 and blocked PR #9355, no unrelated changes
22PowerShell Scripting Hygiene➖ N/A

Clean dimensions: 22/22 (12 applicable, all LGTM; 10 N/A)


Pre-existing observation (not introduced by this PR, no action required)

LoopConsumerA._stopConsume and ConsumerB._stopConsume are plain bool fields written by the main test thread (StopConsume()) and read by background ITask.Run tasks (ConsumeAsync). Per dimension §2, cross-thread fields without volatile/Interlocked/lock are technically a data race. In this specific test the write happens only after the InvalidOperationException has already been caught (so the race cannot cause the assertion to fail), making it inconsequential in practice. Worth a volatile annotation in a future cleanup, but does not affect correctness here and is out of scope for this PR.


Overall: The fix is correct, minimal, and well-justified. The approach exactly mirrors the TerminalOutputDeviceTests precedent and directly eliminates the starvation condition that triggered the intermittent failure. Ready to merge.

@Evangelink

Copy link
Copy Markdown
MemberAuthor

🧪 Test quality grade — PR #9366

ΔTestGradeBandNotes
modAsynchronousMessageBusTests.
DrainDataAsync_
Loop_
ShouldFail
A90–100Exact exception type + message content assertions; clear AAA structure; [DoNotParallelize] addition is well-motivated by flakiness concern.

This advisory comment was generated automatically. Grades are heuristic
and informational — they do not block merging. Re-run with
/grade-tests.

🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Grade Tests on PR (on open / sync) workflow. · 162.4 AIC · ⌖ 13.7 AIC · ⊞ 43.7K · [◷]( · )

@Evangelink
Amaury Levé (Evangelink) merged commit 0e5e176 into mainJun 23, 2026
47 checks passed
@Evangelink
Amaury Levé (Evangelink) deleted the evangelink-fix-pr-9355-build branch June 23, 2026 09:21
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

state/needs-reviewAwaiting review from the team.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@Evangelink@0101