Summary
ArtifactNamingService.ResolveFileName (new, in Microsoft.Testing.Platform) and the ReportEngineBase method that delegates to ReportFileNameHelper.ResolveAndSanitize (in SharedExtensionHelpers) implement the same algorithm: resolve template placeholders via ArtifactNamingHelper, split the result into directory and leaf file name, sanitize the leaf, and reassemble the path.
Duplication Details
Pattern: Near-identical template-resolve-and-sanitize pipelines in two layers
- Severity: Medium
- Occurrences: 3 closely related code paths across 3 files
- Locations:
src/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs — ResolveFileName (lines 21–38)
src/Platform/SharedExtensionHelpers/ReportFileNameHelper.cs — ResolveAndSanitize (lines 28–38)
src/Platform/SharedExtensionHelpers/ReportEngineBase.cs — wrapper around ResolveAndSanitize (uses same processName/processId extraction pattern, lines ~60–72)
ArtifactNamingService.ResolveFileName:
string processName = Path.GetFileNameWithoutExtension(_testApplicationModuleInfo.GetCurrentTestApplicationFullPath());
string processId = _environment.ProcessId.ToString(CultureInfo.InvariantCulture);
Dictionary<string, string> replacements = ArtifactNamingHelper.GetStandardReplacements(processName, processId, _clock.UtcNow);
string resolved = ArtifactNamingHelper.ResolveTemplate(template, replacements);
string directoryPart = Path.GetDirectoryName(resolved) ?? string.Empty;
string leafName = Path.GetFileName(resolved);
string sanitized = ArtifactFileNameSanitizer.ReplaceInvalidFileNameChars(leafName);
return directoryPart.Length == 0 ? sanitized : Path.Combine(directoryPart, sanitized);
ReportFileNameHelper.ResolveAndSanitize:
Dictionary<string, string> replacements = ArtifactNamingHelper.GetStandardReplacements(processName, processId, now);
string resolved = ArtifactNamingHelper.ResolveTemplate(template, replacements);
string directoryPart = Path.GetDirectoryName(resolved) ?? string.Empty;
string sanitizedFileName = ReportFileNameSanitizer.ReplaceInvalidFileNameChars(Path.GetFileName(resolved));
return directoryPart.Length == 0 ? sanitizedFileName : Path.Combine(directoryPart, sanitizedFileName);
The only structural differences are:
- The service receives processName/processId through DI (
ITestApplicationModuleInfo, IEnvironment), while the helper receives them as parameters.
- The service uses
ArtifactFileNameSanitizer; the helper uses ReportFileNameSanitizer (themselves duplicates — see related issue).
Impact Analysis
- Maintainability: Path-handling edge cases (e.g. invalid characters in directory portion, empty leaf name validation) must be kept in sync across two implementations. The
ArtifactNamingService adds a non-empty leaf name guard that ReportFileNameHelper lacks.
- Bug Risk: A change to template resolution or sanitization behavior needs to be reflected in both places.
- Code Bloat: ~15 lines of near-identical logic in two files.
Refactoring Recommendations
-
Delegate ReportFileNameHelper.ResolveAndSanitize to IArtifactNamingService
- Inject
IArtifactNamingService (already registered in the platform's DI container) into report engines that need template resolution, and remove ReportFileNameHelper.
- Requires passing the service through
ReportEngineContext or ReportEngineBase constructor, but the interface is already available in IServiceProvider.
- Estimated effort: Low-medium.
-
Extract a static ResolveAndSanitizeCore helper into ArtifactNamingHelper
- Add a static overload that accepts processName/processId/timestamp and returns a sanitized resolved path.
- Both
ArtifactNamingService and ReportFileNameHelper delegate to it.
- This is a smaller, less invasive change.
Implementation Checklist
Analysis Metadata
- Analyzed Files: 3
- Detection Method: Semantic code analysis
- Commit: c66515a
- Analysis Date: 2026-07-11
🤖 Automated content by GitHub Copilot. Generated by the Duplicate Code Detector workflow. · 113.6 AIC · ⌖ 6.58 AIC · ⊞ 8K · [◷]( · ◷)
Add this agentic workflows to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
Summary
ArtifactNamingService.ResolveFileName(new, inMicrosoft.Testing.Platform) and theReportEngineBasemethod that delegates toReportFileNameHelper.ResolveAndSanitize(inSharedExtensionHelpers) implement the same algorithm: resolve template placeholders viaArtifactNamingHelper, split the result into directory and leaf file name, sanitize the leaf, and reassemble the path.Duplication Details
Pattern: Near-identical template-resolve-and-sanitize pipelines in two layers
src/Platform/Microsoft.Testing.Platform/Services/ArtifactNamingService.cs—ResolveFileName(lines 21–38)src/Platform/SharedExtensionHelpers/ReportFileNameHelper.cs—ResolveAndSanitize(lines 28–38)src/Platform/SharedExtensionHelpers/ReportEngineBase.cs— wrapper aroundResolveAndSanitize(uses same processName/processId extraction pattern, lines ~60–72)ArtifactNamingService.ResolveFileName:ReportFileNameHelper.ResolveAndSanitize:The only structural differences are:
ITestApplicationModuleInfo,IEnvironment), while the helper receives them as parameters.ArtifactFileNameSanitizer; the helper usesReportFileNameSanitizer(themselves duplicates — see related issue).Impact Analysis
ArtifactNamingServiceadds a non-empty leaf name guard thatReportFileNameHelperlacks.Refactoring Recommendations
Delegate
ReportFileNameHelper.ResolveAndSanitizetoIArtifactNamingServiceIArtifactNamingService(already registered in the platform's DI container) into report engines that need template resolution, and removeReportFileNameHelper.ReportEngineContextorReportEngineBaseconstructor, but the interface is already available inIServiceProvider.Extract a static
ResolveAndSanitizeCorehelper intoArtifactNamingHelperArtifactNamingServiceandReportFileNameHelperdelegate to it.Implementation Checklist
ArtifactFileNameSanitizer/ReportFileNameSanitizerfirst or in parallel)Analysis Metadata
Add this agentic workflows to your repo
To install this agentic workflow, run