Analysis of commit 916e3a0
Assignee: @copilot
Summary
CtrfReportGenerator, HtmlReportGenerator, and JUnitReportGenerator share nearly identical constructor signatures, field declarations, ConsumeAsync, OnTestSessionStartingAsync, and OnTestSessionFinishingAsync patterns — 180 duplicated lines across 10 clone pairs. A ReportEngineBase class already exists in src/Platform/SharedExtensionHelpers/ but the generators are not inheriting from it.
Duplication Details
Pattern: Triplicated Report Generator Infrastructure
- Severity: High
- Occurrences: 10 clone pairs, 180 duplicated lines (across 3 files of ~157–174 lines each)
- Locations:
src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportGenerator.cs (full file, 158 lines)
src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGenerator.cs (full file, 157 lines)
src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportGenerator.cs (full file, 174 lines)
Duplicated blocks include:
- Using directives (lines 4–18 in all three, 15 lines × 2 pairs)
- Field declarations + constructor (lines 20–66 in Ctrf/Html, 17+14 lines × 2 pairs)
ConsumeAsync implementation (lines 91–112 / 91–111, 25 lines × 2 pairs)
OnTestSessionStartingAsync + OnTestSessionFinishingAsync lifecycle methods (lines 104–155+, 25+13 lines × 2 pairs)
Code Sample (identical constructor pattern across all three generators):
// Appears in CtrfReportGenerator.cs:26-66, HtmlReportGenerator.cs:26-66, JUnitReportGenerator.cs:26-72
private readonly IConfiguration _configuration;
private readonly ICommandLineOptions _commandLineOptions;
private readonly IFileSystem _fileSystem;
private readonly ITestApplicationModuleInfo _testApplicationModuleInfo;
private readonly IMessageBus _messageBus;
private readonly IClock _clock;
private readonly IEnvironment _environment;
private readonly IOutputDevice _outputDevice;
private readonly ITestFramework _testFramework;
private readonly ITestApplicationProcessExitCode _testApplicationProcessExitCode;
private readonly ILogger<TGenerator> _logger;
private readonly List<CapturedTestResult> _tests = [];
private readonly bool _isEnabled;
private DateTimeOffset? _testStartTime;
Impact Analysis
- Maintainability: Adding a new constructor parameter (e.g. a new platform service) must be done in three places. The existing
ReportEngineBase in SharedExtensionHelpers covers the engine layer but not the generator (data consumer) layer.
- Bug Risk: Any behavioral change to
ConsumeAsync or the session lifecycle hooks must be replicated across all three generators; divergence is possible and hard to detect.
- Code Bloat: Approximately 50% of each generator file is boilerplate shared with the other two generators.
Refactoring Recommendations
-
Introduce ReportGeneratorBase in SharedExtensionHelpers
- Extract the shared field declarations, constructor,
ConsumeAsync, OnTestSessionStartingAsync, and the common part of OnTestSessionFinishingAsync into an abstract base class alongside the existing ReportEngineBase
- Path:
src/Platform/SharedExtensionHelpers/ReportGeneratorBase.cs
- Have
CtrfReportGenerator, HtmlReportGenerator, and JUnitReportGenerator extend it
- Estimated effort: 4–6 hours
- Benefits: DRY constructor; future platform-service additions require a single-line change
-
Template-method pattern for OnTestSessionFinishingAsync
- The outer structure (cancellation check,
_testStartTime guard, log message, engine instantiation, warning display, artifact publish) is identical; only the engine type, log message text, and resource strings differ
- The base class can implement the outer flow and call an abstract
CreateEngineAsync / GetArtifactDisplayName virtual pair
Implementation Checklist
Analysis Metadata
- Analyzed Files: 1003 C# source files
- Detection Method: jscpd semantic code analysis (threshold: minLines=6, minTokens=50)
- Commit: 916e3a0
- Analysis Date: 2026-06-13
🤖 Automated content by GitHub Copilot. Posted via a maintainer's GitHub token, so it appears under their account — the account owner did not write or approve this content personally. Generated by the Duplicate Code Detector workflow. · 734 AIC · ⌖ 14.1 AIC · [◷]( · ◷)
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 916e3a0
Assignee:
@copilotSummary
CtrfReportGenerator,HtmlReportGenerator, andJUnitReportGeneratorshare nearly identical constructor signatures, field declarations,ConsumeAsync,OnTestSessionStartingAsync, andOnTestSessionFinishingAsyncpatterns — 180 duplicated lines across 10 clone pairs. AReportEngineBaseclass already exists insrc/Platform/SharedExtensionHelpers/but the generators are not inheriting from it.Duplication Details
Pattern: Triplicated Report Generator Infrastructure
src/Platform/Microsoft.Testing.Extensions.CtrfReport/CtrfReportGenerator.cs(full file, 158 lines)src/Platform/Microsoft.Testing.Extensions.HtmlReport/HtmlReportGenerator.cs(full file, 157 lines)src/Platform/Microsoft.Testing.Extensions.JUnitReport/JUnitReportGenerator.cs(full file, 174 lines)Duplicated blocks include:
ConsumeAsyncimplementation (lines 91–112 / 91–111, 25 lines × 2 pairs)OnTestSessionStartingAsync+OnTestSessionFinishingAsynclifecycle methods (lines 104–155+, 25+13 lines × 2 pairs)Code Sample (identical constructor pattern across all three generators):
Impact Analysis
ReportEngineBaseinSharedExtensionHelperscovers the engine layer but not the generator (data consumer) layer.ConsumeAsyncor the session lifecycle hooks must be replicated across all three generators; divergence is possible and hard to detect.Refactoring Recommendations
Introduce
ReportGeneratorBaseinSharedExtensionHelpersConsumeAsync,OnTestSessionStartingAsync, and the common part ofOnTestSessionFinishingAsyncinto an abstract base class alongside the existingReportEngineBasesrc/Platform/SharedExtensionHelpers/ReportGeneratorBase.csCtrfReportGenerator,HtmlReportGenerator, andJUnitReportGeneratorextend itTemplate-method pattern for
OnTestSessionFinishingAsync_testStartTimeguard, log message, engine instantiation, warning display, artifact publish) is identical; only the engine type, log message text, and resource strings differCreateEngineAsync/GetArtifactDisplayNamevirtual pairImplementation Checklist
ReportEngineBaseto ensure it covers the needed field setReportGeneratorBasewith common fields, constructor, and lifecycle methodsCtrfReportGenerator,HtmlReportGenerator,JUnitReportGeneratorto extendReportGeneratorBaseAnalysis Metadata
Add this agentic workflows to your repo
To install this agentic workflow, run