Skip to content

[duplicate-code] Duplicate RunSettings providers: MSTest mirrors VSTestBridge implementations #9875

Description

@github-actions

Analysis of commit fa9bbfa

Assignee: @copilot

Summary

Commit fa9bbfa introduced three new MSTest-native RunSettings provider classes that are near-identical copies of their counterparts in Microsoft.Testing.Extensions.VSTestBridge. The duplication was intentional (to avoid a VSTestBridge assembly dependency) but represents ~180 lines of structural duplication across 3 file pairs, each differing only in namespace, class name, and resource string references.

Duplication Details

Pattern: Mirrored RunSettings Provider Trio

  • Severity: Medium
  • Occurrences: 3 file pairs (~360 lines total, ~180 lines structurally duplicated)
  • Locations:
MSTest-native (new) VSTestBridge original
src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestRunSettingsCommandLineOptionsProvider.cs (47 lines) src/Platform/Microsoft.Testing.Extensions.VSTestBridge/CommandLine/RunSettingsCommandLineOptionsProvider.cs (52 lines)
src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestRunSettingsConfigurationProvider.cs (74 lines) src/Platform/Microsoft.Testing.Extensions.VSTestBridge/Configurations/RunSettingsConfigurationProvider.cs (67 lines)
src/Adapter/MSTest.TestAdapter/TestingPlatformAdapter/MSTestRunSettingsEnvironmentVariableProvider.cs (65 lines) src/Platform/Microsoft.Testing.Extensions.VSTestBridge/TestHostControllers/RunSettingsEnvironmentVariableProvider.cs (55 lines)

Code Sample (CommandLineOptionsProvider pair — nearly identical):

// VSTestBridge version
internal sealed class RunSettingsCommandLineOptionsProvider : CommandLineOptionsProviderBase
{
    public const string RunSettingsOptionName = "settings";
    private readonly IFileSystem _fileSystem;

    public RunSettingsCommandLineOptionsProvider(IExtension extension)
        : this(extension, new SystemFileSystem()) { }

    internal RunSettingsCommandLineOptionsProvider(IExtension extension, IFileSystem fileSystem)
        : base(extension, [new CommandLineOption(RunSettingsOptionName, ExtensionResources.RunSettingsOptionDescription, ArgumentArity.ExactlyOne, false)])
        => _fileSystem = fileSystem;

    public override Task<ValidationResult> ValidateOptionArgumentsAsync(CommandLineOption commandOption, string[] arguments)
    {
        string filePath = arguments[0];
        if (!_fileSystem.ExistFile(filePath))
            return ValidationResult.InvalidTask(string.Format(..., ExtensionResources.RunsettingsFileDoesNotExist, filePath));
        if (!RunSettingsProviderHelper.CanReadFile(_fileSystem, filePath))
            return ValidationResult.InvalidTask(string.Format(..., ExtensionResources.RunsettingsFileCannotBeRead, filePath));
        return ValidationResult.ValidTask;
    }
}

// MSTest-native version — structurally identical, different class name and resource strings
internal sealed class MSTestRunSettingsCommandLineOptionsProvider : CommandLineOptionsProviderBase
{
    // ... identical structure, uses PlatformAdapterResources instead of ExtensionResources
}

The same pattern repeats for ConfigurationProvider and EnvironmentVariableProvider.

Impact Analysis

  • Maintainability: Any bug fix or enhancement to the VSTestBridge providers (e.g., new validation logic, new XPath query in TryGet) must also be manually applied to the three MSTest-native copies.
  • Bug Risk: High — divergence is already visible: ConfigurationProvider uses string.IsNullOrEmpty while the bridge uses RoslynString.IsNullOrEmpty; constructor signatures differ slightly.
  • Code Bloat: ~180 lines of logic duplicated across two assemblies.

Refactoring Recommendations

  1. Extract to SharedExtensionHelpers (preferred)

    • The project src/Platform/SharedExtensionHelpers/ already hosts RunSettingsProviderHelper.cs shared between VSTestBridge and MSTest.
    • Move the base/core logic for CommandLineOptionsProvider, ConfigurationProvider, and EnvironmentVariableProvider into abstract or static helper classes in SharedExtensionHelpers.
    • Both the VSTestBridge and MSTest assemblies can then derive/call these shared helpers.
    • Estimated effort: Medium (2–4 hours).
    • Benefits: Single place to fix bugs, consistent behavior, smaller binary size.
  2. Make VSTestBridge providers internal protected / expose via shared contract

    • If the VSTestBridge assembly is already referenced (or can be referenced) by MSTest.TestAdapter at build time only, expose the providers as internal with InternalsVisibleTo.
    • Estimated effort: Low (1 hour) — but requires verifying no circular dependency.
  3. Accept duplication with a sync test (lowest risk)

    • Add a Roslyn analyzer or a unit test that asserts the two sets of providers remain in sync (same validation logic).
    • Estimated effort: Low (1–2 hours).
    • Benefits: Catches divergence early without requiring a risky refactor.

Implementation Checklist

  • Review duplication findings
  • Decide on preferred approach (shared helpers vs. test-enforced sync vs. accept duplication)
  • Implement chosen approach
  • Verify both VSTestBridge and MSTest-native paths work correctly
  • Update/add tests
  • Verify no functionality broken

Analysis Metadata

  • Analyzed Files: 6 source files
  • Detection Method: Semantic code analysis + structural diff
  • Commit: fa9bbfa
  • Analysis Date: 2026-07-13

🤖 Automated content by GitHub Copilot. Generated by the Duplicate Code Detector workflow. · 50.3 AIC · ⌖ 5.35 AIC · ⊞ 8K · [◷]( · ◷)

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 Jul 15, 2026, 5:22 AM UTC

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    type/automationCreated or maintained by an agentic workflow.type/tech-debtCode health, refactoring, simplification.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions