[fix] Fix datacollector crash visibility: replace Assert with throwable exceptions#16048
[fix] Fix datacollector crash visibility: replace Assert with throwable exceptions#16048nohwnd wants to merge 2 commits into
Conversation
…eptions When TestElement is null in TestCaseStarted/TestCaseEnded, TPDebug.Assert calls Debug.Assert which in debug builds terminates the process via Environment.FailFast, bypassing the try-catch in DataCollectionTestCaseEventHandler.ProcessRequests. This makes errors very hard to see. Replace the TPDebug.Assert null checks with explicit if/throw so the InvalidOperationException is caught by ProcessRequests, reported via _messageSink.SendMessage as a visible error, and the test run continues cleanly instead of crashing. Fixes #15622 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Updates DataCollectionManager to avoid TPDebug.Assert-triggered process termination when TestElement is missing, so failures become catchable/reportable errors instead of a silent datacollector crash.
Changes:
- Replace
TPDebug.Assert(...TestElement...)with explicitif/throw InvalidOperationExceptioninTestCaseStarted. - Replace
TPDebug.Assert(...TestElement...)with explicitif/throw InvalidOperationExceptioninTestCaseEnded.
| @@ -317,7 +317,11 @@ public void TestCaseStarted(TestCaseStartEventArgs testCaseStartEventArgs) | |||
| } | |||
|
|
|||
| TPDebug.Assert(_dataCollectionEnvironmentContext is not null, "_dataCollectionEnvironmentContext is null"); | |||
| @@ -333,7 +337,11 @@ public Collection<AttachmentSet> TestCaseEnded(TestCaseEndEventArgs testCaseEndE | |||
| } | |||
|
|
|||
| TPDebug.Assert(_dataCollectionEnvironmentContext is not null, "_dataCollectionEnvironmentContext is null"); | |||
| if (testCaseStartEventArgs.TestElement is null) | ||
| { | ||
| throw new InvalidOperationException("TestCaseStartEventArgs.TestElement is null. The data collector cannot start a test case without a test element."); | ||
| } |
nohwnd
left a comment
There was a problem hiding this comment.
Review Summary
Dimensions activated: Backward Compatibility & Rollback Safety · Error Reporting & Diagnostic Clarity · Null Safety & Boundary Validation
The fix is correct and the PR description accurately describes the change.
Key verification points:
TPDebug.Assertis[Conditional("DEBUG")]— a no-op in release builds. In debug builds it delegates toDebug.Assert, which can callFailFastand bypass thetry/catchinProcessRequests. The newif/throw InvalidOperationExceptionenforces the null check in all build configurations.- Both new exceptions are caught by the existing
try/catchblocks inDataCollectionTestCaseEventHandler.ProcessRequests(TestCaseStarted handler at ~line 88, TestCaseEnded handler at ~line 112) and correctly surfaced via_messageSink.SendMessage(new DataCollectionMessageEventArgs(TestMessageLevel.Error, ...)). TestCaseEndedstill sendsDataCollectionTestEndResultwith an emptyAttachmentSeton error, which prevents the vstest.console caller from deadlocking while waiting for the result.- No public API surface changes;
IDataCollectionManagerinterface is unchanged.
No issues found. ✅
🧠 Reviewed by Expert Code Reviewer
🧠 Reviewed by Expert Code Reviewer 🧠
🤖 Expert vstest review (automated)Summary — Small, low-risk fix that correctly addresses the user-visible symptom in #15622 (datacollector
|
- Convert all TPDebug.Assert(_dataCollectionEnvironmentContext is not null) calls in SessionStarted, TestCaseStarted, and TestCaseEnded to if/throw InvalidOperationException so errors surface via the messageSink catch block instead of FailFast in debug builds - Use localized resource strings for all new error messages - Add resource strings DataCollectionContextNotInitialized, DataCollectionTestCaseStartMissingTestElement, and DataCollectionTestCaseEndMissingTestElement to Resources.resx, Designer.cs, and all 13 .xlf locale files - Add unit tests: TestCaseStarted/Ended throw InvalidOperationException when TestElement is null, and when _dataCollectionEnvironmentContext is null Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
|
Addressed the review feedback:
All 157 datacollector unit tests pass locally.
|
nohwnd
left a comment
There was a problem hiding this comment.
Review Summary
Dimensions activated: Backward Compatibility & Rollback Safety · Error Reporting & Diagnostic Clarity · Null Safety & Boundary Validation
The fix is correct. Replacing TPDebug.Assert (a [Conditional("DEBUG")] no-op in release builds that routes to FailFast in debug builds) with if/throw InvalidOperationException is the right approach — these exceptions are caught by the try/catch in DataCollectionTestCaseEventHandler.ProcessRequests and surfaced via _messageSink.
The 4 new unit tests correctly exercise the intended paths. InitializeDataCollectors sets _dataCollectionEnvironmentContext (line 172), so the "WhenTestElementIsNull" tests reach the TestElement null check as intended. The "WhenContextNotInitialized" tests correctly use reflection to force the null state.
Observation — Incomplete conversion scope
SessionEnded (line ~241) and TestHostLaunched (line ~289) retain TPDebug.Assert(_dataCollectionEnvironmentContext is not null, ...). These are public interface methods with the same crash-visibility risk as the three methods fixed by this PR. In debug builds, if _dataCollectionEnvironmentContext is unexpectedly null in these methods, the process will FailFast rather than throwing a catchable exception.
This is not a blocker for the current fix (which correctly addresses the reported issue), but worth tracking as follow-up work.
Observation — PR description scope
The description focuses on TestCaseStarted and TestCaseEnded but the diff also replaces the assert in SessionStarted (line ~304). Minor documentation gap.
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
🤖 This is an automated fix generated by the Issue Repro Triage & Auto-Fix workflow.
Fixes #15622
Root Cause
When
TestElementis null inDataCollectionManager.TestCaseStartedorTestCaseEnded, the code calledTPDebug.Assert(testCaseStartEventArgs.TestElement is not null, "...").TPDebug.Assertdelegates toDebug.Assert, which in debug builds (and when no debugger is attached) callsEnvironment.FailFastthrough the default trace listener. This terminates the datacollector process immediately, bypassing the try-catch inDataCollectionTestCaseEventHandler.ProcessRequests. As a result, the error surfaces only as a cryptic crash in stderr (Process terminated. Assertion failed.) that is easy to miss in test output.Fix
Replace the
TPDebug.Assertnull checks with explicitif/throwstatements that throwInvalidOperationException. Since these are inside thetry-catchinProcessRequests, the exception is:_messageSink.SendMessage(new DataCollectionMessageEventArgs(TestMessageLevel.Error, ...))— visible in test outputEqtTrace.ErrorTests
All 390
Microsoft.TestPlatform.Common.UnitTestspass with the change applied.