From 478e113e7b64438edc73026b2929a2e2d6479d2e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 13 May 2026 16:28:23 +0000
Subject: [PATCH 1/3] Initial plan
From b4696105527bd0a93136618d4ea29a0a1ae1c01e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 13 May 2026 16:37:32 +0000
Subject: [PATCH 2/3] Fix TRX per-test artifact directory to include relative
results folder
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
---
.../TrxReportEngine.cs | 12 +++++++-----
.../TrxTests.cs | 9 +++++++++
2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs
index 831e83767d..85dfe942d0 100644
--- a/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs
+++ b/src/Platform/Microsoft.Testing.Extensions.TrxReport/TrxReportEngine.cs
@@ -360,9 +360,9 @@ private void AddResultSummary(XElement testRun, string resultSummaryOutcome, str
AddArtifactsToCollection(_artifactsByExtension, collectorDataEntries, runDeploymentRoot);
}
- private string CopyArtifactIntoTrxDirectoryAndReturnHrefValue(FileInfo artifact, string runDeploymentRoot)
+ private string CopyArtifactIntoTrxDirectoryAndReturnHrefValue(FileInfo artifact, string runDeploymentRoot, string? relativeResultsDirectory = null)
{
- string artifactDirectory = CreateOrGetTrxArtifactDirectory(runDeploymentRoot);
+ string artifactDirectory = CreateOrGetTrxArtifactDirectory(runDeploymentRoot, relativeResultsDirectory);
string fileName = artifact.Name;
string destination = Path.Combine(artifactDirectory, fileName);
@@ -386,9 +386,11 @@ private string CopyArtifactIntoTrxDirectoryAndReturnHrefValue(FileInfo artifact,
return Path.Combine(_environment.MachineName, Path.GetFileName(destination));
}
- private string CreateOrGetTrxArtifactDirectory(string runDeploymentRoot)
+ private string CreateOrGetTrxArtifactDirectory(string runDeploymentRoot, string? relativeResultsDirectory = null)
{
- string directoryName = Path.Combine(_configuration.GetTestResultDirectory(), runDeploymentRoot, "In", _environment.MachineName);
+ string directoryName = relativeResultsDirectory is null
+ ? Path.Combine(_configuration.GetTestResultDirectory(), runDeploymentRoot, "In", _environment.MachineName)
+ : Path.Combine(_configuration.GetTestResultDirectory(), runDeploymentRoot, "In", relativeResultsDirectory, _environment.MachineName);
if (!Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
@@ -580,7 +582,7 @@ private SummaryCounts AddResults(TestNodeUpdateMessage[] testNodeUpdateMessages,
{
resultFiles ??= new XElement("ResultFiles");
- string href = CopyArtifactIntoTrxDirectoryAndReturnHrefValue(testFileArtifact.FileInfo, runDeploymentRoot);
+ string href = CopyArtifactIntoTrxDirectoryAndReturnHrefValue(testFileArtifact.FileInfo, runDeploymentRoot, executionId);
resultFiles.Add(new XElement(
"ResultFile",
new XAttribute("path", href)));
diff --git a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs
index 491a882efe..4315016f4a 100644
--- a/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs
+++ b/test/UnitTests/Microsoft.Testing.Extensions.UnitTests/TrxTests.cs
@@ -469,6 +469,8 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByTestNode_Tr
Assert.IsNotNull(memoryStream.TrxContent);
XDocument xml = memoryStream.TrxContent;
AssertTrxOutcome(xml, "Completed");
+ string relativeResultsDirectory = xml.Descendants().Single(x => x.Name.LocalName == "UnitTestResult").Attribute("relativeResultsDirectory")!.Value;
+ string expectedDestinationSuffix = Path.Combine("_MachineName_0001-01-01_00_00_00.0000000", "In", relativeResultsDirectory, "MachineName", "fileName");
string trxContent = xml.ToString();
string trxContentsPattern = @"
@@ -478,6 +480,13 @@ public async Task TrxReportEngine_GenerateReportAsync_WithArtifactsByTestNode_Tr
";
Assert.IsTrue(Regex.IsMatch(trxContent, trxContentsPattern));
+ _fileSystem.Verify(
+ x => x.CopyFile(
+ It.Is(source => source.EndsWith("fileName", StringComparison.Ordinal)),
+ It.Is(destination => destination.EndsWith(
+ expectedDestinationSuffix,
+ StringComparison.Ordinal))),
+ Times.Once);
}
[TestMethod]
From 719c7047f0e9a037f0dd4d313e372540b89a9f0f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 14 May 2026 07:49:18 +0000
Subject: [PATCH 3/3] Add TRX acceptance coverage for artifact relative results
path
Co-authored-by: Evangelink <11340282+Evangelink@users.noreply.github.com>
---
.../TrxTests.cs | 44 ++++++++++++++++++-
1 file changed, 43 insertions(+), 1 deletion(-)
diff --git a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs
index 956bd745bf..fe377cc261 100644
--- a/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs
+++ b/test/IntegrationTests/Microsoft.Testing.Platform.Acceptance.IntegrationTests/TrxTests.cs
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+using System.Xml.Linq;
+
namespace Microsoft.Testing.Platform.Acceptance.IntegrationTests;
[TestClass]
@@ -36,6 +38,38 @@ public async Task Trx_WhenReportTrxIsSpecified_TrxReportIsGeneratedInDefaultLoca
await AssertTrxReportWasGeneratedAsync(testHostResult, trxPathPattern, 1);
}
+ [DynamicData(nameof(TargetFrameworks.AllForDynamicData), typeof(TargetFrameworks))]
+ [TestMethod]
+ public async Task Trx_WhenReportTrxAndResultsDirectoryAreSpecifiedWithArtifact_ArtifactIsCopiedUnderRelativeResultsDirectory(string tfm)
+ {
+ string fileName = Guid.NewGuid().ToString("N");
+ string testResultsPath = Path.Combine(AssetFixture.TargetAssetPath, Guid.NewGuid().ToString("N"));
+ var testHost = TestInfrastructure.TestHost.LocateFrom(AssetFixture.TargetAssetPath, TestAssetFixture.AssetName, tfm);
+ TestHostResult testHostResult = await testHost.ExecuteAsync(
+ $"--report-trx --report-trx-filename {fileName}.trx --results-directory \"{testResultsPath}\"",
+ new() { ["WITH_ARTIFACT"] = "1" },
+ cancellationToken: TestContext.CancellationToken);
+
+ testHostResult.AssertExitCodeIs(ExitCode.Success);
+
+ string[] trxFiles = Directory.GetFiles(testResultsPath, $"{fileName}.trx", SearchOption.AllDirectories);
+ Assert.HasCount(1, trxFiles, $"Expected exactly one trx file but found {trxFiles.Length}: {string.Join(", ", trxFiles)}");
+
+ var trxDocument = XDocument.Parse(File.ReadAllText(trxFiles[0]));
+ XNamespace ns = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010";
+ XElement unitTestResult = trxDocument.Descendants(ns + "UnitTestResult").Single();
+ string relativeResultsDirectory = unitTestResult.Attribute("relativeResultsDirectory")!.Value;
+ string resultFilePath = unitTestResult.Descendants(ns + "ResultFile").Single().Attribute("path")!.Value;
+ string runDeploymentRoot = trxDocument.Descendants(ns + "Deployment").Single().Attribute("runDeploymentRoot")!.Value;
+ string normalizedResultFilePath = resultFilePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
+
+ string copiedArtifactPath = Path.Combine(testResultsPath, runDeploymentRoot, "In", relativeResultsDirectory, normalizedResultFilePath);
+ Assert.IsTrue(File.Exists(copiedArtifactPath), $"Expected copied artifact at '{copiedArtifactPath}' but it was not found.");
+
+ string legacyArtifactPath = Path.Combine(testResultsPath, runDeploymentRoot, "In", normalizedResultFilePath);
+ Assert.IsFalse(File.Exists(legacyArtifactPath), $"Artifact was copied to legacy path '{legacyArtifactPath}'.");
+ }
+
[DynamicData(nameof(TargetFrameworks.NetForDynamicData), typeof(TargetFrameworks))]
[TestMethod]
public async Task Trx_WhenTestHostCrash_ErrorIsDisplayedInsideTheTrx(string tfm)
@@ -252,8 +286,16 @@ public async Task ExecuteRequestAsync(ExecuteRequestContext context)
}
var testMethodIdentifier = new TestMethodIdentifierProperty(string.Empty, string.Empty, "DummyClassName", "Test", 0, Array.Empty(), string.Empty);
+ PropertyBag properties = new(PassedTestNodeStateProperty.CachedInstance, testMethodIdentifier);
+ if (Environment.GetEnvironmentVariable("WITH_ARTIFACT") == "1")
+ {
+ string artifactPath = Path.Combine(Directory.GetCurrentDirectory(), "test-artifact.txt");
+ File.WriteAllText(artifactPath, "artifact");
+ properties.Add(new FileArtifactProperty(new FileInfo(artifactPath), "TestMethod", "description"));
+ }
+
await context.MessageBus.PublishAsync(this, new TestNodeUpdateMessage(context.Request.Session.SessionUid,
- new TestNode() { Uid = "0", DisplayName = "Test", Properties = new(PassedTestNodeStateProperty.CachedInstance, testMethodIdentifier) }));
+ new TestNode() { Uid = "0", DisplayName = "Test", Properties = properties }));
context.Complete();
}
}