Skip to content

Commit 708979a

Browse files
EvangelinkCopilot
andcommitted
Address second round of PR review feedback
- Remove de-duplication of MTPProjects: the old CreateXUnitV3WorkItems task didn't deduplicate either; defer to MSBuild item semantics and keep the task minimal. - Drop the TrxReportFilename defensive validation: MTP itself rejects values containing path separators, so the redundant pre-check just added complexity. Filename is still quoted in the emitted command so spaces are safe. - Simplify the timeout-parsing block to use 'out timeout' directly with a default reset on failure (TimeSpan.TryParse zeroes the out var when it fails). - Shrink the MTPTrxReportFilename property documentation in the targets file to a one-liner. - Update tests: replace InvalidTrxReportFilenameIsRejected with TrxReportFilenameIsPassedThroughVerbatim and replace DuplicatesByIdentityAndAdditionalPropertiesAreCollapsed with DuplicateInputsArePassedThroughAsSeparateWorkItems. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 7403aea commit 708979a

3 files changed

Lines changed: 18 additions & 58 deletions

File tree

src/Microsoft.DotNet.Helix/Sdk.Tests/Microsoft.DotNet.Helix.Sdk.Tests/CreateMTPWorkItemsTests.cs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,17 @@ public void CustomTrxReportFilenameIsQuoted()
8989
[InlineData("sub\\results.trx")]
9090
[InlineData("with\"quote.trx")]
9191
[InlineData("")]
92-
public void InvalidTrxReportFilenameIsRejected(string filename)
92+
public void TrxReportFilenameIsPassedThroughVerbatim(string filename)
9393
{
94+
// The task no longer validates the filename - MTP itself rejects values containing
95+
// path separators, and the value is quoted in the command so spaces are safe.
9496
var task = CreateTask();
9597
task.TrxReportFilename = filename;
9698
task.MTPProjects = new[] { CreateProject("MyApp.Tests.csproj") };
9799

98-
task.Execute().Should().BeFalse();
99-
task.MTPWorkItems.Should().BeEmpty();
100+
task.Execute().Should().BeTrue();
101+
task.MTPWorkItems.Single().GetMetadata("Command")
102+
.Should().Contain($"--report-trx-filename \"{filename}\"");
100103
}
101104

102105
[Fact]
@@ -144,8 +147,11 @@ public void InvalidTimeoutFallsBackToDefaultWithWarning()
144147
}
145148

146149
[Fact]
147-
public void DuplicatesByIdentityAndAdditionalPropertiesAreCollapsed()
150+
public void DuplicateInputsArePassedThroughAsSeparateWorkItems()
148151
{
152+
// The task does not deduplicate - it mirrors the old CreateXUnitV3WorkItems behavior
153+
// and trusts the caller to provide a clean item list (MSBuild's RemoveDuplicates can
154+
// be used upstream if needed).
149155
var task = CreateTask();
150156
task.MTPProjects = new[]
151157
{
@@ -154,7 +160,7 @@ public void DuplicatesByIdentityAndAdditionalPropertiesAreCollapsed()
154160
};
155161

156162
task.Execute().Should().BeTrue();
157-
task.MTPWorkItems.Should().HaveCount(1);
163+
task.MTPWorkItems.Should().HaveCount(2);
158164
}
159165

160166
[Fact]

src/Microsoft.DotNet.Helix/Sdk/CreateMTPWorkItems.cs

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,7 @@ public override bool Execute()
7575

7676
private async Task ExecuteAsync()
7777
{
78-
// De-duplicate inputs by (Identity, AdditionalProperties). Protects against the
79-
// same project being included by overlapping globs while preserving legitimate
80-
// same-Identity entries that differ in AdditionalProperties (the canonical
81-
// multi-TFM pattern).
82-
var deduped = MTPProjects
83-
.GroupBy(p => (p.ItemSpec, p.GetMetadata("AdditionalProperties") ?? string.Empty))
84-
.Select(g => g.First())
85-
.ToArray();
86-
87-
MTPWorkItems = (await Task.WhenAll(deduped.Select(PrepareWorkItem))).Where(wi => wi != null).ToArray();
78+
MTPWorkItems = (await Task.WhenAll(MTPProjects.Select(PrepareWorkItem))).Where(wi => wi != null).ToArray();
8879
}
8980

9081
/// <summary>
@@ -113,39 +104,13 @@ private async Task<ITaskItem> PrepareWorkItem(ITaskItem mtpProject)
113104
assemblyBaseName = assemblyBaseName.Substring(0, assemblyBaseName.Length - 4);
114105
}
115106

116-
// Defense in depth: reject values that would let the TRX file escape the work
117-
// item working directory or break shell tokenization in surprising ways. Path
118-
// separators in particular would cause '--results-directory .' + the filename
119-
// to land outside the cwd where neither the arcade Python reporter nor the
120-
// Helix job monitor scan for results.
121-
if (string.IsNullOrEmpty(TrxReportFilename) ||
122-
TrxReportFilename.IndexOfAny(new[] { '/', '\\', '"' }) >= 0)
123-
{
124-
Log.LogError($"Invalid value \"{TrxReportFilename}\" provided for TrxReportFilename; it must be a non-empty filename with no path separators or quote characters.");
125-
return null;
126-
}
127-
128107
// MTP test apps are self-hosting executables. Run the assembly directly with
129108
// 'dotnet exec'. The reporter args below require the test project to reference
130109
// Microsoft.Testing.Extensions.TrxReport. MSTest.Sdk references it transitively;
131110
// xUnit v3 projects built with Microsoft.DotNet.Arcade.Sdk's XUnitV3 targets get
132111
// it implicitly as well. Other MTP-based frameworks must add the package.
133-
//
134-
// --results-directory . -> TRX is written next to the assembly, which is the
135-
// work item's cwd and is scanned by the arcade Python
136-
// reporter / EnableHelixJobMonitor.
137-
// --report-trx -> enable the TrxReport extension.
138-
// --report-trx-filename -> deterministic filename so the parser finds it. The
139-
// value is wrapped in double quotes so spaces or
140-
// other shell-significant characters in the filename
141-
// do not split the argument.
142-
//
143-
// Note about the AzureDevOps reporter (Microsoft.Testing.Extensions.AzureDevOpsReport):
144-
// it activates only when '--report-azdo' is explicitly passed AND TF_BUILD=true.
145-
// We never pass '--report-azdo' here, so there is no risk of a duplicate Azure
146-
// DevOps test run on top of the one the Helix Sdk already opened. If a user has
147-
// wired '--report-azdo' into their test project they should remove it for Helix
148-
// runs (it conflicts with the Helix-managed run).
112+
// The filename is wrapped in double quotes so spaces in user-provided values do
113+
// not split the argument. MTP itself rejects values containing path separators.
149114
string reporterArgs =
150115
$"--results-directory . --report-trx --report-trx-filename \"{TrxReportFilename}\"";
151116

@@ -158,16 +123,10 @@ private async Task<ITaskItem> PrepareWorkItem(ITaskItem mtpProject)
158123
Log.LogMessage($"Creating MTP work item with properties Identity: {assemblyName}, PayloadDirectory: {publishDirectory}, Command: {command}");
159124

160125
TimeSpan timeout = TimeSpan.FromMinutes(5);
161-
if (!string.IsNullOrEmpty(MTPWorkItemTimeout))
126+
if (!string.IsNullOrEmpty(MTPWorkItemTimeout) && !TimeSpan.TryParse(MTPWorkItemTimeout, out timeout))
162127
{
163-
if (!TimeSpan.TryParse(MTPWorkItemTimeout, out TimeSpan parsedTimeout))
164-
{
165-
Log.LogWarning($"Invalid value \"{MTPWorkItemTimeout}\" provided for MTPWorkItemTimeout; falling back to default value of \"00:05:00\" (5 minutes)");
166-
}
167-
else
168-
{
169-
timeout = parsedTimeout;
170-
}
128+
Log.LogWarning($"Invalid value \"{MTPWorkItemTimeout}\" provided for MTPWorkItemTimeout; falling back to default value of \"00:05:00\" (5 minutes)");
129+
timeout = TimeSpan.FromMinutes(5);
171130
}
172131

173132
var result = new Microsoft.Build.Utilities.TaskItem(assemblyName, new Dictionary<string, string>()

src/Microsoft.DotNet.Helix/Sdk/tools/mtp-runner/MTPRunner.targets

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,7 @@
1919

2020
<PropertyGroup>
2121
<_MTPPublishTargetsPath>$(MSBuildThisFileDirectory)../xunit-runner/XUnitPublish.targets</_MTPPublishTargetsPath>
22-
<!--
23-
Name of the TRX file produced by Microsoft.Testing.Extensions.TrxReport. The arcade
24-
Python TRXFormat parser (and EnableHelixJobMonitor's *.trx sweep) will pick up any
25-
*.trx in the work item working directory, so the exact name only matters if the user
26-
has multiple consumers expecting a specific filename.
27-
-->
22+
<!-- Name of the TRX file produced by Microsoft.Testing.Extensions.TrxReport. -->
2823
<MTPTrxReportFilename Condition="'$(MTPTrxReportFilename)' == ''">testResults.trx</MTPTrxReportFilename>
2924
</PropertyGroup>
3025

0 commit comments

Comments
 (0)