Uh oh!
There was an error while loading. Please reload this page.
Read CLI options from testconfig.json via IConfiguration (#6349) - #8664
Conversation
…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>
There was a problem hiding this comment.
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
CommandLineHandlerto 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
Uh oh!
There was an error while loading. Please reload this page.
1eaacbf to
dad27c1CompareUh oh!
There was an error while loading. Please reload this page.
Amaury Levé (Evangelink)
left a comment
There was a problem hiding this comment.
Round-2 review summary
All four round-1 fixes are correctly applied and the build is clean (0 warnings, 0 errors; 1034 pass / 0 fail). Two minor correctness observations and three test-hygiene NITs left inline. Nothing blocking.
Fix validation
| Fix | Status |
|---|---|
Case-insensitive duplicate detection (3 dicts + ToDictionary) | ✅ Applied correctly. One remaining edge case at validator.cs:62 — see inline. |
FormatException catch around EnumerateJsonCommandLineOptions | ✅ Mirrors the standard failure path, no double-banner risk. One asymmetry re HasTool — see inline. |
[DoesNotReturn] on the two throw helpers | ✅ Both helpers carry the attribute. |
Rewritten XML doc on TryGetCommandLineOptionArguments | ✅ No more stale "validation on parseResult only" wording. |
New test adequacy
| Test | Verdict |
|---|---|
EnumerateCommandLineOptions_SectionNameCaseInsensitive_Honored | ✅ Strong assertions (option name + argument value). |
Validator_EmptyOptionNameInJson_FailsWithJsonPrefix | "testconfig.json" substring — wouldn't catch a resource string change. Advisory. |
Validator_DuplicateOptionNamesDifferingByCase_FailsGracefully | IsValid == false. See inline. |
Validator_JsonSparseIndexedEntry_DefensiveSchemaRejection |
Public API hygiene
✅ PublicAPI.Unshipped.txt only has #nullable enable — all new types (JsonCommandLineOptionEntry, CommandLineConfigurationSource, CommandLineConfigurationProvider) and methods (EnumerateJsonCommandLineOptions, TryGetCommandLineOptionArguments, IsCommandLineOptionSet) are correctly internal.
✅ JsonCommandLineOptionEntry uses plain get; properties — no init accessors on any new API.
✅ New internalCommandLineHandler constructor accepting IConfiguration? preserves source/binary compatibility (the existing public constructor delegates with configuration: null).
Summary table
| # | Dimension | Verdict |
|---|---|---|
| 1 | Algorithmic Correctness | 🟡 2 LOW (system+system dictionary, FormatException + HasTool) |
| 13 | Test Completeness & Coverage | ⚪ 1 NIT (weaker substring assertions on 3 new tests) |
| 16 | Naming & Conventions | ⚪ 1 NIT (Validator_JsonSparseIndexedEntry_* name) |
| 17 | Documentation Accuracy | ⚪ 1 NIT (test comment misrepresents pre-fix behavior) |
✅ 17/21 dimensions clean.
Threading & Concurrency, Security & IPC, Public API & Binary Compatibility, Performance & Allocations, Cross-TFM Compatibility, Resource & IDisposable, Defensive Coding at Boundaries, Localization, Test Isolation, Assertion Quality, Flakiness Patterns, Data-Driven Coverage, Code Structure, Analyzer Quality (N/A), IPC Wire Compatibility (N/A), Build Infrastructure, Scope Discipline — all clean.
Noted for follow-up (out-of-scope for this PR)
- Env-var injection of
commandLineOptions:*keys: now thatIsCommandLineOptionSet/TryGetCommandLineOptionArgumentsroute throughIConfiguration, an env var spelledcommandLineOptions__timeout=30s(with__→:normalization inEnvironmentVariablesConfigurationProvider) will silently shadow JSON values and be invisible toCommandLineOptionsValidator(which only sees CLI + JSON entries). This is an existing platform concern that the unification only exposes — not introduced here — but worth tracking. - Behavioral break for extension authors who intentionally registered case-differing option names (e.g.
"Timeout"for one provider,"timeout"for another). They were previously accepted as distinct (silently mis-treated as one by every downstream case-insensitive lookup); they now fail validation up front. This is the correct fix, but worth a CHANGELOG entry if not already planned.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
dad27c1 to
bcb7973CompareAmaury Levé (Evangelink)
commented
Jun 3, 2026
Round 3 review — |
This is a follow-up to the unified ICommandLineOptions/IConfiguration work that addresses two gaps surfaced during review. * Validator gap. CommandLineOptionsValidator walks parseResult.Options only. Options set exclusively via testconfig.json bypass arity, per-option, and unknown-option validation, so typos can crash deep inside option handlers (e.g. --timeout IndexOutOfRange, --exit-on-process-exit FormatException). JsonConfigurationProvider now exposes a typed, schema-validated enumeration of commandLineOptions entries (scalar / true / false / scalar-array, anything else fails fast with FormatException). CommandLineOptionsValidator runs three extra passes over those entries: unknown-option detection, arity check, and per-arg validation - so testconfig.json typos surface during startup instead of crashing later. Option-name dictionaries now use OrdinalIgnoreCase to match JSON's case-insensitive storage. * --no-banner read. DisplayBannerIfEnabledAsync used to read off the raw parseResult, so noBanner: true in testconfig.json was ignored. It now takes the unified ICommandLineOptions and honors both sources. Adds JsonCommandLineOptionsTests covering enumeration schema, validator passes (unknown / arity / per-arg), disabled entries, shadowing, case-insensitivity, and the round-trip through ConfigurationManager. Deferred (truly architectural): --diagnostic* / --config-file bootstrap reads, scalar-bool ambiguity, IsCommandLineOptionSet API visibility. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
bcb7973 to
75c8179CompareUh oh!
There was an error while loading. Please reload this page.
…ccuracy
- Strip section prefix from fullKey when formatting
JsonCommandLineOptionsEntryMustBeScalarOrArrayErrorMessage so the
'{0}' placeholder renders as the entry name relative to the section
('foo' or 'foo:0') instead of the redundant 'commandLineOptions:foo'.
Update the resx <comment> to match the new contract and regenerate xlf.
- Add a Assert.DoesNotContain pin so EnumerateCommandLineOptions_NestedObject_IsRejected
catches accidental regression to the prefixed rendering.
- Rewrite the Validator_DuplicateOptionNamesDifferingByCase_FailsGracefully
comment to accurately describe the pre-fix behavior (silent acceptance,
not raw ArgumentException), per @Evangelink's Round-2 NIT.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>…assertions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Uh oh!
There was an error while loading. Please reload this page.
Summary
Resolves part of #6349: let users specify CLI options inside
testconfig.jsonby routingICommandLineOptionsthroughIConfiguration.Approach
CommandLineConfigurationSource(Order = 0) wraps the parsed CLI into anIConfigurationProviderthat flattens each option undercommandLineOptions:<name>(zero-arity) orcommandLineOptions:<name>:<index>(arg-bearing).TestHostBuilder.CommonServicesbefore the JSON source, so CLI keeps highest precedence.AggregatedConfiguration.TryGetCommandLineOptionFromProvidersdoes 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 whereIConfiguration[key]accidentally returns the JSON:0while the CLI set the zero-arity bare key, or vice versa.IsCommandLineOptionSet,TryGetCommandLineOptionArguments) delegate to that method when given anAggregatedConfiguration, andCommandLineHandlerbecomes a thin facade that does the same. End result: every consumer ofICommandLineOptionstransparently sees JSON-sourced options.JsonConfigurationProviderexposes a typed, schema-validated enumeration ofcommandLineOptions:*entries.CommandLineOptionsValidatorruns three extra passes over those entries (unknown-option, arity, per-arg validation) sotestconfig.jsontypos surface during startup instead of crashing inside option handlers.DisplayBannerIfEnabledAsyncreads--no-bannerfrom the unifiedICommandLineOptions, so banner suppression honorstestconfig.jsonas well as the CLI.What works
testconfig.jsonkeys undercommandLineOptionsare surfaced everywhereICommandLineOptionsis consumed.--results-directorydefined in JSON is honored (was previously ignored - the legacyGetResultsDirectoryCorepath was bypassing JSON).--no-bannerdefined in JSON suppresses the banner.testconfig.json, instead of crashing later. Concrete cases now caught:--timeout: true(was:IndexOutOfRangeExceptioninargs[0])--exit-on-process-exit: "abc"(was:FormatExceptioninint.Parse(args[0]))"timeoutt": "30s"(was: silently ignored)CommandLineHandleris kept so external code keeps compiling.Rough edges still on the table
These are not addressed in this PR:
--diagnostic*and--config-fileare read offparseResultbeforeIConfigurationis built. Honoring them from JSON requires either a two-phase config build or rewriting those bootstrap paths."true"/"false"is ambiguous with the zero-arity presence marker. Callers must use the array form ("foo": ["true"]) for non-boolean string values that happen to be"true"/"false". No code enforces this today.internalfor now. If we ship this we need to decide whether they become public surface.Tests
CommandLineConfigurationProviderTestscovers the source/provider flattening + the provider-aware invariants (includingProviderAwareResolution_CliZeroArityShadowsJsonIndexedArgsend-to-end).CommandLineConfigurationExtensionsTestscovers the helper-level and handler-end-to-end behavior.JsonCommandLineOptionsTests(new) covers the JSON enumeration schema, the three new validator passes, disabled entries, shadowing, and case-insensitivity.