[duplicate-code] Extract duplicated `HasDuplicateModuleIdentity` helper from AzureDevOps and GitHubActions summary reporters
Maintainers usually reply within 1 day
Assessment
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Newbie friendliness
- 72/100
- Issue type
- Refactor
- Clarity
- Clearly specified
- Activity status
- Active
- Tech stack
- csharp
- Domain
- testing-qa
Research direction
Read the two duplicate implementations in AzureDevOpsSummaryReporter.Markdown.cs and GitHubActionsSummaryReporter.Markdown.Aggregate.cs, then check the existing shared helper pattern in SharedExtensionHelpers/CiRunSummaryExitCode.cs. Move the predicate into SharedExtensionHelpers and update both call sites. Build both reporter projects and run their existing unit tests; done means the duplicate methods are removed and behavior is unchanged.
Written by the indexing model from the issue text.
Description
Summary
The private helper method HasDuplicateModuleIdentity(IReadOnlyList<CiRunSummaryModule> modules, CiRunSummaryModule module) is implemented with byte-for-byte identical logic in two separate reporter projects that both already share compiled source via src/Platform/SharedExtensionHelpers/.
Duplication Details
Pattern: Identical module-identity-disambiguation predicate
-
Severity: Medium
-
Occurrences: 2 (exact duplicate, same signature and body)
-
Locations:
src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.Markdown.cs(lines 547-551)src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsSummaryReporter.Markdown.Aggregate.cs(lines 197-201)
-
Code Sample:
private static bool HasDuplicateModuleIdentity(IReadOnlyList<CiRunSummaryModule> modules, CiRunSummaryModule module) => modules.Count(candidate => string.Equals(candidate.AssemblyName, module.AssemblyName, StringComparison.Ordinal) && string.Equals(candidate.TargetFramework, module.TargetFramework, StringComparison.Ordinal) && string.Equals(candidate.Architecture, module.Architecture, StringComparison.OrdinalIgnoreCase)) > 1;
This exact text appears identically in both files, operating on the shared CiRunSummaryModule type defined in src/Platform/SharedExtensionHelpers/CiRunSummaryModels.cs, which both .csproj files already link via <Compile Include="...SharedExtensionHelpers\CiRunSummaryModels.cs" ...>.
Impact Analysis
- Maintainability: Any future change to the module-identity-disambiguation rule (e.g., adding a new discriminator field, changing comparison semantics) requires updating both reporters in lockstep. There is no compiler/analyzer safeguard forcing that synchronization today.
- Bug Risk: A fix or tweak applied to one copy (e.g., in a future PR touching only the AzureDevOps or only the GitHub Actions reporter) can silently diverge from the other, producing inconsistent "attempt N, session X" disambiguation behavior between the two CI summary formats.
- Code Bloat: Minor (5 lines), but representative of a broader pattern already being addressed in this codebase —
CiRunSummaryExitCode(PR #11970) and otherSharedExtensionHelpersfiles were introduced specifically to deduplicate logic shared by these two reporters.
Refactoring Recommendations
- Move
HasDuplicateModuleIdentityintoSharedExtensionHelpers- Extract common functionality to:
src/Platform/SharedExtensionHelpers/CiRunSummaryModule.Identity.cs(or add as a method onCiRunSummaryModels.csalongside theCiRunSummaryModuletype it operates on, following the same pattern asCiRunSummaryExitCode.cs) - Make it
internal staticin namespaceMicrosoft.Testing.Extensions(matchingCiRunSummaryExitCode's placement) so both linked projects pick it up without any.csprojchange (both already compile every file underSharedExtensionHelpers/). - Update both call sites to call the shared method instead of their private copies, and delete the two private duplicates.
- Estimated effort: ~15 minutes, very low complexity — no behavior change, pure move.
- Benefits: Single source of truth for module-identity-disambiguation; consistent with the existing
CiRunSummaryExitCodeprecedent in the same directory; removes the latent risk of silent divergence.
- Extract common functionality to:
Implementation Checklist
- Review duplication findings
- Move
HasDuplicateModuleIdentitytosrc/Platform/SharedExtensionHelpers/(e.g., as part ofCiRunSummaryModels.csor a new small file) - Update
AzureDevOpsSummaryReporter.Markdown.csto call the shared method and delete its private copy - Update
GitHubActionsSummaryReporter.Markdown.Aggregate.csto call the shared method and delete its private copy - Build both
Microsoft.Testing.Extensions.AzureDevOpsReportandMicrosoft.Testing.Extensions.GitHubActionsReportprojects to confirm no regressions - Run existing unit tests for both reporters to verify behavior is unchanged (this is a pure, behavior-preserving extraction)
Analysis Metadata
- Analyzed Files: Recent commits touching
src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/andsrc/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/(commits88788a9eb,96ad90a8a, and related history) - Detection Method: Semantic code analysis (method signature/body comparison across reporter projects that already share compiled source)
- Commit: e51893b06f6a5c6faa3a702a8e9d78cd93c35ce9
🤖 Automated content by GitHub Copilot. Generated by the Duplicate Code Detector workflow. · copilot · auto · 72.2 AIC · ⌖ 5.27 AIC · ⊞ 13.9K · [◷]( · ◷)
Add this agentic workflow to your repo
To install this agentic workflow, run
gh aw add githubnext/agentics/workflows/duplicate-code-detector.md@main
- expires on Oct 19, 2026, 4:26 AM UTC
- Dominant language
- C#
- Stars
- 1.1k
- Forks
- 312
- Avg merge
- 8h 33m
- Merged PRs (30d)
- 411
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from microsoft/testfx
-
agentic-workflows area/performance type/automation
Difficulty 4/5 3-5 days Newbie friendliness 15/100
Maintainers usually reply within 1 day
-
[duplicate-code] Duplicate `HasDuplicateModuleIdentity` logic between AzureDevOps and GitHub Actions reportersPossibly taken @Evangelink claimed this today. Opentype/automation type/tech-debt
microsoft/testfx#12000 · 1 reaction · 2 assignees ·
Maintainers usually reply within 1 day
-
[test-improver] Add unit tests for DotnetTestConnection.StartServerControlChannelAsync guard conditionsPossibly taken @Evangelink claimed this today. Openarea/mtp needs/triage
microsoft/testfx#11998 · 1 reaction · 2 assignees ·
Maintainers usually reply within 1 day
-
area/mtp-github-actions-report external/dotnet-test needs/triage
Difficulty 4/5 3-5 days Newbie friendliness 56/100
Maintainers usually reply within 1 day
-
Allow a trailing CancellationToken parameter on MSTest test methodsPossibly taken @Evangelink claimed this 2 days ago. Openarea/mstest area/parameterized-tests needs/triage
Difficulty 4/5 3-5 days Newbie friendliness 22/100
Maintainers usually reply within 1 day
All issues in microsoft/testfx
Similar issues
-
Backend enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
Maintainers usually reply within 1 day
-
UI: money shows the minus sign next to the number, not before the currency (IL -3.0m, not -IL 3.0m)Openarea: sim area: unity enhancement
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
eyalgolan/statecraft-israel#1107 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 83/100
dimitar-radenkov/Pointframe#271 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
qew21/Genshin-Subtitles#45 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
HoffmanEngineering/3d-print-log-api#274 ·
Maintainers usually reply within 1 day