[fix] Add TreatErrorMessagesAsWarnings parameter to TRX logger - #16106
Conversation
Fixes issue #10391: Data Collector Errors Cause TRX Result to be Failed. When a data collector (or any source) logs an error-level message during a test run where all tests pass, the TRX logger sets the ResultSummary outcome to 'Failed', misleading users into thinking their tests failed. This change adds a new TreatErrorMessagesAsWarnings logger parameter. When set to true (e.g. /logger:"trx;TreatErrorMessagesAsWarnings=true"), error-level run messages no longer mark the overall outcome as Failed. The messages are still recorded in the TRX file's ErrorInfo section. Changes: - Constants.cs: add TreatErrorMessagesAsWarnings string constant - TrxLogger.cs: read the parameter in Initialize and honor it in TestMessageHandler to skip updating TestResultOutcome for errors - TrxLoggerTests.cs: unit tests for new and existing behavior - LoggerTests.cs: acceptance tests using OutOfProcDataCollector which logs an error in SessionEnded_Handler to exercise the real pipeline Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds an opt-in TRX logger parameter to prevent error-level run messages (notably from data collectors) from forcing the overall TRX run outcome to Failed, addressing #10391 while keeping default behavior unchanged.
Changes:
- Introduces a new TRX logger parameter
TreatErrorMessagesAsWarnings. - Updates
TrxLogger.TestMessageHandlerto avoid setting the overall outcome toFailedon error-level messages when the parameter is enabled. - Adds unit + acceptance coverage to validate default behavior and the new opt-in behavior end-to-end.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/Microsoft.TestPlatform.Extensions.TrxLogger/Utility/Constants.cs |
Adds the new logger parameter key constant. |
src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs |
Parses the new parameter and gates whether error-level messages flip the overall TRX outcome to Failed. |
test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs |
Adds unit tests for default outcome behavior vs. opt-in behavior. |
test/Microsoft.TestPlatform.Acceptance.IntegrationTests/LoggerTests.cs |
Adds acceptance tests verifying TRX ResultSummary outcome changes with/without the new parameter when a data collector logs an error. |
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Expert Review — [fix] Add TreatErrorMessagesAsWarnings parameter to TRX logger
Activated dimensions: RunSettings Validation & Inference, Backward Compatibility & Rollback Safety, Error Reporting & Diagnostic Clarity, Null Safety & Boundary Validation
Summary: The change is well-structured and correctly follows the WarnOnFileOverwrite pattern for parameter parsing. Backward compatibility is preserved by defaulting to false. Tests cover the happy path and recording behavior. One correctness issue found in the parse-error fallback path.
🔴 Finding: Parse-error fallback defaults to true (suppresses failures silently)
File: TrxLogger.cs, line 153
When TreatErrorMessagesAsWarnings is present in the parameters dictionary but fails bool.TryParse (e.g. TreatErrorMessagesAsWarnings=yes or TreatErrorMessagesAsWarnings=1), the code falls back to true. This means a misconfigured value silently opts the user into suppressing error-level messages from counting as failures — the opposite of the safe default.
Unlike WarnOnFileOverwrite (where true means "warn = cautious"), true here means "mask failures = less cautious". The correct parse-error fallback is false (same as the not-found default).
See the inline comment for the fix.
✅ What looks correct
- Backward compatibility:
_treatErrorMessagesAsWarningsdefaults tofalsewhen the parameter is absent — existing behavior is fully preserved. - Error recording: Even with
TreatErrorMessagesAsWarnings=true, theRunInfois still added to_runLevelErrorsAndWarningswithTestOutcome.Error, so errors appear in the TRXErrorInfosection. Initializecall ordering:_treatErrorMessagesAsWarningsis set beforeInitialize(events, testRunDirectory)is called; the simpler overload doesn't reset it, which is consistent with how_warnOnFileOverwriteis handled.- Acceptance tests:
SampleDataCollector.SessionEnded_HandlercallsLogErrorunconditionally, giving the tests a reliable error source. Thenetstandard2.0target onOutOfProcDataCollector.csprojmatches the test'sGetTestDllForFramework("OutOfProcDataCollector.dll", "netstandard2.0")call.
🧠 Reviewed by Expert Code Reviewer
🧠 Reviewed by Expert Code Reviewer 🧠
| ? bool.TryParse(treatErrorMessagesAsWarningsString, out bool treatErrorMessagesAsWarningsValue) | ||
| ? treatErrorMessagesAsWarningsValue | ||
| // We found the option but could not parse the value, default to true. | ||
| : true |
There was a problem hiding this comment.
[Correctness] The parse-error fallback defaults to true, which silently suppresses failure reporting when the parameter value can't be parsed (e.g. TreatErrorMessagesAsWarnings=yes).
The analogy to WarnOnFileOverwrite doesn't hold here: for WarnOnFileOverwrite, true is the cautious default (produces a visible warning instead of silently overwriting). For TreatErrorMessagesAsWarnings, true is the less cautious default — it masks failures that the user may not have intended to suppress.
If a user passes an invalid value, the safe fallback is false, which preserves the original behavior where errors mark the run as failed:
_treatErrorMessagesAsWarnings = parameters.TryGetValue(TrxLoggerConstants.TreatErrorMessagesAsWarnings, out string? treatErrorMessagesAsWarningsString)
? bool.TryParse(treatErrorMessagesAsWarningsString, out bool treatErrorMessagesAsWarningsValue)
? treatErrorMessagesAsWarningsValue
// We found the option but could not parse the value; preserve existing behavior.
: false
: false;🧠 Reviewed by Expert Code Reviewer
There was a problem hiding this comment.
Fixed — changed the parse-error fallback from true to false so an unparseable value (e.g. TreatErrorMessagesAsWarnings=yes) preserves the existing behavior rather than silently opting into warning mode.
🔧 Iterated by PR Iteration Agent 🔧
…to false When TreatErrorMessagesAsWarnings is present but cannot be parsed (e.g. 'yes', '1'), the safe fallback is false (preserve existing behavior where errors mark the run as failed) rather than true (silently suppress failures). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Expert Review — Follow-up: [fix] Add TreatErrorMessagesAsWarnings parameter to TRX logger
The previous finding has been addressed. The parse-error fallback for TreatErrorMessagesAsWarnings is now correctly false — a malformed value (e.g. TreatErrorMessagesAsWarnings=yes) preserves existing behavior rather than silently opting the user into warning mode.
All activated dimensions checked (RunSettings Validation & Inference, Backward Compatibility & Rollback Safety, Error Reporting & Diagnostic Clarity, Null Safety & Boundary Validation):
- ✅ Parse-error fallback fixed: Both the not-found and parse-failure branches now return
false, matching the safe default. - ✅ Backward compatibility: Unchanged —
falsedefault means existing runs see no behavior change. - ✅ Error recording preserved:
_runLevelErrorsAndWarnings.Add(runMessage)still executes unconditionally regardless of the flag, so errors appear in the TRXErrorInfosection. - ✅ Initialize ordering:
_treatErrorMessagesAsWarningsis set beforeInitialize(events, testRunDirectory)is called, consistent with the_warnOnFileOverwritepattern. - ✅ Unit tests: Cover the default path (
Failedon error), the opt-in path (outcome unchanged), and the recording invariant (error still logged even with flag). - ✅ Acceptance tests: Cover the end-to-end pipeline with
OutOfProcDataCollectoragainst both .NET Full and .NET Core runners.
No further issues found.
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
| if (!_treatErrorMessagesAsWarnings) | ||
| { | ||
| TestResultOutcome = TrxLoggerObjectModel.TestOutcome.Failed; | ||
| } | ||
|
|
||
| runMessage = new RunInfo(e.Message, null, Environment.MachineName, TrxLoggerObjectModel.TestOutcome.Error); | ||
| _runLevelErrorsAndWarnings.Add(runMessage); | ||
| break; |
| _treatErrorMessagesAsWarnings = parameters.TryGetValue(TrxLoggerConstants.TreatErrorMessagesAsWarnings, out string? treatErrorMessagesAsWarningsString) | ||
| ? bool.TryParse(treatErrorMessagesAsWarningsString, out bool treatErrorMessagesAsWarningsValue) | ||
| ? treatErrorMessagesAsWarningsValue | ||
| // We found the option but could not parse the value; preserve existing behavior. | ||
| : false | ||
| // We did not find the option, default to false to preserve existing behavior. | ||
| : false; |
| var arguments = PrepareArguments(assemblyPath, null, null, FrameworkArgValue, runnerInfo.InIsolationValue, TempDirectory.Path); | ||
| arguments = string.Concat(arguments, $" /TestCaseFilter:PassingTest"); | ||
| arguments = string.Concat(arguments, $" /Collect:SampleDataCollector"); | ||
| arguments = string.Concat(arguments, $" /TestAdapterPath:{extensionsPath}"); |
Jakub Jareš (nohwnd)
left a comment
There was a problem hiding this comment.
Expert Review — Follow-up: [fix] Add TreatErrorMessagesAsWarnings parameter to TRX logger
New commit reviewed: 6a24672 ("Potential fix for pull request finding") — changes TreatErrorMessagesAsWarnings in Constants.cs from public static string to public const string. This is a correct improvement: the value is never reassigned, so const is more accurate and allows compile-time inlining. No correctness impact.
Activated dimensions: RunSettings Validation & Inference, Backward Compatibility & Rollback Safety, Error Reporting & Diagnostic Clarity, Null Safety & Boundary Validation, Acceptance Test Coverage Design
All dimensions checked against the full current diff:
- ✅ Parse-error fallback (previously
REQUEST_CHANGES): Fixed in prior commit — both the not-found and parse-failure branches returnfalse. - ✅
TestResultOutcomeinitialization: Property initializes toTestOutcome.Passed; at run completionPassedis converted toCompleted— consistent with the acceptance test expecting"Completed"for a clean run. - ✅ Error recording invariant:
_runLevelErrorsAndWarnings.Add(runMessage)executes unconditionally regardless of the flag, so errors always appear in the TRXErrorInfosection. - ✅ Backward compatibility: Default
falsepreserves existing behavior; no change to TRX format or wire protocol. - ✅ Null safety:
bool.TryParse(null, ...)returnsfalse, so a null dictionary value is handled safely by the parse-failure branch. - ✅
constdeclaration:internal static class Constants— no external consumers;constis correct and consistent with all other string constants in the file. - ✅ Acceptance tests: Cover both the regression case (
Failedwhen collector logs error without flag) and the opt-in case (Completedwhen flag is set), with[NetFullTargetFrameworkDataSource]and[NetCoreTargetFrameworkDataSource]coverage.
No further issues found.
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
🤖 Automated Fix
Fixes issue #10391: Data Collector Errors Cause TRX Result to be Failed.
Root Cause
In
TrxLogger.TestMessageHandler, thecase TestMessageLevel.Error:branch unconditionally setsTestResultOutcome = TestOutcome.Failed. This means any error-level message — including those from data collectors that fail during session teardown — marks the entire TRX result as failed, even when all tests actually passed.Fix
Adds a new
TreatErrorMessagesAsWarningslogger parameter. When set totrue, error-level run messages no longer updateTestResultOutcome, so the overall TRX outcome reflects actual test results rather than data collector errors.Usage:
or with vstest.console:
Error messages are still recorded in the TRX file's
ErrorInfosection — they are not silenced, just excluded from the pass/fail decision.Changes
Constants.cs: AddTreatErrorMessagesAsWarningsstring constant (follows the same pattern asWarnOnFileOverwrite)TrxLogger.cs: Read the parameter inInitialize(TestLoggerEvents, Dictionary<string, string?>)and skip settingTestResultOutcome = Failedwhen_treatErrorMessagesAsWarningsistrueTrxLoggerTests.cs: Unit tests for default behavior (backward compatible) and new parameter behaviorLoggerTests.cs: Acceptance tests usingOutOfProcDataCollector(which logs errors inSessionEnded_Handler) to verify the real end-to-end pipeline:Failedeven when onlyPassingTestran ✓TreatErrorMessagesAsWarnings=true: TRX outcome isCompleted✓Default Behavior
The default remains unchanged (backward compatible):
TreatErrorMessagesAsWarningsdefaults tofalse, preserving existing behavior where any error message marks the run as failed.