[fix] Fix TRX attachment paths when LogFileName contains a subdirectory#15791
[fix] Fix TRX attachment paths when LogFileName contains a subdirectory#15791nohwnd wants to merge 3 commits into
Conversation
When LogFileName is set to a path like 'subdir/results.trx', the TRX file is placed at '<TestRunDirectory>/subdir/results.trx'. However, attachments were still organized relative to the original TestRunDirectory. Since TRX viewers resolve RunDeploymentRootDirectory relative to the TRX file's parent folder, attachments became unreachable. Fix: adjust _testResultsDirPath in Initialize to include the directory component of LogFileName so that both the TRX file and its attachments land under the same parent directory. Also update GetTrxFilePath to use only the filename portion of LogFileName (since the directory is now already baked into _testResultsDirPath). Fixes #15271 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR updates the TRX logger so LogFileName values containing a subdirectory place the TRX file and attachment deployment directory under the same relative folder, addressing attachment resolution for TRX viewers.
Changes:
- Adjusts the logger’s test results directory when
LogFileNameincludes a directory component. - Prevents duplicating the directory portion when constructing the final TRX file path.
- Adds a unit test for TRX path placement with a subdirectory in
LogFileName.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
src/Microsoft.TestPlatform.Extensions.TrxLogger/TrxLogger.cs |
Updates initialization and TRX path construction to account for subdirectories in LogFileName. |
test/Microsoft.TestPlatform.Extensions.TrxLogger.UnitTests/TrxLoggerTests.cs |
Adds coverage for TRX file path placement when LogFileName includes a subdirectory. |
| var parameters = new Dictionary<string, string?> | ||
| { | ||
| [DefaultLoggerParameterNames.TestRunDirectory] = DefaultTestRunDirectory, | ||
| [TrxLoggerConstants.LogFileNameKey] = Path.Combine(subDir, fileName), | ||
| }; | ||
| logger.Initialize(_events.Object, parameters); | ||
|
|
||
| MakeTestRunComplete(logger); | ||
|
|
||
| try | ||
| { | ||
| // The TRX file must be at <TestRunDirectory>/<subDir>/<fileName> | ||
| var expectedTrxPath = Path.Combine(DefaultTestRunDirectory, subDir, fileName); | ||
| Assert.AreEqual(expectedTrxPath, logger.TrxFile, "TRX file should be in the specified subdirectory."); | ||
| } | ||
| finally | ||
| { | ||
| if (!string.IsNullOrEmpty(logger.TrxFile) && File.Exists(logger.TrxFile)) | ||
| { | ||
| File.Delete(logger.TrxFile); | ||
| } |
nohwnd
left a comment
There was a problem hiding this comment.
Code Review
Dimensions activated: Null Safety & Boundary Validation, Backward Compatibility & Rollback Safety, Error Reporting & Diagnostic Clarity
Summary
The fix correctly addresses the root cause. When LogFileName contains a directory component, _testResultsDirPath is now pre-adjusted to include that subdirectory, ensuring both the TRX file and its attachment folder land in the same directory. The AcquireTrxFileNamePath change correctly uses Path.GetFileName to avoid double-inclusion of the subdirectory.
Correctness Analysis
Path edge cases — all handled correctly:
- Plain filename (
results.trx):Path.GetDirectoryNamereturns"", which is null-or-whitespace → no adjustment. ✓ - Absolute path (
/tmp/results.trx):Path.Combine(testRunDirectory, "/tmp")→ absolute path wins, same behavior as before. ✓ - Nested subdirectories (
dir1/dir2/results.trx):Path.GetDirectoryNamereturnsdir1/dir2→ correctly combined. ✓ LogFilePrefixguard: the adjustment is skipped whenisLogFilePrefixParameterExistsis true, matching theAcquireTrxFileNamePathbranch logic. ✓
runDeploymentRoot in TRX: CreateTestRun derives runDeploymentRoot from the run name (not _testResultsDirPath), so it's always a relative folder name. The fix ensures this relative folder resolves correctly from the TRX file's directory because _testResultsDirPath now points to the same directory as the TRX file. ✓
One Minor Note
The new test doesn't clean up the subdir directory it creates under Path.GetTempPath() — only the TRX file itself is deleted. However, this is consistent with the existing test style in this file (e.g., CustomTrxFileNameShouldConstructFromLogFileParameter performs no cleanup at all), and using the system temp path means it's not harmful. The new test is actually more careful than its peers by having a try/finally block at all.
No blocking issues. The fix is correct and backward-compatible.
🧠 Reviewed by Expert Code Reviewer
🧠 Reviewed by Expert Code Reviewer 🧠
Verifies that when LogFileName contains a path component (e.g. subdir/results.trx), the TRX file is placed in the expected subdirectory under the results directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Commit pushed:
|
nohwnd
left a comment
There was a problem hiding this comment.
Code Review
Dimensions activated: Null Safety & Boundary Validation, Backward Compatibility & Rollback Safety, Acceptance Test Coverage Design, Cross-TFM & Framework Resolution
Summary
No blocking issues. The fix is correct and backward-compatible. This is a re-review pass — the head commit (69eced6) is only a CI retrigger with no code changes since the previous review.
Dimension Findings
Null Safety & Boundary Validation ✓
logFileNameInit.IsNullOrWhiteSpace()correctly guards theTryGetValueresult beforePath.GetDirectoryName.logFileDir.IsNullOrWhiteSpace()correctly skips the adjustment for plain filenames (wherePath.GetDirectoryNamereturns"").Path.GetFileName(logFileNameValue!)is sound — it's called inside theisLogFileNameParameterExistsbranch, which guarantees the value was set.
Backward Compatibility & Rollback Safety ✓
- Plain filename (
results.trx):Path.GetDirectoryNamereturns""→IsNullOrWhiteSpaceis true → no adjustment → identical behavior to before. ✓ - Subdirectory (
subdir/results.trx): adjustment applied, attachments land co-located with the TRX. ✓ - Absolute path (
/tmp/results.trx):Path.Combine(testRunDirectory, "/tmp")→ absolute path wins on both platforms →_testResultsDirPath=/tmp; TRX file =/tmp/results.trx— same final location as before, and attachments are now also correctly co-located. ✓ - The
!isLogFilePrefixParameterExistsguard correctly mirrors theLogFilePrefixvsLogFileNamebranch logic inAcquireTrxFileNamePath. ✓
Acceptance Test Coverage Design ✓
- Test covers both
NetFullTargetFrameworkDataSourceandNetCoreTargetFrameworkDataSource— good TFM breadth. - Uses
TempDirectory.Pathfor the results directory, so cleanup is automatic. - Asserts both file existence and XML validity, providing meaningful failure messages including a glob of all TRX files found.
Cross-TFM & Framework Resolution ✓
- All APIs used (
Path.GetDirectoryName,Path.GetFileName,Path.Combine,string.IsNullOrWhiteSpace) are available on all vstest target TFMs (net462 / netstandard2.0 / net8.0+).
🧠 Reviewed by Expert Code Reviewer 🧠
🧠 Reviewed by Expert Code Reviewer 🧠
| [TestMethod] | ||
| public void CustomTrxFileNameWithSubdirectoryShouldPlaceTrxAndAttachmentsUnderSameDirectory() | ||
| { | ||
| // Arrange: LogFileName contains a subdirectory component | ||
| var logger = new TestableTrxLogger(); |
| [TestMethod] | ||
| [NetFullTargetFrameworkDataSource] | ||
| [NetCoreTargetFrameworkDataSource] | ||
| public void TrxLoggerShouldPlaceTrxFileInSubdirectoryWhenLogFileNameContainsPath(RunnerInfo runnerInfo) | ||
| { | ||
| // Regression test for https://github.com/microsoft/vstest/issues/15271 | ||
| // When LogFileName contains a subdirectory (e.g. "subdir/results.trx"), | ||
| // the TRX file and its attachments should be placed under that subdirectory. | ||
| SetTestEnvironment(_testEnvironment, runnerInfo); |
🤖 This is an automated fix generated by the Issue Triage agent.
Fixes #15271
Root Cause
When
--logger trx;LogFileName=subdir/results.trxis used, the TRX file is placed at<TestRunDirectory>/subdir/results.trx. However, the internal_testResultsDirPath(used to organize test attachments) remained set to the originalTestRunDirectory.TRX viewers resolve the
runDeploymentRootattribute relative to the directory containing the TRX file. So a viewer openingsubdir/results.trxexpects attachments atsubdir/<runDeploymentRoot>/In/..., but they were actually placed at<runDeploymentRoot>/In/...(one level up) — making them unreachable.Fix
In
Initialize(events, parameters), detect whenLogFileNamehas a directory component and pre-adjust_testResultsDirPathto point to the directory that will contain the TRX file. This ensures both the TRX and its attachment folder are co-located.In
GetTrxFilePath, usePath.GetFileName(logFileNameValue)instead of the fulllogFileNameValueso the directory portion isn't appended twice (it's already in_testResultsDirPath).The fix is backward-compatible: when
LogFileNameis a plain filename with no directory component,Path.GetDirectoryNamereturns an empty string and no adjustment is made.Test
Added
CustomTrxFileNameWithSubdirectoryShouldPlaceTrxAndAttachmentsUnderSameDirectorywhich verifies that whenLogFileName=subdir/results.trx, the TRX file lands at<TestRunDirectory>/subdir/results.trx.