Skip to content

[Prototype] Option C: read CLI options from testconfig.json via IConfiguration (#6349)#8664

Draft
Evangelink wants to merge 3 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/unify-config-option-c
Draft

[Prototype] Option C: read CLI options from testconfig.json via IConfiguration (#6349)#8664
Evangelink wants to merge 3 commits into
microsoft:mainfrom
Evangelink:dev/amauryleve/unify-config-option-c

Conversation

@Evangelink
Copy link
Copy Markdown
Member

Summary

Prototype for #6349: let users specify CLI options inside testconfig.json by routing ICommandLineOptions through IConfiguration.

This is a draft for review. It is not a finished feature — I'm opening it so we can look at the approach end-to-end and decide whether to invest in the rough edges listed below or move to a different design.

Approach (Option C – unified configuration view)

  1. New CommandLineConfigurationSource (Order = 0) wraps the parsed CLI into an IConfigurationProvider that flattens each option under commandLineOptions:<name> (zero-arity) or commandLineOptions:<name>:<index> (arg-bearing).
  2. The CLI source is registered in TestHostBuilder.CommonServices before the JSON source, so CLI keeps highest precedence.
  3. New AggregatedConfiguration.TryGetCommandLineOptionFromProviders does a provider-aware lookup: it walks providers in registration order, and the first provider with any data for that option wins outright (no per-key cross-provider merging). This avoids cases where IConfiguration[key] accidentally returns the JSON :0 while the CLI set the zero-arity bare key, or vice versa.
  4. The public extension helpers (IsCommandLineOptionSet, TryGetCommandLineOptionArguments) delegate to that method when given an AggregatedConfiguration, and CommandLineHandler becomes a thin facade that does the same. End result: every consumer of ICommandLineOptions transparently sees JSON-sourced options.

What works

  • testconfig.json keys under commandLineOptions are surfaced everywhere ICommandLineOptions is consumed.
  • CLI still wins over JSON for the same option.
  • --results-directory defined in JSON is honored (was previously ignored — the legacy GetResultsDirectoryCore path was bypassing JSON).
  • Back-compat ctor on CommandLineHandler is kept so external code keeps compiling.

Rough edges I'd like to discuss before going further

These are documented in the code with OPTION C LIMITATION / OPTION C TODO comments — I deliberately did not fix them in this PR because I want a design call first:

  1. Validator gap. CommandLineOptionsValidator walks parseResult.Options only. Options set exclusively via JSON bypass arity, per-option, and "unknown option" validation. Concrete crash sites I found: --timeout does args[0] without a length check, --exit-on-process-exit does int.Parse on args[0]. Fixing this means giving the validator a unified view too (probably moving it after IConfiguration is built).
  2. Bootstrap readers. --diagnostic*, --config-file, --no-banner are read off parseResult before IConfiguration is built. Honoring them from JSON requires either a two-phase config build or rewriting those bootstrap paths.
  3. Scalar bool ambiguity. For arg-bearing options, JSON "true" / "false" is ambiguous with the zero-arity presence marker. I documented that callers must use the array form ("foo": ["true"]) for non-boolean string values that happen to be "true" / "false". No code enforces this today.
  4. No schema enforcement. Nothing validates that keys under commandLineOptions:* correspond to real registered options or have the right arity.
  5. API visibility. New helpers are kept internal for now. If we ship Option C we need to decide whether they become public surface.

A hybrid B→C follow-up plan exists outside the repo (option B = explicit per-feature JSON keys for the highest-impact options, option C = generic unified view for the rest). Happy to share if useful.

Tests

  • CommandLineConfigurationProviderTests covers the source/provider flattening + the provider-aware invariants (including ProviderAwareResolution_CliZeroArityShadowsJsonIndexedArgs end-to-end, plus two in-memory provider tests added in the latest review round).
  • CommandLineConfigurationExtensionsTests covers the helper-level and handler-end-to-end behavior.
  • Full UT project green on net8.0 / net9.0 / net462.

Why draft?

Looking for a thumbs-up on the shape of this approach (unified IConfiguration view) before I invest in (1)–(4). If you'd rather take a more explicit per-feature path (Option B) for the most-requested options, I can switch direction without much waste — most of the JSON binding plumbing is reusable either way.

Evangelink and others added 3 commits May 28, 2026 16:29
…rosoft#6349)

Introduces a CLI-backed IConfigurationSource (Order=0) so values parsed from the command line, env vars, and testconfig.json all flow through the same IConfiguration. CommandLineHandler becomes a facade over IConfiguration when one is supplied so every existing ICommandLineOptions consumer transparently sees JSON-sourced options.

This is a prototype to highlight rough edges:

- CommandLineOptionsValidator still walks parseResult.Options only (arity / per-option / unknown-option detection skips JSON-only options); TODOs added in place.

- Bootstrap-time readers (TestApplication diagnostic plumbing, --config-file, --no-banner) read parseResult before IConfiguration is built.

- IConfiguration only exposes string?; multi-value reads walk indexed keys (commandLineOptions:<name>:<index>) which the JSON parser already produces for arrays.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cation

Review-round fixes:

- Add AggregatedConfiguration.TryGetCommandLineOptionFromProviders that resolves command-line options at the option granularity by walking providers in registration order. The first provider with any data for the option wins outright, so a CLI zero-arity flag is no longer silently merged with JSON indexed arguments.

- Route ConfigurationExtensions.IsCommandLineOptionSet / TryGetCommandLineOptionArguments through the new provider-aware path when the IConfiguration is an AggregatedConfiguration; keep the merged-view fallback for test mocks.

- Update AggregatedConfiguration.GetResultsDirectoryCore to consult the unified command-line view first so JSON-supplied results-directory is honored.

- Strengthen tests: rewrite the precedence test to use a shared storage key (a JSON array) so a flipped Order would actually fail it; add ProviderAwareResolution_CliZeroArityShadowsJsonIndexedArgs end-to-end test that locks in the new behavior through the CommandLineHandler facade.

- Expand validator TODOs with the concrete runtime crash sites surfaced during review (timeout args[0], exit-on-process-exit int.Parse).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…directory

Review-round-2 fixes:
- AggregatedConfiguration.GetResultsDirectoryCore now checks for the
  CommandLineConfigurationProvider before going through the unified path.
  Without it (legacy hand-built AggregatedConfiguration), the parseResult
  fallback runs first so a custom provider cannot silently demote CLI
  precedence for --results-directory.
- Document the provider contract for the commandLineOptions section directly
  on TryGetCommandLineOptionFromProviders: TryGet returning true with a null
  value is treated as absent at this provider, and indexed entries must be
  contiguous from :0.
- Add focused in-memory provider tests that lock the provider-aware
  invariants without requiring a JSON file:
  * FirstProviderWithDataShadowsLaterProvidersForSameOption
  * ExplicitDisableAtFirstProviderShortCircuits

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 28, 2026 14:57
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This prototype routes command-line options through the platform IConfiguration model so testconfig.json can provide values consumed by existing ICommandLineOptions users, while preserving CLI precedence.

Changes:

  • Adds a CLI-backed configuration source/provider under commandLineOptions:*.
  • Adds provider-aware command-line option lookup helpers on AggregatedConfiguration/ConfigurationExtensions.
  • Wires CommandLineHandler to use the unified configuration view and adds unit coverage for precedence and lookup behavior.
Show a summary per file
File Description
src/Platform/Microsoft.Testing.Platform/Hosts/TestHostBuilder.CommonServices.cs Registers the CLI configuration source and passes built configuration into command-line handling.
src/Platform/Microsoft.Testing.Platform/Configurations/PlatformConfigurationConstants.cs Adds the commandLineOptions section constant.
src/Platform/Microsoft.Testing.Platform/Configurations/ConfigurationExtensions.cs Adds unified command-line option lookup helpers.
src/Platform/Microsoft.Testing.Platform/Configurations/CommandLineConfigurationSource.cs Adds the configuration source for parsed CLI options.
src/Platform/Microsoft.Testing.Platform/Configurations/CommandLineConfigurationProvider.cs Flattens parsed CLI options into configuration keys.
src/Platform/Microsoft.Testing.Platform/Configurations/AggregatedConfiguration.cs Adds provider-aware option resolution and uses it for results-directory.
src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineOptionsValidator.cs Documents current validation gaps for JSON-sourced options.
src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineManager.cs Passes configuration into CommandLineHandler.
src/Platform/Microsoft.Testing.Platform/CommandLine/CommandLineHandler.cs Delegates option reads to configuration when available.
test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/CommandLineConfigurationProviderTests.cs Adds tests for CLI provider flattening and precedence behavior.
test/UnitTests/Microsoft.Testing.Platform.UnitTests/Configuration/CommandLineConfigurationExtensionsTests.cs Adds tests for helper and handler behavior over configuration-backed options.

Copilot's findings

  • Files reviewed: 11/11 changed files
  • Comments generated: 1

Comment on lines +232 to +239
=> _configuration is not null
? _configuration.IsCommandLineOptionSet(optionName)
: ParseResult.IsOptionSet(optionName);

public bool TryGetOptionArgumentList(string optionName, [NotNullWhen(true)] out string[]? arguments)
=> _configuration is not null
? _configuration.TryGetCommandLineOptionArguments(optionName, out arguments)
: ParseResult.TryGetOptionArgumentList(optionName, out arguments);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants