Skip to content

[fix] Fix datacollector crash visibility: replace Assert with throwable exceptions#16048

Open
nohwnd wants to merge 1 commit into
mainfrom
fix/issue-15622-c6839125a22aaa9f
Open

[fix] Fix datacollector crash visibility: replace Assert with throwable exceptions#16048
nohwnd wants to merge 1 commit into
mainfrom
fix/issue-15622-c6839125a22aaa9f

Conversation

@nohwnd
Copy link
Copy Markdown
Member

@nohwnd nohwnd commented May 20, 2026

🤖 This is an automated fix generated by the Issue Repro Triage & Auto-Fix workflow.

Fixes #15622

Root Cause

When TestElement is null in DataCollectionManager.TestCaseStarted or TestCaseEnded, the code called TPDebug.Assert(testCaseStartEventArgs.TestElement is not null, "...").

TPDebug.Assert delegates to Debug.Assert, which in debug builds (and when no debugger is attached) calls Environment.FailFast through the default trace listener. This terminates the datacollector process immediately, bypassing the try-catch in DataCollectionTestCaseEventHandler.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.Assert null checks with explicit if/throw statements that throw InvalidOperationException. Since these are inside the try-catch in ProcessRequests, the exception is:

  1. Caught by the exception handler
  2. Reported via _messageSink.SendMessage(new DataCollectionMessageEventArgs(TestMessageLevel.Error, ...))visible in test output
  3. Logged via EqtTrace.Error
  4. The data collection continues without crashing the entire process
// Before (crashes the process in debug builds)
TPDebug.Assert(testCaseStartEventArgs.TestElement is not null, "testCaseStartEventArgs.TestElement is null");

// After (caught, logged, and reported as a user-visible error)
if (testCaseStartEventArgs.TestElement is null)
{
    throw new InvalidOperationException("TestCaseStartEventArgs.TestElement is null...");
}

Tests

All 390 Microsoft.TestPlatform.Common.UnitTests pass with the change applied.

🔍 Triaged by Issue Repro Triage & Auto-Fix 🔍

🔍 Triaged by Issue Repro Triage & Auto-Fix 🔍

…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>
Copilot AI review requested due to automatic review settings May 20, 2026 14:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 explicit if/throw InvalidOperationException in TestCaseStarted.
  • Replace TPDebug.Assert(...TestElement...) with explicit if/throw InvalidOperationException in TestCaseEnded.

@@ -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");
Comment on lines +320 to +323
if (testCaseStartEventArgs.TestElement is null)
{
throw new InvalidOperationException("TestCaseStartEventArgs.TestElement is null. The data collector cannot start a test case without a test element.");
}
Copy link
Copy Markdown
Member Author

@nohwnd nohwnd left a comment

Choose a reason for hiding this comment

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

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.Assert is [Conditional("DEBUG")] — a no-op in release builds. In debug builds it delegates to Debug.Assert, which can call FailFast and bypass the try/catch in ProcessRequests. The new if/throw InvalidOperationException enforces the null check in all build configurations.
  • Both new exceptions are caught by the existing try/catch blocks in DataCollectionTestCaseEventHandler.ProcessRequests (TestCaseStarted handler at ~line 88, TestCaseEnded handler at ~line 112) and correctly surfaced via _messageSink.SendMessage(new DataCollectionMessageEventArgs(TestMessageLevel.Error, ...)).
  • TestCaseEnded still sends DataCollectionTestEndResult with an empty AttachmentSet on error, which prevents the vstest.console caller from deadlocking while waiting for the result.
  • No public API surface changes; IDataCollectionManager interface is unchanged.

No issues found. ✅


🧠 Reviewed by Expert Code Reviewer

🧠 Reviewed by Expert Code Reviewer 🧠

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

datacollector crash is not easy to see,

2 participants