[duplicate-code] Duplicate `HasDuplicateModuleIdentity` logic between AzureDevOps and GitHub Actions reporters
Maintainers usually reply within 1 day
Assessment
This issue has not been assessed yet.
Description
Summary
The exact same module-identity-collision check is duplicated byte-for-byte between the Azure DevOps and GitHub Actions CI summary reporters, each operating on the already-shared CiRunSummaryModule type (src/Platform/SharedExtensionHelpers/CiRunSummaryModels.cs). The repo already has a precedent for extracting this kind of cross-reporter logic into src/Platform/SharedExtensionHelpers — see CiRunSummaryExitCode (added in #11970) which consolidated near-identical exit-code logic from GitHubActionsExitCode for reuse by the Azure DevOps reporter.
Duplication Details
Pattern: Identical HasDuplicateModuleIdentity private method
- Severity: Medium
- Occurrences: 2 (each called from 2–3 call sites within its own file, so the logic effectively has multiple internal users per project)
- Locations:
src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/AzureDevOpsSummaryReporter.Markdown.cs(lines 547-551), called from lines 466 and 528src/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/GitHubActionsSummaryReporter.Markdown.Aggregate.cs(lines 197-201), called from line 122
- Code Sample:
Both copies are identical character-for-character (verified withprivate 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;diff).
Impact Analysis
- Maintainability: Any future change to how module identity collisions are detected (e.g. adding a new discriminator field, changing comparison semantics) must be made in two places; it is easy to update one and forget the other, causing the two CI report flavors to silently disagree on when module attempt/session discriminators are shown.
- Bug Risk: Because both copies currently operate on the shared
CiRunSummaryModuletype with no reporter-specific variation, there is no reason for behavior to diverge — any divergence introduced later would be an unintentional bug rather than a deliberate design choice. - Code Bloat: Small (5 lines each), but it sets a precedent of re-deriving shared CI-summary logic per reporter instead of consolidating it, which compounds as more reporters (e.g. future CI providers) are added.
Refactoring Recommendations
- Move
HasDuplicateModuleIdentityintosrc/Platform/SharedExtensionHelpers- Add it as a new
internal staticmethod (e.g. aModuleIdentityorCiRunSummaryModuleIdentityhelper class, or append to the existingCiRunSummaryAggregation.cssince it already operates onCiRunSummaryModule/CiRunSummaryAggregate). - Mirror the pattern used for
CiRunSummaryExitCode(added in #11970): a single implementation guarded by#pragma warning disable RS0051since the helper compiles into multiple extension assemblies. - Update both
AzureDevOpsSummaryReporter.Markdown.csandGitHubActionsSummaryReporter.Markdown.Aggregate.csto call the shared helper instead of their private copies. - Estimated effort: low (under an hour); purely mechanical extraction with no behavior change.
- Benefits: single source of truth for module-identity-collision detection; consistent with the existing
CiRunSummaryExitCodeprecedent; removes duplicate code that must otherwise be kept in sync manually.
- Add it as a new
Implementation Checklist
- Review duplication findings
- Extract
HasDuplicateModuleIdentityintosrc/Platform/SharedExtensionHelpers - Update both reporters to call the shared helper
- Remove the two private copies
- Verify existing AzureDevOps and GitHub Actions summary reporter unit tests still pass unchanged (behavior-preserving refactor)
Analysis Metadata
- Analyzed Files: Recent commits touching
src/Platform/Microsoft.Testing.Extensions.AzureDevOpsReport/andsrc/Platform/Microsoft.Testing.Extensions.GitHubActionsReport/(commits around 88788a9eb, 95c503f07, 73a403f7f, e51893b06) - Detection Method: Semantic code analysis + exact diff comparison
- Commit: e51893b06f6a5c6faa3a702a8e9d78cd93c35ce9
🤖 Automated content by GitHub Copilot. Generated by the Duplicate Code Detector workflow. · copilot · auto · 123.1 AIC · ⌖ 6.64 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 20, 2026, 4:31 AM UTC
- Dominant language
- C#
- Stars
- 1.1k
- Forks
- 312
- Avg merge
- 8h 29m
- Merged PRs (30d)
- 421
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
-
type/automation type/tech-debt
Difficulty 4/5 3-5 days Newbie friendliness 35/100
Maintainers usually reply within 1 day
-
needs/triage
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 1 day
-
type/automation type/tech-debt
Difficulty 4/5 3-5 days Newbie friendliness 48/100
Maintainers usually reply within 1 day
-
agentic-workflows type/automation type/tech-debt
Difficulty 1/5 Under an hour Newbie friendliness 1/100
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
All issues in microsoft/testfx
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 66/100
gavin-wolfe/wolfden#104 ·
Maintainers usually reply within 1 day
-
[BUG] Still libmicrohttpd dependency is not removed properly from External-Dependency-Info.txt fileOpentype/bug
Difficulty 1/5 Under an hour Newbie friendliness 72/100
Maintainers usually reply within 1 day
-
[辞書] かんそうりつ->乾燥率Open辞書
Difficulty 1/5 Under an hour Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
bug good first issue
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
thomhurst/TUnit#7201 · 1 comment ·
Maintainers usually reply within 1 day
-
Difficulty 1/5 Under an hour Newbie friendliness 88/100
ArduPilot/MissionPlanner#3826 ·
Maintainers usually reply within 1 day