-
-
Notifications
You must be signed in to change notification settings - Fork 23
Model repeatable CLI options #4006
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
6871f71
fix(generator): model repeatable options
thomhurst 00987e9
refactor(generator): reduce parser complexity
thomhurst 7c8c234
test(options): cover repeatable typed values
thomhurst 09fbe47
refactor(generator): extract terraform type shape
thomhurst ea76cb3
fix(generator): narrow repeatability prose
thomhurst 6a1df55
fix(generator): qualify repeatability prose
thomhurst 92d3e5c
fix(generator): preserve repeatable phrases
thomhurst 7d8365f
fix(generator): reject operational counts
thomhurst e372548
fix(generator): harden repeatable detection
thomhurst 01060a1
fix(generator): harden repeatable prose
thomhurst c446a51
fix(packer): parse quoted option values
thomhurst 9a78e97
fix(generator): reject qualified retry counts
thomhurst 48d1b15
fix(generator): align repeatable parser tests
thomhurst 3f5db57
fix(generator): retain boolean value options
thomhurst 6ff7908
fix(generator): retain passive repeatability
thomhurst File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
262 changes: 262 additions & 0 deletions
262
...ator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/RepeatableOptionAdapterTests.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,262 @@ | ||
| using Microsoft.Extensions.Logging.Abstractions; | ||
| using ModularPipelines.OptionsGenerator.Models; | ||
| using ModularPipelines.OptionsGenerator.Scrapers.Cli; | ||
| using ModularPipelines.OptionsGenerator.TypeDetection; | ||
|
|
||
| namespace ModularPipelines.OptionsGenerator.Tests.Scrapers; | ||
|
|
||
| public class RepeatableOptionAdapterTests | ||
| { | ||
| private static ICliCommandExecutor Executor { get; } = | ||
| new ProcessCliCommandExecutor(NullLogger<ProcessCliCommandExecutor>.Instance); | ||
|
|
||
| private static IHelpTextCache Cache { get; } = | ||
| new HelpTextCache(NullLogger<HelpTextCache>.Instance); | ||
|
|
||
| [Test] | ||
| public async Task Terraform_Recognizes_Multiple_Times_Prose() | ||
| { | ||
| const string helpText = """ | ||
| Usage: terraform apply [options] | ||
|
|
||
| Options: | ||
| -var-file=path Set variables from a file. This flag can be used multiple times. | ||
| """; | ||
| var command = await new TestTerraformCliScraper().Parse(["terraform", "apply"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "-var-file"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Pip_Recognizes_Multiline_Multiple_Times_Prose() | ||
| { | ||
| const string helpText = """ | ||
| Usage: pip freeze [options] | ||
|
|
||
| General Options: | ||
| -r, --requirement <file> Install from the given requirements file. | ||
| This option can be used multiple times. | ||
| """; | ||
| var command = await new TestPipCliScraper().Parse(["pip", "freeze"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "--requirement"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Packer_Recognizes_Repeatable_Prose() | ||
| { | ||
| const string helpText = """ | ||
| Usage: packer build [options] | ||
|
|
||
| Options: | ||
| -var-file=path Set a variable file; repeatable for additional files. | ||
| """; | ||
| var command = await new TestPackerCliScraper().Parse(["packer", "build"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "--var-file"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Packer_Recognizes_Quoted_Repeatable_Value() | ||
| { | ||
| const string helpText = """ | ||
| Usage: packer build [options] | ||
|
|
||
| Options: | ||
| -var 'key=value' Set a variable. Can be repeated. | ||
| """; | ||
| var command = await new TestPackerCliScraper().Parse(["packer", "build"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "--var"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Packer_Recognizes_Multiline_Repeatable_Prose() | ||
| { | ||
| const string helpText = """ | ||
| Usage: packer build [options] | ||
|
|
||
| Options: | ||
| -var-file=path Set a variable file. | ||
| Can be repeated for additional files. | ||
| """; | ||
| var command = await new TestPackerCliScraper().Parse(["packer", "build"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "--var-file"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Packer_Does_Not_Treat_Operational_Repetition_As_Repeatable() | ||
| { | ||
| const string helpText = """ | ||
| Usage: packer build [options] | ||
|
|
||
| Options: | ||
| -retry-count=count Retry the operation multiple times before failing. | ||
| """; | ||
| var command = await new TestPackerCliScraper().Parse(["packer", "build"], helpText); | ||
| var option = command!.Options.Single(item => item.SwitchName == "--retry-count"); | ||
|
|
||
| using (Assert.Multiple()) | ||
| { | ||
| await Assert.That(option.AcceptsMultipleValues).IsFalse(); | ||
| await Assert.That(option.CSharpType).IsEqualTo("string?"); | ||
| } | ||
| } | ||
|
|
||
| [Test] | ||
| [Arguments("This argument must be repeated for each entry.", true)] | ||
| [Arguments("The value is repeated across runs.", true)] | ||
| [Arguments("Provide one or more paths to scan.", true)] | ||
| [Arguments("Validates multiple values against a schema.", false)] | ||
| [Arguments("Retry the operation one or more times before failing.", false)] | ||
| [Arguments("This operation runs one or more attempts depending on configuration.", false)] | ||
| [Arguments("One or more attempts are made before failure.", false)] | ||
| [Arguments("One or more times may be required.", false)] | ||
| [Arguments("One or more retries may be attempted.", false)] | ||
| [Arguments("Provide one or more attempts before failing.", false)] | ||
| [Arguments("Accepts one or more retries.", false)] | ||
| [Arguments("Give one or more times to retry.", false)] | ||
| [Arguments("One or more retry attempts before failing.", false)] | ||
| [Arguments("Zero or more retry attempts may be made.", false)] | ||
| [Arguments("One or more connection retries before giving up.", false)] | ||
| [Arguments("One or more polling attempts before timing out.", false)] | ||
| [Arguments("This option can be specified one or more times.", true)] | ||
| [Arguments("May be provided zero or more times.", true)] | ||
| [Arguments("This option should be used more than once.", true)] | ||
| [Arguments("Supports multiple values.", true)] | ||
| [Arguments("Takes multiple values.", true)] | ||
| [Arguments("Value can contain multiple values.", true)] | ||
| public async Task Packer_Classifies_Explicit_Repeatability_Prose( | ||
| string description, | ||
| bool expected) | ||
| { | ||
| var helpText = $""" | ||
| Usage: packer build [options] | ||
|
|
||
| Options: | ||
| -var-file=path {description} | ||
| """; | ||
| var command = await new TestPackerCliScraper().Parse(["packer", "build"], helpText); | ||
| var option = command!.Options.Single(item => item.SwitchName == "--var-file"); | ||
|
|
||
| await Assert.That(option.AcceptsMultipleValues).IsEqualTo(expected); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Snyk_Recognizes_Repeated_Prose() | ||
| { | ||
| const string helpText = """ | ||
| Usage: snyk monitor [<OPTIONS>] | ||
|
|
||
| Options | ||
| --project-environment=<ENVIRONMENT> | ||
| Set the project environment. Can be repeated. | ||
| """; | ||
| var command = await new TestSnykCliScraper().Parse(["snyk", "monitor"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "--project-environment"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Snyk_Preserves_Numeric_Element_Type_For_Repeatable_Options() | ||
| { | ||
| const string helpText = """ | ||
| Usage: snyk test [<OPTIONS>] | ||
|
|
||
| Options | ||
| --max-depth=<DEPTH> | ||
| Set the maximum dependency depth. Can be repeated. | ||
| """; | ||
| var command = await new TestSnykCliScraper().Parse(["snyk", "test"], helpText); | ||
|
|
||
| await AssertRepeatable(command, "--max-depth", "IEnumerable<int>?"); | ||
| } | ||
|
|
||
| [Test] | ||
| public async Task Snyk_Preserves_Enum_Element_Type_For_Repeatable_Options() | ||
| { | ||
| const string helpText = """ | ||
| Usage: snyk test [<OPTIONS>] | ||
|
|
||
| Options | ||
| --severity-threshold=<low|medium|high|critical> | ||
| Report only vulnerabilities at the selected threshold. Can be repeated. | ||
| """; | ||
| var command = await new TestSnykCliScraper().Parse(["snyk", "test"], helpText); | ||
|
|
||
| await AssertRepeatable( | ||
| command, | ||
| "--severity-threshold", | ||
| "IEnumerable<SnykSeverityThreshold>?"); | ||
| } | ||
|
|
||
| private static async Task AssertRepeatable( | ||
| CliCommandDefinition? command, | ||
| string switchName, | ||
| string expectedType = "IEnumerable<string>?") | ||
| { | ||
| var option = command!.Options.Single(item => item.SwitchName == switchName); | ||
| using (Assert.Multiple()) | ||
| { | ||
| await Assert.That(option.AcceptsMultipleValues).IsTrue(); | ||
| await Assert.That(option.CSharpType).IsEqualTo(expectedType); | ||
| } | ||
| } | ||
|
|
||
| private sealed class TestTerraformCliScraper() | ||
| : TerraformCliScraper( | ||
| RepeatableOptionAdapterTests.Executor, | ||
| RepeatableOptionAdapterTests.Cache, | ||
| NullLogger<TerraformCliScraper>.Instance) | ||
| { | ||
| public Task<CliCommandDefinition?> Parse(string[] commandPath, string helpText) => | ||
| ParseCommandAsync( | ||
| commandPath, | ||
| helpText, | ||
| ParseUsageSynopsis(commandPath, helpText), | ||
| CancellationToken.None); | ||
| } | ||
|
|
||
| private sealed class TestPipCliScraper() | ||
| : PipCliScraper( | ||
| RepeatableOptionAdapterTests.Executor, | ||
| RepeatableOptionAdapterTests.Cache, | ||
| NullLogger<PipCliScraper>.Instance) | ||
| { | ||
| public Task<CliCommandDefinition?> Parse(string[] commandPath, string helpText) => | ||
| ParseCommandAsync( | ||
| commandPath, | ||
| helpText, | ||
| ParseUsageSynopsis(commandPath, helpText), | ||
| CancellationToken.None); | ||
| } | ||
|
|
||
| private sealed class TestPackerCliScraper() | ||
| : PackerCliScraper( | ||
| RepeatableOptionAdapterTests.Executor, | ||
| RepeatableOptionAdapterTests.Cache, | ||
| NullLogger<PackerCliScraper>.Instance) | ||
| { | ||
| public Task<CliCommandDefinition?> Parse(string[] commandPath, string helpText) => | ||
| ParseCommandAsync( | ||
| commandPath, | ||
| helpText, | ||
| ParseUsageSynopsis(commandPath, helpText), | ||
| CancellationToken.None); | ||
| } | ||
|
|
||
| private sealed class TestSnykCliScraper() | ||
| : SnykCliScraper( | ||
| RepeatableOptionAdapterTests.Executor, | ||
| RepeatableOptionAdapterTests.Cache, | ||
| NullLogger<SnykCliScraper>.Instance) | ||
| { | ||
| public Task<CliCommandDefinition?> Parse(string[] commandPath, string helpText) => | ||
| ParseCommandAsync( | ||
| commandPath, | ||
| helpText, | ||
| ParseUsageSynopsis(commandPath, helpText), | ||
| CancellationToken.None); | ||
| } | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.