Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e1a52d8
Add artifact merge engines and Kind metadata flow (RFC 018 phase 1)
Evangelink Jul 9, 2026
03364a8
Merge remote-tracking branch 'origin/main' into dev/amauryleve/miniat…
Evangelink Jul 13, 2026
3d61cd4
Address review: TRX merge fidelity + out-of-process Kind
Evangelink Jul 13, 2026
3c482cd
Merge remote-tracking branch 'origin/main' into dev/amauryleve/miniat…
Evangelink Jul 13, 2026
46448d7
Address review: harden TRX attachment relocation
Evangelink Jul 13, 2026
4ea04cc
Update .xlf files to be in sync with .resx
Evangelink Jul 14, 2026
a05a088
Merge remote-tracking branch 'origin/main' into dev/amauryleve/miniat…
Evangelink Jul 14, 2026
3a712c1
Address review: destination reparse hardening, Kind assertion, revert…
Evangelink Jul 14, 2026
a5dab5b
Address review: ancestor-symlink hardening + producer-to-wire Kind tests
Evangelink Jul 14, 2026
a468d2e
Address review: CTRF tool/env attribution + TRX cancellation & path-f…
Evangelink Jul 14, 2026
1c197f4
Address review: TRX nested-overlap relocation + CTRF full tool identity
Evangelink Jul 14, 2026
1400070
Merge branch 'main' into dev/amauryleve/miniature-broccoli
Evangelink Jul 14, 2026
0bf2a16
Merge branch 'main' into dev/amauryleve/miniature-broccoli
Copilot Jul 15, 2026
011ce7c
Merge branch 'main' into dev/amauryleve/miniature-broccoli
Evangelink Jul 15, 2026
579f8b2
Address review: CTRF test-level timing fallback + TRX nested staging …
Evangelink Jul 15, 2026
97dcbb0
Address review: never delete input trees during TRX attachment reloca…
Evangelink Jul 15, 2026
92aae3a
Fix IDE0008 CI break + address review: relocation confinement, stagin…
Evangelink Jul 15, 2026
109ab51
Make report merges deterministic and non-destructive (RFC 018 review)
Evangelink Jul 15, 2026
df5c7c6
Fix TRX per-test ResultFile relocation, naming, and IDE0008 (RFC 018 …
Evangelink Jul 15, 2026
c14a6f9
Fix IDE0007 in merge test (use var for apparent type)
Evangelink Jul 15, 2026
02c0bef
Harden report-merge filesystem handling (RFC 018 review)
Evangelink Jul 15, 2026
bac64fb
Copy only referenced attachments in TRX merge (RFC 018 review)
Evangelink Jul 15, 2026
2b7a860
Drop unrelocated references and detect symlinked-parent output aliase…
Evangelink Jul 15, 2026
31f43de
Fix merge semantics: outcome, definition dedup, environment, referenc…
Evangelink Jul 15, 2026
d582d47
Fix UriAttachment schema validity, dangling dest symlinks, netstandar…
Evangelink Jul 15, 2026
ecdc9f4
Harden path containment, hardlink copy, top-level bail, and env unani…
Evangelink Jul 15, 2026
e68ed42
Preserve rooted attachments (RFC 018), normalize separators, validate…
Evangelink Jul 15, 2026
6cdc1b1
Require CTRF reportFormat, carry run-level ResultFiles, preserve root…
Evangelink Jul 15, 2026
d1b8007
Merge run-level Output, reject empty input early, filesystem-aware al…
Evangelink Jul 16, 2026
f378b5d
Probe output-path case sensitivity at its own directory, not temp (re…
Evangelink Jul 16, 2026
f91fcb2
Extract shared merge file I/O + alias checks into MergeOutputFileHelp…
Evangelink Jul 16, 2026
bd927b5
Fix macOS symlink-alias canonicalization and address merge review com…
Evangelink Jul 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public CtrfReportGenerator(IServiceProvider serviceProvider)

protected override string ArtifactDisplayName => ExtensionResources.CtrfReportArtifactDisplayName;

protected override string? ArtifactKind => "microsoft.testing.ctrf";

protected override string ArtifactDescription => ExtensionResources.CtrfReportArtifactDescription;

protected override string GetGenerationLogMessage(int testResultCount)
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
#nullable enable
virtual Microsoft.Testing.Extensions.ReportGeneratorBase<TGenerator, TCapturedTestResult>.ArtifactKind.get -> string?
Microsoft.Testing.Extensions.CtrfReport.CtrfReportMerger
static Microsoft.Testing.Extensions.CtrfReport.CtrfReportMerger.Merge(System.Collections.Generic.IReadOnlyList<string!>! inputReports) -> string!
static Microsoft.Testing.Extensions.CtrfReport.CtrfReportMerger.MergeToFileAsync(System.Collections.Generic.IReadOnlyList<string!>! inputPaths, string! outputPath, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipeDirectoryNotWritableErrorMessage.get -> string!
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipePathTooLongErrorMessage.get -> string!
static Microsoft.Testing.Platform.Services.ArtifactNamingHelper.ResolveAndSanitize(string! template, string! processName, string! processId, System.DateTimeOffset timestamp, System.Func<string!, string!>! sanitizeLeafFileName) -> string!
Microsoft.Testing.Extensions.MergeOutputFileHelper
static Microsoft.Testing.Extensions.MergeOutputFileHelper.EnsureOutputDoesNotAliasInput(System.Collections.Generic.IReadOnlyList<string!>! inputPaths, string! outputPath) -> void
static Microsoft.Testing.Extensions.MergeOutputFileHelper.WriteViaTemporarySiblingAsync(string! outputPath, System.Func<string!, System.Threading.Tasks.Task!>! writeToTempAsync) -> System.Threading.Tasks.Task!
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ This package extends Microsoft Testing Platform to produce test reports in the C
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportProviderRegistration.cs" Link="Helpers\ReportProviderRegistration.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportGeneratorCommandLineBase.cs" Link="Helpers\ReportGeneratorCommandLineBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameValidator.cs" Link="Helpers\ReportFileNameValidator.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\MergeOutputFileHelper.cs" Link="Helpers\MergeOutputFileHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\CapturedTestResultBase.cs" Link="Helpers\CapturedTestResultBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\TargetFrameworkMonikerHelper.cs" Link="Helpers\TargetFrameworkMonikerHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\TestResultCaptureHelper.cs" Link="Helpers\TestResultCaptureHelper.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
const Microsoft.Testing.Platform.IPC.FileArtifactMessageFieldsId.Kind = 7 -> ushort
const Microsoft.Testing.Platform.Helpers.EnvironmentVariableConstants.TESTINGPLATFORM_PIPE_DIRECTORY = "TESTINGPLATFORM_PIPE_DIRECTORY" -> string!
const Microsoft.Testing.Platform.IPC.NamedPipeServer.MaxUnixDomainSocketPathLengthInBytes = 103 -> int
static Microsoft.Testing.Platform.IPC.NamedPipeServer.EnsureDirectoryIsWritable(string! directory) -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public HtmlReportGenerator(IServiceProvider serviceProvider)

protected override string ArtifactDisplayName => ExtensionResources.HtmlReportArtifactDisplayName;

protected override string? ArtifactKind => "microsoft.testing.html";

protected override string ArtifactDescription => ExtensionResources.HtmlReportArtifactDescription;

protected override string GetGenerationLogMessage(int testResultCount)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
virtual Microsoft.Testing.Extensions.ReportGeneratorBase<TGenerator, TCapturedTestResult>.ArtifactKind.get -> string?
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipeDirectoryNotWritableErrorMessage.get -> string!
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipePathTooLongErrorMessage.get -> string!
static Microsoft.Testing.Platform.Services.ArtifactNamingHelper.ResolveAndSanitize(string! template, string! processName, string! processId, System.DateTimeOffset timestamp, System.Func<string!, string!>! sanitizeLeafFileName) -> string!
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
#nullable enable
virtual Microsoft.Testing.Extensions.ReportGeneratorBase<TGenerator, TCapturedTestResult>.ArtifactKind.get -> string?
Microsoft.Testing.Extensions.JUnitReport.JUnitReportMerger
static Microsoft.Testing.Extensions.JUnitReport.JUnitReportMerger.Merge(System.Collections.Generic.IReadOnlyList<System.Xml.Linq.XDocument!>! inputReports, string! reportName) -> System.Xml.Linq.XDocument!
static Microsoft.Testing.Extensions.JUnitReport.JUnitReportMerger.MergeToFileAsync(System.Collections.Generic.IReadOnlyList<string!>! inputPaths, string! outputPath, string! reportName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipeDirectoryNotWritableErrorMessage.get -> string!
static Microsoft.Testing.Platform.Resources.PlatformResources.NamedPipePathTooLongErrorMessage.get -> string!
static Microsoft.Testing.Platform.Services.ArtifactNamingHelper.ResolveAndSanitize(string! template, string! processName, string! processId, System.DateTimeOffset timestamp, System.Func<string!, string!>! sanitizeLeafFileName) -> string!
Microsoft.Testing.Extensions.MergeOutputFileHelper
static Microsoft.Testing.Extensions.MergeOutputFileHelper.EnsureOutputDoesNotAliasInput(System.Collections.Generic.IReadOnlyList<string!>! inputPaths, string! outputPath) -> void
static Microsoft.Testing.Extensions.MergeOutputFileHelper.WriteViaTemporarySiblingAsync(string! outputPath, System.Func<string!, System.Threading.Tasks.Task!>! writeToTempAsync) -> System.Threading.Tasks.Task!
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public JUnitReportGenerator(IServiceProvider serviceProvider)

protected override string ArtifactDisplayName => ExtensionResources.JUnitReportArtifactDisplayName;

protected override string? ArtifactKind => "microsoft.testing.junit";

protected override string ArtifactDescription => ExtensionResources.JUnitReportArtifactDescription;

protected override string GetGenerationLogMessage(int testResultCount)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using Microsoft.Testing.Platform;

namespace Microsoft.Testing.Extensions.JUnitReport;

/// <summary>
/// Merges several already-produced JUnit XML reports into a single JUnit document.
/// </summary>
/// <remarks>
/// This is a pure, invocation-agnostic XML-level merge (no I/O, no clock) that mirrors the
/// approach used for TRX: a user-facing merge tool and an SDK-orchestrated post-processor can
/// share it and, given the same inputs and <c>reportName</c>, produce deterministic output.
/// <para>
/// Merge rules:
/// <list type="bullet">
/// <item><description>Every <c>&lt;testsuite&gt;</c> element is unioned as-is and re-assigned a sequential <c>id</c>. Both <c>&lt;testsuites&gt;</c>-rooted documents and bare <c>&lt;testsuite&gt;</c>-rooted documents are supported; any other root is skipped.</description></item>
/// <item><description>Root <c>tests</c>/<c>failures</c>/<c>errors</c>/<c>skipped</c>/<c>time</c> counters are derived by summing the per-suite counters, so they are correct even when an input's root aggregates are missing.</description></item>
/// <item><description>The root <c>timestamp</c> is the earliest across all merged suites.</description></item>
/// </list>
/// </para>
/// </remarks>
internal static class JUnitReportMerger
{
private const string RootElementName = "testsuites";
private const string SuiteElementName = "testsuite";

internal static XDocument Merge(IReadOnlyList<XDocument> inputReports, string reportName)
{
if (inputReports is null)
{
throw new ArgumentNullException(nameof(inputReports));
}

if (reportName is null)
{
throw new ArgumentNullException(nameof(reportName));
}

if (inputReports.Count == 0)
{
throw new ArgumentException("At least one JUnit report is required to merge.", nameof(inputReports));
}

long totalTests = 0;
long totalFailures = 0;
long totalErrors = 0;
long totalSkipped = 0;
double totalTime = 0;
DateTimeOffset? earliestTimestamp = null;

var mergedRoot = new XElement(RootElementName);
int suiteId = 0;

foreach (XDocument report in inputReports)
{
XElement? root = report.Root;
if (root is null)
{
continue;
}

// Support both <testsuites>-rooted documents and a bare <testsuite> root (a valid,
// common JUnit shape); any other root has no suites to contribute and is skipped.
IEnumerable<XElement> suites = string.Equals(root.Name.LocalName, RootElementName, StringComparison.Ordinal)
? root.Elements().Where(e => string.Equals(e.Name.LocalName, SuiteElementName, StringComparison.Ordinal))
: string.Equals(root.Name.LocalName, SuiteElementName, StringComparison.Ordinal)
? [root]
: [];

foreach (XElement suite in suites)
{
var clonedSuite = new XElement(suite);
clonedSuite.SetAttributeValue("id", suiteId++);
mergedRoot.Add(clonedSuite);

// Derive aggregates from the per-suite counters rather than trusting the (optional)
// root aggregates, so a merge cannot silently under-count.
totalTests += ReadLong(suite, "tests");
totalFailures += ReadLong(suite, "failures");
totalErrors += ReadLong(suite, "errors");
totalSkipped += ReadLong(suite, "skipped");
totalTime += ReadDouble(suite, "time");

if (TryReadTimestamp(suite, "timestamp", out DateTimeOffset timestamp)
&& (earliestTimestamp is null || timestamp < earliestTimestamp))
{
earliestTimestamp = timestamp;
}
}
}

mergedRoot.SetAttributeValue("name", reportName);
mergedRoot.SetAttributeValue("tests", totalTests.ToString(CultureInfo.InvariantCulture));
mergedRoot.SetAttributeValue("failures", totalFailures.ToString(CultureInfo.InvariantCulture));
mergedRoot.SetAttributeValue("errors", totalErrors.ToString(CultureInfo.InvariantCulture));
mergedRoot.SetAttributeValue("skipped", totalSkipped.ToString(CultureInfo.InvariantCulture));
mergedRoot.SetAttributeValue("time", totalTime.ToString("0.000", CultureInfo.InvariantCulture));
if (earliestTimestamp is { } stamp)
{
mergedRoot.SetAttributeValue("timestamp", stamp.UtcDateTime.ToString("yyyy-MM-ddTHH:mm:ss.fff", CultureInfo.InvariantCulture));
}

return new XDocument(new XDeclaration("1.0", "UTF-8", null), mergedRoot);
}

internal static async Task MergeToFileAsync(
IReadOnlyList<string> inputPaths,
string outputPath,
string reportName,
CancellationToken cancellationToken)
{
if (inputPaths is null)
{
throw new ArgumentNullException(nameof(inputPaths));
}

if (outputPath is null)
{
throw new ArgumentNullException(nameof(outputPath));
}

// Reject an empty input list before any filesystem work (Merge throws for empty input, but only
// after the output directory would already have been created).
if (inputPaths.Count == 0)
{
throw new ArgumentException("At least one JUnit report is required to merge.", nameof(inputPaths));
}

// RFC 018 treats per-module inputs as read-only and requires them to remain on disk; reject an
// output that aliases an input so a merge (which writes with a truncating File.Create) can never
// overwrite one of its own sources.
MergeOutputFileHelper.EnsureOutputDoesNotAliasInput(inputPaths, outputPath);

var reports = new List<XDocument>(inputPaths.Count);
foreach (string inputPath in inputPaths)
{
cancellationToken.ThrowIfCancellationRequested();
reports.Add(XDocument.Load(inputPath));
}

XDocument merged = Merge(reports, reportName);

string? outputDirectory = Path.GetDirectoryName(outputPath);
if (!RoslynString.IsNullOrEmpty(outputDirectory))
{
Directory.CreateDirectory(outputDirectory);
}

// Write to a temporary sibling, then replace the destination ENTRY, so a symlink/hardlink output
// alias of an input has only its link removed rather than the read-only source truncated in place.
await MergeOutputFileHelper.WriteViaTemporarySiblingAsync(outputPath, async tempPath =>
{
using FileStream stream = File.Create(tempPath);
#if NETCOREAPP
await merged.SaveAsync(stream, SaveOptions.None, cancellationToken).ConfigureAwait(false);
#else
merged.Save(stream, SaveOptions.None);
await Task.CompletedTask.ConfigureAwait(false);
#endif
}).ConfigureAwait(false);
}

private static long ReadLong(XElement element, string attributeName)
=> long.TryParse(element.Attribute(attributeName)?.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out long value)
? value
: 0;

private static double ReadDouble(XElement element, string attributeName)
=> double.TryParse(element.Attribute(attributeName)?.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out double value)
? value
: 0;

private static bool TryReadTimestamp(XElement element, string attributeName, out DateTimeOffset result)
{
string? value = element.Attribute(attributeName)?.Value;
if (RoslynString.IsNullOrEmpty(value))
{
result = default;
return false;
}

return DateTimeOffset.TryParse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ This package extends Microsoft Testing Platform to produce JUnit XML test report
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameSanitizer.cs" Link="Helpers\ReportFileNameSanitizer.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\CapturedTestResultBase.cs" Link="Helpers\CapturedTestResultBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameValidator.cs" Link="Helpers\ReportFileNameValidator.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\MergeOutputFileHelper.cs" Link="Helpers\MergeOutputFileHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportEngineBase.cs" Link="Helpers\ReportEngineBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportGeneratorBase.cs" Link="Helpers\ReportGeneratorBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportProviderRegistration.cs" Link="Helpers\ReportProviderRegistration.cs" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
const Microsoft.Testing.Platform.IPC.FileArtifactMessageFieldsId.Kind = 7 -> ushort
const Microsoft.Testing.Platform.Helpers.EnvironmentVariableConstants.TESTINGPLATFORM_PIPE_DIRECTORY = "TESTINGPLATFORM_PIPE_DIRECTORY" -> string!
const Microsoft.Testing.Platform.IPC.NamedPipeServer.MaxUnixDomainSocketPathLengthInBytes = 103 -> int
static Microsoft.Testing.Platform.IPC.NamedPipeServer.EnsureDirectoryIsWritable(string! directory) -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#nullable enable
const Microsoft.Testing.Platform.IPC.FileArtifactMessageFieldsId.Kind = 7 -> ushort
const Microsoft.Testing.Platform.Helpers.EnvironmentVariableConstants.TESTINGPLATFORM_PIPE_DIRECTORY = "TESTINGPLATFORM_PIPE_DIRECTORY" -> string!
Microsoft.Testing.Platform.Helpers.ArgumentGuard
Microsoft.Testing.Platform.Helpers.PasteArguments
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#nullable enable
const Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxReportEngine.TrxArtifactKind = "microsoft.testing.trx" -> string!
const Microsoft.Testing.Platform.IPC.FileArtifactMessageFieldsId.Kind = 7 -> ushort
static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxReportEngine.Merge(System.Collections.Generic.IReadOnlyList<System.Xml.Linq.XDocument!>! inputReports, System.Guid runId, string! runName) -> System.Xml.Linq.XDocument!
static Microsoft.Testing.Extensions.TrxReport.Abstractions.TrxReportEngine.MergeToFileAsync(System.Collections.Generic.IReadOnlyList<string!>! inputPaths, string! outputPath, System.Guid runId, string! runName, System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.Task!
const Microsoft.Testing.Platform.Helpers.EnvironmentVariableConstants.TESTINGPLATFORM_PIPE_DIRECTORY = "TESTINGPLATFORM_PIPE_DIRECTORY" -> string!
Microsoft.Testing.Platform.Helpers.DisposeHelper
static Microsoft.Testing.Platform.Helpers.DisposeHelper.DisposeAsync(object? obj) -> System.Threading.Tasks.Task!
Expand All @@ -15,3 +19,6 @@ static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteExecutionS
static Microsoft.Testing.Platform.IPC.Serializers.BaseSerializer.WriteListPayload<T>(System.IO.Stream! stream, ushort fieldId, T[]? list, System.Action<System.IO.Stream!, T>! writeItem) -> void
const Microsoft.Testing.Platform.IPC.FailedTestResultMessageFieldsId.Expected = 10 -> ushort
const Microsoft.Testing.Platform.IPC.FailedTestResultMessageFieldsId.Actual = 11 -> ushort
Microsoft.Testing.Extensions.MergeOutputFileHelper
static Microsoft.Testing.Extensions.MergeOutputFileHelper.EnsureOutputDoesNotAliasInput(System.Collections.Generic.IReadOnlyList<string!>! inputPaths, string! outputPath) -> void
static Microsoft.Testing.Extensions.MergeOutputFileHelper.WriteViaTemporarySiblingAsync(string! outputPath, System.Func<string!, System.Threading.Tasks.Task!>! writeToTempAsync) -> System.Threading.Tasks.Task!
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportGeneratorCommandLineBase.cs" Link="Helpers\ReportGeneratorCommandLineBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportEngineBase.cs" Link="Helpers\ReportEngineBase.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameValidator.cs" Link="Helpers\ReportFileNameValidator.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\MergeOutputFileHelper.cs" Link="Helpers\MergeOutputFileHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileWriterHelper.cs" Link="Helpers\ReportFileWriterHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\TargetFrameworkMonikerHelper.cs" Link="Helpers\TargetFrameworkMonikerHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\PipeNameEnvironmentVariableProviderBase.cs" Link="Helpers\PipeNameEnvironmentVariableProviderBase.cs" />
Expand Down
Loading
Loading