Analysis of commit 1b64756
Assignee: @copilot
Summary
JUnitReportEngine.cs contains private copies of four functionalities already provided by the SharedExtensionHelpers library: file name sanitization (ReportFileNameSanitizer), target framework moniker resolution (TargetFrameworkMonikerHelper), template-based file name resolution (ReportFileNameHelper), and the retry timeout constant (ReportFileWriterHelper.FileWriteRetryTimeout). This duplication exists because JUnitReport.csproj does not link any SharedExtensionHelpers files, unlike the HtmlReport.csproj and TrxReport.csproj projects which link the same shared code.
Duplication Details
Pattern 1: ReplaceInvalidFileNameChars / IsInvalidFileNameChar / IsReservedFileName
- Severity: High
- Occurrences: 3 duplicate methods
- Locations:
src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportEngine.cs (lines 731–774)
src/Platform/SharedExtensionHelpers/ReportFileNameSanitizer.cs (lines 13–51) — authoritative source
The character set in IsInvalidFileNameChar is bit-for-bit identical:
// JUnitReportEngine.cs line 751 — DUPLICATE
private static bool IsInvalidFileNameChar(char c)
=> c is < ' ' or '"' or '<' or '>' or '|' or ':' or '*' or '?' or '\\' or '/' or '@' or '(' or ')' or '^' or ' ';
// ReportFileNameSanitizer.cs line 33–34 — AUTHORITATIVE
private static bool IsInvalidFileNameChar(char c)
=> c is < ' ' or '"' or '<' or '>' or '|' or ':' or '*' or '?' or '\\' or '/' or '@' or '(' or ')' or '^' or ' ';
Pattern 2: GetTargetFrameworkMoniker (identical to TargetFrameworkMonikerHelper)
- Severity: Medium
- Occurrences: Duplicated in full
- Locations:
src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportEngine.cs (lines 725–729)
src/Platform/SharedExtensionHelpers/TargetFrameworkMonikerHelper.cs (lines 10–13) — authoritative source
// JUnitReportEngine.cs (lines 725–729) — DUPLICATE
private static string GetTargetFrameworkMoniker()
=> TargetFrameworkParser.GetShortTargetFramework(
Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkDisplayName)
?? TargetFrameworkParser.GetShortTargetFramework(RuntimeInformation.FrameworkDescription)
?? "unknown";
// TargetFrameworkMonikerHelper.cs (lines 10–13) — AUTHORITATIVE
public static string GetTargetFrameworkMoniker()
=> TargetFrameworkParser.GetShortTargetFramework(Assembly.GetEntryAssembly()?.GetCustomAttribute<TargetFrameworkAttribute>()?.FrameworkDisplayName)
?? TargetFrameworkParser.GetShortTargetFramework(RuntimeInformation.FrameworkDescription)
?? "unknown";
Note: HtmlReportEngine.cs correctly calls TargetFrameworkMonikerHelper.GetTargetFrameworkMoniker().
Pattern 3: ResolveXmlFileName duplicates ReportFileNameHelper.ResolveAndSanitize
- Severity: Medium
- Occurrences: Inline copy of
ReportFileNameHelper.ResolveAndSanitize
- Locations:
src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportEngine.cs (lines 712–723) — inline copy
src/Platform/SharedExtensionHelpers/ReportFileNameHelper.cs (lines 28–37) — authoritative
// JUnitReportEngine.ResolveXmlFileName (lines 712–723) — duplicates ReportFileNameHelper logic
private string ResolveXmlFileName(string template)
{
string processName = ...;
string processId = ...;
Dictionary<string, string> replacements = ArtifactNamingHelper.GetStandardReplacements(processName, processId, _clock.UtcNow);
string resolved = ArtifactNamingHelper.ResolveTemplate(template, replacements);
string directoryPart = Path.GetDirectoryName(resolved) ?? string.Empty;
string sanitizedFileName = ReplaceInvalidFileNameChars(Path.GetFileName(resolved)); // calls local duplicate
return directoryPart.Length == 0 ? sanitizedFileName : Path.Combine(directoryPart, sanitizedFileName);
}
// HtmlReportEngine.ResolveHtmlFileName — correct (1 line delegation)
private string ResolveHtmlFileName(string template)
=> ReportFileNameHelper.ResolveAndSanitize(template, processName, processId, _clock.UtcNow);
Pattern 4: Hardcoded retry timeout vs. ReportFileWriterHelper.FileWriteRetryTimeout
JUnitReportEngine.WriteWithRetryAsync (line 158) uses TimeSpan.FromSeconds(5) hardcoded, while the correct constant is ReportFileWriterHelper.FileWriteRetryTimeout (also 5s today, but the hardcoded value won't track future changes).
Root Cause
JUnitReport.csproj links none of the SharedExtensionHelpers source files:
<!-- JUnitReport.csproj — missing these links that HtmlReport/TrxReport have: -->
<!-- <Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameSanitizer.cs" /> -->
<!-- <Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\TargetFrameworkMonikerHelper.cs" /> -->
<!-- <Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameHelper.cs" /> -->
<!-- <Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileWriterHelper.cs" /> -->
Impact Analysis
- Maintainability: The
IsInvalidFileNameChar character set defines which characters are forbidden in report file names. Any future additions (e.g., platform-specific invalid chars) must be applied in both ReportFileNameSanitizer.cs and JUnitReportEngine.cs, risking divergence.
- Bug Risk: The JUnit
ReplaceInvalidFileNameChars method differs subtly from ReportFileNameSanitizer.ReplaceInvalidFileNameChars in the reserved-name check implementation — it uses a manual string comparison while the shared version uses a regex. Future edge cases (e.g., CLOCK$ with extension) might behave differently.
- Code Bloat: ~50 lines of dead-weight duplicated code across 4 distinct helper patterns.
Refactoring Recommendations
-
Add SharedExtensionHelpers file links to JUnitReport.csproj
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameSanitizer.cs" Link="Helpers\ReportFileNameSanitizer.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\TargetFrameworkMonikerHelper.cs" Link="Helpers\TargetFrameworkMonikerHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileNameHelper.cs" Link="Helpers\ReportFileNameHelper.cs" />
<Compile Include="$(RepoRoot)src\Platform\SharedExtensionHelpers\ReportFileWriterHelper.cs" Link="Helpers\ReportFileWriterHelper.cs" />
Note: ReportFileNameHelper.cs also requires ReportFileNameSanitizer.cs (already above).
-
Simplify ResolveXmlFileName to delegate to ReportFileNameHelper.ResolveAndSanitize, matching HtmlReportEngine.ResolveHtmlFileName.
-
Update BuildDefaultFileName to call TargetFrameworkMonikerHelper.GetTargetFrameworkMoniker() and ReportFileNameSanitizer.ReplaceInvalidFileNameChars.
-
Replace hardcoded TimeSpan.FromSeconds(5) with ReportFileWriterHelper.FileWriteRetryTimeout.
-
Delete duplicate private methods from JUnitReportEngine.cs: ReplaceInvalidFileNameChars, IsInvalidFileNameChar, IsReservedFileName, GetTargetFrameworkMoniker.
Implementation Checklist
Analysis Metadata
- Analyzed Files:
JUnitReportEngine.cs, HtmlReportEngine.cs, ReportFileNameSanitizer.cs, TargetFrameworkMonikerHelper.cs, ReportFileNameHelper.cs, ReportFileWriterHelper.cs, JUnitReport.csproj, HtmlReport.csproj
- Detection Method: Semantic code analysis — cross-file body comparison and project reference audit
- Commit: 1b64756
- Analysis Date: 2026-06-09
Generated by Duplicate Code Detector · sonnet46 3.6M · ◷
Add this agentic workflows to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
Analysis of commit 1b64756
Assignee:
@copilotSummary
JUnitReportEngine.cscontains private copies of four functionalities already provided by theSharedExtensionHelperslibrary: file name sanitization (ReportFileNameSanitizer), target framework moniker resolution (TargetFrameworkMonikerHelper), template-based file name resolution (ReportFileNameHelper), and the retry timeout constant (ReportFileWriterHelper.FileWriteRetryTimeout). This duplication exists becauseJUnitReport.csprojdoes not link anySharedExtensionHelpersfiles, unlike theHtmlReport.csprojandTrxReport.csprojprojects which link the same shared code.Duplication Details
Pattern 1:
ReplaceInvalidFileNameChars/IsInvalidFileNameChar/IsReservedFileNamesrc/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportEngine.cs(lines 731–774)src/Platform/SharedExtensionHelpers/ReportFileNameSanitizer.cs(lines 13–51) — authoritative sourceThe character set in
IsInvalidFileNameCharis bit-for-bit identical:Pattern 2:
GetTargetFrameworkMoniker(identical toTargetFrameworkMonikerHelper)src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportEngine.cs(lines 725–729)src/Platform/SharedExtensionHelpers/TargetFrameworkMonikerHelper.cs(lines 10–13) — authoritative sourceNote:
HtmlReportEngine.cscorrectly callsTargetFrameworkMonikerHelper.GetTargetFrameworkMoniker().Pattern 3:
ResolveXmlFileNameduplicatesReportFileNameHelper.ResolveAndSanitizeReportFileNameHelper.ResolveAndSanitizesrc/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportEngine.cs(lines 712–723) — inline copysrc/Platform/SharedExtensionHelpers/ReportFileNameHelper.cs(lines 28–37) — authoritativePattern 4: Hardcoded retry timeout vs.
ReportFileWriterHelper.FileWriteRetryTimeoutJUnitReportEngine.WriteWithRetryAsync(line 158) usesTimeSpan.FromSeconds(5)hardcoded, while the correct constant isReportFileWriterHelper.FileWriteRetryTimeout(also5stoday, but the hardcoded value won't track future changes).Root Cause
JUnitReport.csprojlinks none of theSharedExtensionHelperssource files:Impact Analysis
IsInvalidFileNameCharcharacter set defines which characters are forbidden in report file names. Any future additions (e.g., platform-specific invalid chars) must be applied in bothReportFileNameSanitizer.csandJUnitReportEngine.cs, risking divergence.ReplaceInvalidFileNameCharsmethod differs subtly fromReportFileNameSanitizer.ReplaceInvalidFileNameCharsin the reserved-name check implementation — it uses a manual string comparison while the shared version uses a regex. Future edge cases (e.g.,CLOCK$with extension) might behave differently.Refactoring Recommendations
Add
SharedExtensionHelpersfile links toJUnitReport.csprojNote:
ReportFileNameHelper.csalso requiresReportFileNameSanitizer.cs(already above).Simplify
ResolveXmlFileNameto delegate toReportFileNameHelper.ResolveAndSanitize, matchingHtmlReportEngine.ResolveHtmlFileName.Update
BuildDefaultFileNameto callTargetFrameworkMonikerHelper.GetTargetFrameworkMoniker()andReportFileNameSanitizer.ReplaceInvalidFileNameChars.Replace hardcoded
TimeSpan.FromSeconds(5)withReportFileWriterHelper.FileWriteRetryTimeout.Delete duplicate private methods from
JUnitReportEngine.cs:ReplaceInvalidFileNameChars,IsInvalidFileNameChar,IsReservedFileName,GetTargetFrameworkMoniker.Implementation Checklist
<Compile>links toJUnitReport.csproj(see Recommendation 1)ResolveXmlFileNamebody withReportFileNameHelper.ResolveAndSanitizecallGetTargetFrameworkMoniker()call inBuildDefaultFileNamewithTargetFrameworkMonikerHelper.GetTargetFrameworkMoniker()ReplaceInvalidFileNameChars(raw)call inBuildDefaultFileNamewithReportFileNameSanitizer.ReplaceInvalidFileNameChars(raw)TimeSpan.FromSeconds(5)inWriteWithRetryAsyncwithReportFileWriterHelper.FileWriteRetryTimeoutReplaceInvalidFileNameChars,IsInvalidFileNameChar,IsReservedFileName,GetTargetFrameworkMonikerAnalysis Metadata
JUnitReportEngine.cs,HtmlReportEngine.cs,ReportFileNameSanitizer.cs,TargetFrameworkMonikerHelper.cs,ReportFileNameHelper.cs,ReportFileWriterHelper.cs,JUnitReport.csproj,HtmlReport.csprojAdd this agentic workflows to your repo
To install this agentic workflow, run