Summary
Seven *Manager classes in Microsoft.Testing.Platform each independently implement the same 5-step "instantiate → validate unique → check enabled → initialize → register" loop for building platform extensions. The loop body is structurally identical across ~12 methods and 7 files; only the factory delegate type and the list element type differ.
Duplication Details
Pattern: Extension build loop — ~12 independent implementations
The core template (varying only the type parameter T):
foreach (Func<IServiceProvider, T> factory in _factories)
{
T service = factory(serviceProvider); // step 1: instantiate
existingList.ValidateUniqueExtension(service); // step 2: duplicate check
if (await service.IsEnabledAsync().ConfigureAwait(false)) // step 3: enabled guard
{
await service.TryInitializeAsync().ConfigureAwait(false); // step 4: initialize
existingList.Add(service); // step 5: register
}
}
Files and approximate line ranges:
| File |
Method |
Lines |
TestHost/TestHostManager.cs |
BuildTestApplicationLifecycleCallbackAsync |
97–114 |
TestHost/TestHostManager.cs |
BuildDataConsumersAsync (simple factory path) |
138–153 |
TestHost/TestHostManager.cs |
BuildTestSessionLifetimeHandleAsync (simple path) |
233–248 |
TestHostControllers/TestHostControllersManager.cs |
BuildAsync (env-var handlers) |
158–174 |
TestHostControllers/TestHostControllersManager.cs |
BuildAsync (lifetime handlers) |
215–231 |
TestHostControllers/TestHostControllersManager.cs |
BuildTestHostLauncherAsync |
337–351 |
TestHostOrchestrator/TestHostOrchestratorManager.cs |
BuildAsync (main) |
41–58 |
TestHostOrchestrator/TestHostOrchestratorManager.cs |
BuildTestHostOrchestratorApplicationLifetimesAsync |
69–87 |
CommandLine/CommandLineManager.cs |
BuildAsync |
43–57 |
Configurations/ConfigurationManager.cs |
BuildAsync |
29–47 |
Tools/ToolsManager.cs |
BuildAsync |
18–29 |
Logging/LoggingManager.cs |
BuildAsync |
20–31 |
Composite-factory variant — 5 more identical copies (~25 lines each)
A longer variant that additionally handles CompositeExtensionFactory<T> (clone-or-reuse-singleton logic, alreadyBuiltServices.Add(), and a type-check-or-throw) is also duplicated ~5 times inside TestHostManager.cs (lines 155–196, 250–293) and TestHostControllersManager.cs (lines 176–213, 233–268, 272–313).
- Severity: High
- Occurrences: ~17 loop bodies across 7 files
- Total duplicated lines: ~300+
Impact Analysis
- Maintainability: Any change to the initialization protocol — adding a cancellation token to
TryInitializeAsync, reordering ValidateUniqueExtension vs IsEnabledAsync, injecting a timeout guard — must be applied to all 17 loop bodies consistently.
- Bug Risk: Protocol divergence already exists: some loops pass a
registrationOrder index, others do not. A missed update could silently produce a different ordering for one extension type.
- Code Bloat: The 7 manager classes each re-express the same algorithm, making the codebase harder to reason about for new contributors.
Refactoring Recommendations
- Extract a shared
BuildExtensionsAsync<T> helper (e.g., in a new ExtensionManagerHelper.cs internal to the platform) with signature:
internal static async Task BuildExtensionsAsync<T>(
IEnumerable<Func<IServiceProvider, T>> factories,
IServiceProvider serviceProvider,
List<T> result)
where T : IExtension
- Extract
BuildCompositeExtensionsAsync<T> for the composite-factory path, handling the clone-or-reuse-singleton logic once.
- Update all 7 manager classes to delegate to these helpers, reducing each
Build*Async method to a couple of lines.
Implementation Checklist
Analysis Metadata
- Analyzed Files: 7 manager files across
TestHost/, TestHostControllers/, TestHostOrchestrator/, CommandLine/, Configurations/, Tools/, Logging/
- Detection Method: Semantic code analysis
- Commit: 8609d3b
- Analysis Date: 2026-07-06
🤖 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. · 264.8 AIC · ⌖ 8.27 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
Seven
*Managerclasses inMicrosoft.Testing.Platformeach independently implement the same 5-step "instantiate → validate unique → check enabled → initialize → register" loop for building platform extensions. The loop body is structurally identical across ~12 methods and 7 files; only the factory delegate type and the list element type differ.Duplication Details
Pattern: Extension build loop — ~12 independent implementations
The core template (varying only the type parameter
T):Files and approximate line ranges:
TestHost/TestHostManager.csBuildTestApplicationLifecycleCallbackAsyncTestHost/TestHostManager.csBuildDataConsumersAsync(simple factory path)TestHost/TestHostManager.csBuildTestSessionLifetimeHandleAsync(simple path)TestHostControllers/TestHostControllersManager.csBuildAsync(env-var handlers)TestHostControllers/TestHostControllersManager.csBuildAsync(lifetime handlers)TestHostControllers/TestHostControllersManager.csBuildTestHostLauncherAsyncTestHostOrchestrator/TestHostOrchestratorManager.csBuildAsync(main)TestHostOrchestrator/TestHostOrchestratorManager.csBuildTestHostOrchestratorApplicationLifetimesAsyncCommandLine/CommandLineManager.csBuildAsyncConfigurations/ConfigurationManager.csBuildAsyncTools/ToolsManager.csBuildAsyncLogging/LoggingManager.csBuildAsyncComposite-factory variant — 5 more identical copies (~25 lines each)
A longer variant that additionally handles
CompositeExtensionFactory<T>(clone-or-reuse-singleton logic,alreadyBuiltServices.Add(), and a type-check-or-throw) is also duplicated ~5 times insideTestHostManager.cs(lines 155–196, 250–293) andTestHostControllersManager.cs(lines 176–213, 233–268, 272–313).Impact Analysis
TryInitializeAsync, reorderingValidateUniqueExtensionvsIsEnabledAsync, injecting a timeout guard — must be applied to all 17 loop bodies consistently.registrationOrderindex, others do not. A missed update could silently produce a different ordering for one extension type.Refactoring Recommendations
BuildExtensionsAsync<T>helper (e.g., in a newExtensionManagerHelper.csinternal to the platform) with signature:BuildCompositeExtensionsAsync<T>for the composite-factory path, handling the clone-or-reuse-singleton logic once.Build*Asyncmethod to a couple of lines.Implementation Checklist
BuildExtensionsAsync/BuildCompositeExtensionsAsynchelper APIsrc/Platform/Microsoft.Testing.Platform/Build*Asyncmethods to use the helpersAnalysis Metadata
TestHost/,TestHostControllers/,TestHostOrchestrator/,CommandLine/,Configurations/,Tools/,Logging/Add this agentic workflows to your repo
To install this agentic workflow, run