mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 21:38:16 +08:00
Command line argument parsing improvements (#1048)
* Support negative numbers as command option values * Support command line options before arguments * POSIX-compliant handling of quotes (double and single, terminated and unterminated), whitespace, hyphens, and special characters (e.g. emojis)
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
Error: Flags cannot be assigned a value.
|
||||
|
||||
dog --alive foo
|
||||
dog --alive=indeterminate foo
|
||||
^^^^^^^ Can't assign value
|
@ -1,4 +1,4 @@
|
||||
Error: Flags cannot be assigned a value.
|
||||
|
||||
dog -a foo
|
||||
dog -a=indeterminate foo
|
||||
^^ Can't assign value
|
@ -1,3 +0,0 @@
|
||||
# Raw
|
||||
/c
|
||||
set && pause
|
@ -1,4 +0,0 @@
|
||||
Error: Encountered unterminated quoted string 'Rufus'.
|
||||
|
||||
--name "Rufus
|
||||
^^^^^^ Did you forget the closing quotation mark?
|
@ -15,17 +15,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_When_Command_Is_Unknown()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(config =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("cat", "14");
|
||||
var result = app.Run("cat", "14");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -33,17 +33,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Unknown_Command_When_Current_Command_Has_No_Arguments()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<EmptyCommand>("empty");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("empty", "other");
|
||||
var result = app.Run("empty", "other");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -51,8 +51,8 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_With_Suggestion_When_Command_Followed_By_Argument_Is_Unknown_And_Distance_Is_Small()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(config =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.AddBranch<CommandSettings>("dog", a =>
|
||||
{
|
||||
@ -61,10 +61,10 @@ public sealed partial class CommandAppTests
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "bat", "14");
|
||||
var result = app.Run("dog", "bat", "14");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -72,17 +72,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_With_Suggestion_When_Root_Command_Followed_By_Argument_Is_Unknown_And_Distance_Is_Small()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(config =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.AddCommand<CatCommand>("cat");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("bat", "14");
|
||||
var result = app.Run("bat", "14");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -90,18 +90,18 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_With_Suggestion_And_No_Arguments_When_Root_Command_Is_Unknown_And_Distance_Is_Small()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.WithDefaultCommand<GenericCommand<EmptyCommandSettings>>();
|
||||
fixture.Configure(config =>
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<GenericCommand<EmptyCommandSettings>>();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.AddCommand<GenericCommand<EmptyCommandSettings>>("cat");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("bat");
|
||||
var result = app.Run("bat");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -109,9 +109,9 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_With_Suggestion_And_No_Arguments_When_Command_Is_Unknown_And_Distance_Is_Small()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.WithDefaultCommand<GenericCommand<EmptyCommandSettings>>();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<GenericCommand<EmptyCommandSettings>>();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddBranch<CommandSettings>("dog", a =>
|
||||
{
|
||||
@ -120,10 +120,10 @@ public sealed partial class CommandAppTests
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "bat");
|
||||
var result = app.Run("dog", "bat");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -131,18 +131,18 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_With_Suggestion_When_Root_Command_After_Argument_Is_Unknown_And_Distance_Is_Small()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.WithDefaultCommand<GenericCommand<FooCommandSettings>>();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.SetDefaultCommand<GenericCommand<FooCommandSettings>>();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<GenericCommand<BarCommandSettings>>("bar");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("qux", "bat");
|
||||
var result = app.Run("qux", "bat");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -150,8 +150,8 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_With_Suggestion_When_Command_After_Argument_Is_Unknown_And_Distance_Is_Small()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddBranch<FooCommandSettings>("foo", a =>
|
||||
{
|
||||
@ -160,10 +160,10 @@ public sealed partial class CommandAppTests
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("foo", "qux", "bat");
|
||||
var result = app.Run("foo", "qux", "bat");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -176,17 +176,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Long_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "--alive", "foo");
|
||||
var result = app.Run("dog", "--alive=indeterminate", "foo");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -194,17 +194,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Short_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "-a", "foo");
|
||||
var result = app.Run("dog", "-a=indeterminate", "foo");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -217,17 +217,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Long_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "--name");
|
||||
var result = app.Run("dog", "--name");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -235,17 +235,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Short_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "-n");
|
||||
var result = app.Run("dog", "-n");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,17 +258,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<GiraffeCommand>("giraffe");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("giraffe", "foo", "bar", "baz");
|
||||
var result = app.Run("giraffe", "foo", "bar", "baz");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,17 +281,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Long_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--foo");
|
||||
var result = app.Run("--foo");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -299,17 +299,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Short_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("-f");
|
||||
var result = app.Run("-f");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,18 +322,18 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Long_Option_If_Strict_Mode_Is_Enabled()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.UseStrictParsing();
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "--unknown");
|
||||
var result = app.Run("dog", "--unknown");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -341,42 +341,19 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Short_Option_If_Strict_Mode_Is_Enabled()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.UseStrictParsing();
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "-u");
|
||||
var result = app.Run("dog", "-u");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
[ExpectationPath("UnterminatedQuote")]
|
||||
public sealed class UnterminatedQuote
|
||||
{
|
||||
[Fact]
|
||||
[Expectation("Test_1")]
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("--name", "\"Rufus");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
[UsesVerify]
|
||||
@ -388,17 +365,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Short_Option()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", "-", " ");
|
||||
var result = app.Run("dog", "-", " ");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -406,17 +383,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Missing_Long_Option_Value_With_Equality_Separator()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"--foo=");
|
||||
var result = app.Run("dog", $"--foo=");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -424,17 +401,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Missing_Long_Option_Value_With_Colon_Separator()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"--foo:");
|
||||
var result = app.Run("dog", $"--foo:");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -442,17 +419,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Missing_Short_Option_Value_With_Equality_Separator()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"-f=");
|
||||
var result = app.Run("dog", $"-f=");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -460,17 +437,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text_For_Missing_Short_Option_Value_With_Colon_Separator()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"-f:");
|
||||
var result = app.Run("dog", $"-f:");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -483,17 +460,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"-f0o");
|
||||
var result = app.Run("dog", $"-f0o");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -506,17 +483,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"--f");
|
||||
var result = app.Run("dog", $"--f");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -529,17 +506,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"-- ");
|
||||
var result = app.Run("dog", $"-- ");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -552,17 +529,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"--1foo");
|
||||
var result = app.Run("dog", $"--1foo");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
}
|
||||
|
||||
@ -575,17 +552,17 @@ public sealed partial class CommandAppTests
|
||||
public Task Should_Return_Correct_Text()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", $"--f€oo");
|
||||
var result = app.Run("dog", $"--f€oo");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
return Verifier.Verify(result.Output);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
@ -596,71 +573,18 @@ public sealed partial class CommandAppTests
|
||||
public void Should_Allow_Special_Symbols_In_Name(string option)
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("dog", option);
|
||||
var result = app.Run("dog", option);
|
||||
|
||||
// Then
|
||||
result.ShouldBe("Error: Command 'dog' is missing required argument 'AGE'.");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
[Expectation("Quoted_Strings")]
|
||||
public Task Should_Parse_Quoted_Strings_Correctly()
|
||||
{
|
||||
// Given
|
||||
var fixture = new Fixture();
|
||||
fixture.Configure(configurator =>
|
||||
{
|
||||
configurator.AddCommand<DumpRemainingCommand>("foo");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = fixture.Run("foo", "--", "/c", "\"set && pause\"");
|
||||
|
||||
// Then
|
||||
return Verifier.Verify(result);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class Fixture
|
||||
{
|
||||
private Action<CommandApp> _appConfiguration = _ => { };
|
||||
private Action<IConfigurator> _configuration;
|
||||
|
||||
public void WithDefaultCommand<T>()
|
||||
where T : class, ICommand
|
||||
{
|
||||
_appConfiguration = (app) => app.SetDefaultCommand<T>();
|
||||
}
|
||||
|
||||
public void Configure(Action<IConfigurator> action)
|
||||
{
|
||||
_configuration = action;
|
||||
}
|
||||
|
||||
public string Run(params string[] args)
|
||||
{
|
||||
using (var console = new TestConsole())
|
||||
{
|
||||
var app = new CommandApp();
|
||||
_appConfiguration?.Invoke(app);
|
||||
|
||||
app.Configure(_configuration);
|
||||
app.Configure(c => c.ConfigureConsole(console));
|
||||
app.Run(args);
|
||||
|
||||
return console.Output
|
||||
.NormalizeLineEndings()
|
||||
.TrimLines()
|
||||
.Trim();
|
||||
}
|
||||
result.Output.ShouldBe("Error: Command 'dog' is missing required argument 'AGE'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -62,9 +62,38 @@ public sealed partial class CommandAppTests
|
||||
result.Context.Remaining.Raw[0].ShouldBe("--foo");
|
||||
result.Context.Remaining.Raw[1].ShouldBe("bar");
|
||||
result.Context.Remaining.Raw[2].ShouldBe("-bar");
|
||||
result.Context.Remaining.Raw[3].ShouldBe("baz");
|
||||
result.Context.Remaining.Raw[3].ShouldBe("\"baz\"");
|
||||
result.Context.Remaining.Raw[4].ShouldBe("qux");
|
||||
result.Context.Remaining.Raw[5].ShouldBe("foo bar baz qux");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Preserve_Quotes_Hyphen_Delimiters()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddBranch<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"animal", "4", "dog", "12", "--",
|
||||
"/c", "\"set && pause\"",
|
||||
"Name=\" -Rufus --' ",
|
||||
});
|
||||
|
||||
// Then
|
||||
result.Context.Remaining.Raw.Count.ShouldBe(3);
|
||||
result.Context.Remaining.Raw[0].ShouldBe("/c");
|
||||
result.Context.Remaining.Raw[1].ShouldBe("\"set && pause\"");
|
||||
result.Context.Remaining.Raw[2].ShouldBe("Name=\" -Rufus --' ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -132,10 +132,43 @@ public sealed partial class CommandAppTests
|
||||
dog.IsAlive.ShouldBe(false);
|
||||
dog.Name.ShouldBe("Rufus");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Pass_Case_5()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddBranch<AnimalSettings>("animal", animal =>
|
||||
{
|
||||
animal.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"animal", "--alive", "4", "dog", "--good-boy", "12",
|
||||
"--name", "Rufus",
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
|
||||
{
|
||||
dog.Legs.ShouldBe(4);
|
||||
dog.Age.ShouldBe(12);
|
||||
dog.GoodBoy.ShouldBe(true);
|
||||
dog.IsAlive.ShouldBe(true);
|
||||
dog.Name.ShouldBe("Rufus");
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Pass_Case_6()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
@ -164,7 +197,7 @@ public sealed partial class CommandAppTests
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Pass_Case_6()
|
||||
public void Should_Pass_Case_7()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
@ -189,6 +222,38 @@ public sealed partial class CommandAppTests
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Preserve_Quotes_Hyphen_Delimiters_Spaces()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[]
|
||||
{
|
||||
"dog", "12", "4",
|
||||
"--name=\" -Rufus --' ",
|
||||
"--",
|
||||
"--order-by", "\"-size\"",
|
||||
"--order-by", " ",
|
||||
"--order-by", string.Empty,
|
||||
});
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(dog =>
|
||||
{
|
||||
dog.Name.ShouldBe("\" -Rufus --' ");
|
||||
});
|
||||
result.Context.Remaining.Parsed.Count.ShouldBe(1);
|
||||
result.Context.ShouldHaveRemainingArgument("order-by", values: new[] { "\"-size\"", " ", string.Empty });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Be_Able_To_Use_Command_Alias()
|
||||
{
|
||||
@ -489,6 +554,181 @@ public sealed partial class CommandAppTests
|
||||
{
|
||||
dog.IsAlive.ShouldBe(expected);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Set_Short_Option_Before_Argument()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[] { "dog", "-a", "-n=Rufus", "4", "12", });
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(settings =>
|
||||
{
|
||||
settings.IsAlive.ShouldBeTrue();
|
||||
settings.Name.ShouldBe("Rufus");
|
||||
settings.Legs.ShouldBe(4);
|
||||
settings.Age.ShouldBe(12);
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("true", true)]
|
||||
[InlineData("True", true)]
|
||||
[InlineData("false", false)]
|
||||
[InlineData("False", false)]
|
||||
public void Should_Set_Short_Option_With_Explicit_Boolan_Flag_Before_Argument(string value, bool expected)
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[] { "dog", "-a", value, "4", "12", });
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(settings =>
|
||||
{
|
||||
settings.IsAlive.ShouldBe(expected);
|
||||
settings.Legs.ShouldBe(4);
|
||||
settings.Age.ShouldBe(12);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Set_Long_Option_Before_Argument()
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[] { "dog", "--alive", "--name=Rufus", "4", "12" });
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(settings =>
|
||||
{
|
||||
settings.IsAlive.ShouldBeTrue();
|
||||
settings.Name.ShouldBe("Rufus");
|
||||
settings.Legs.ShouldBe(4);
|
||||
settings.Age.ShouldBe(12);
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("true", true)]
|
||||
[InlineData("True", true)]
|
||||
[InlineData("false", false)]
|
||||
[InlineData("False", false)]
|
||||
public void Should_Set_Long_Option_With_Explicit_Boolan_Flag_Before_Argument(string value, bool expected)
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(new[] { "dog", "--alive", value, "4", "12", });
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(settings =>
|
||||
{
|
||||
settings.IsAlive.ShouldBe(expected);
|
||||
settings.Legs.ShouldBe(4);
|
||||
settings.Age.ShouldBe(12);
|
||||
});
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
// Long options
|
||||
[InlineData("dog --alive 4 12 --name Rufus", 4, 12, false, true, "Rufus")]
|
||||
[InlineData("dog --alive=true 4 12 --name Rufus", 4, 12, false, true, "Rufus")]
|
||||
[InlineData("dog --alive:true 4 12 --name Rufus", 4, 12, false, true, "Rufus")]
|
||||
[InlineData("dog --alive --good-boy 4 12 --name Rufus", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --alive=true --good-boy=true 4 12 --name Rufus", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --alive:true --good-boy:true 4 12 --name Rufus", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --alive --good-boy --name Rufus 4 12", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --alive=true --good-boy=true --name Rufus 4 12", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --alive:true --good-boy:true --name Rufus 4 12", 4, 12, true, true, "Rufus")]
|
||||
|
||||
// Short options
|
||||
[InlineData("dog -a 4 12 --name Rufus", 4, 12, false, true, "Rufus")]
|
||||
[InlineData("dog -a=true 4 12 --name Rufus", 4, 12, false, true, "Rufus")]
|
||||
[InlineData("dog -a:true 4 12 --name Rufus", 4, 12, false, true, "Rufus")]
|
||||
[InlineData("dog -a --good-boy 4 12 --name Rufus", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog -a=true -g=true 4 12 --name Rufus", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog -a:true -g:true 4 12 --name Rufus", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog -a -g --name Rufus 4 12", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog -a=true -g=true --name Rufus 4 12", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog -a:true -g:true --name Rufus 4 12", 4, 12, true, true, "Rufus")]
|
||||
|
||||
// Switch around ordering of the options
|
||||
[InlineData("dog --good-boy:true --name Rufus --alive:true 4 12", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --name Rufus --alive:true --good-boy:true 4 12", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --name Rufus --good-boy:true --alive:true 4 12", 4, 12, true, true, "Rufus")]
|
||||
|
||||
// Inject the command arguments in between the options
|
||||
[InlineData("dog 4 12 --good-boy:true --name Rufus --alive:true", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog 4 --good-boy:true 12 --name Rufus --alive:true", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --good-boy:true 4 12 --name Rufus --alive:true", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --good-boy:true 4 --name Rufus 12 --alive:true", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --name Rufus --alive:true 4 12 --good-boy:true", 4, 12, true, true, "Rufus")]
|
||||
[InlineData("dog --name Rufus --alive:true 4 --good-boy:true 12", 4, 12, true, true, "Rufus")]
|
||||
|
||||
// Inject the command arguments in between the options (all flag values set to false)
|
||||
[InlineData("dog 4 12 --good-boy:false --name Rufus --alive:false", 4, 12, false, false, "Rufus")]
|
||||
[InlineData("dog 4 --good-boy:false 12 --name Rufus --alive:false", 4, 12, false, false, "Rufus")]
|
||||
[InlineData("dog --good-boy:false 4 12 --name Rufus --alive:false", 4, 12, false, false, "Rufus")]
|
||||
[InlineData("dog --good-boy:false 4 --name Rufus 12 --alive:false", 4, 12, false, false, "Rufus")]
|
||||
[InlineData("dog --name Rufus --alive:false 4 12 --good-boy:false", 4, 12, false, false, "Rufus")]
|
||||
[InlineData("dog --name Rufus --alive:false 4 --good-boy:false 12", 4, 12, false, false, "Rufus")]
|
||||
public void Should_Set_Option_Before_Argument(string arguments, int legs, int age, bool goodBoy, bool isAlive, string name)
|
||||
{
|
||||
// Given
|
||||
var app = new CommandAppTester();
|
||||
app.Configure(config =>
|
||||
{
|
||||
config.PropagateExceptions();
|
||||
config.AddCommand<DogCommand>("dog");
|
||||
});
|
||||
|
||||
// When
|
||||
var result = app.Run(arguments.Split(' '));
|
||||
|
||||
// Then
|
||||
result.ExitCode.ShouldBe(0);
|
||||
result.Settings.ShouldBeOfType<DogSettings>().And(settings =>
|
||||
{
|
||||
settings.Legs.ShouldBe(legs);
|
||||
settings.Age.ShouldBe(age);
|
||||
settings.GoodBoy.ShouldBe(goodBoy);
|
||||
settings.IsAlive.ShouldBe(isAlive);
|
||||
settings.Name.ShouldBe(name);
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@ -805,7 +1045,7 @@ public sealed partial class CommandAppTests
|
||||
result.Context.Remaining.Raw[0].ShouldBe("--foo");
|
||||
result.Context.Remaining.Raw[1].ShouldBe("bar");
|
||||
result.Context.Remaining.Raw[2].ShouldBe("-bar");
|
||||
result.Context.Remaining.Raw[3].ShouldBe("baz");
|
||||
result.Context.Remaining.Raw[3].ShouldBe("\"baz\"");
|
||||
result.Context.Remaining.Raw[4].ShouldBe("qux");
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,314 @@
|
||||
namespace Spectre.Console.Tests.Unit.Cli.Parsing;
|
||||
|
||||
public class CommandTreeTokenizerTests
|
||||
{
|
||||
public sealed class ScanString
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
[InlineData(" ")]
|
||||
[InlineData("\t")]
|
||||
[InlineData("\r\n\t")]
|
||||
[InlineData("👋🏻")]
|
||||
[InlineData("🐎👋🏻🔥❤️")]
|
||||
[InlineData("\"🐎👋🏻🔥❤️\" is an emoji sequence")]
|
||||
public void Should_Preserve_Edgecase_Inputs(string actualAndExpected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(actualAndExpected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
// Double-quote handling
|
||||
[InlineData("\"")]
|
||||
[InlineData("\"\"")]
|
||||
[InlineData("\"Rufus\"")]
|
||||
[InlineData("\" Rufus\"")]
|
||||
[InlineData("\"-R\"")]
|
||||
[InlineData("\"-Rufus\"")]
|
||||
[InlineData("\" -Rufus\"")]
|
||||
|
||||
// Single-quote handling
|
||||
[InlineData("'")]
|
||||
[InlineData("''")]
|
||||
[InlineData("'Rufus'")]
|
||||
[InlineData("' Rufus'")]
|
||||
[InlineData("'-R'")]
|
||||
[InlineData("'-Rufus'")]
|
||||
[InlineData("' -Rufus'")]
|
||||
public void Should_Preserve_Quotes(string actualAndExpected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(actualAndExpected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("Rufus-")]
|
||||
[InlineData("Rufus--")]
|
||||
[InlineData("R-u-f-u-s")]
|
||||
public void Should_Preserve_Hyphen_Delimiters(string actualAndExpected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(actualAndExpected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(" Rufus")]
|
||||
[InlineData("Rufus ")]
|
||||
[InlineData(" Rufus ")]
|
||||
public void Should_Preserve_Spaces(string actualAndExpected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(actualAndExpected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(" \" -Rufus -- ")]
|
||||
[InlineData("Name=\" -Rufus --' ")]
|
||||
public void Should_Preserve_Quotes_Hyphen_Delimiters_Spaces(string actualAndExpected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actualAndExpected });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(actualAndExpected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ScanLongOption
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("--Name-", "Name-")]
|
||||
[InlineData("--Name_", "Name_")]
|
||||
public void Should_Allow_Hyphens_And_Underscores_In_Option_Name(string actual, string expected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actual });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(expected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.LongOption);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-- ")]
|
||||
[InlineData("--Name ")]
|
||||
[InlineData("--Name\"")]
|
||||
[InlineData("--Nam\"e")]
|
||||
public void Should_Throw_On_Invalid_Option_Name(string actual)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual }));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Invalid long option name.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ScanShortOptions
|
||||
{
|
||||
[Fact]
|
||||
public void Should_Accept_Option_Without_Value()
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new[] { "-a" });
|
||||
|
||||
// Then
|
||||
result.Remaining.ShouldBeEmpty();
|
||||
result.Tokens.ShouldHaveSingleItem();
|
||||
|
||||
var t = result.Tokens[0];
|
||||
t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
|
||||
t.IsGrouped.ShouldBe(false);
|
||||
t.Position.ShouldBe(0);
|
||||
t.Value.ShouldBe("a");
|
||||
t.Representation.ShouldBe("-a");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-a:foo")]
|
||||
[InlineData("-a=foo")]
|
||||
public void Should_Accept_Option_With_Value(string param)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new[] { param });
|
||||
|
||||
// Then
|
||||
result.Remaining.ShouldBeEmpty();
|
||||
result.Tokens.Count.ShouldBe(2);
|
||||
|
||||
var t = result.Tokens.Consume();
|
||||
t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
|
||||
t.IsGrouped.ShouldBe(false);
|
||||
t.Position.ShouldBe(0);
|
||||
t.Value.ShouldBe("a");
|
||||
t.Representation.ShouldBe("-a");
|
||||
|
||||
t = result.Tokens.Consume();
|
||||
t.TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
t.IsGrouped.ShouldBe(false);
|
||||
t.Position.ShouldBe(3);
|
||||
t.Value.ShouldBe("foo");
|
||||
t.Representation.ShouldBe("foo");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
||||
// Positive values
|
||||
[InlineData("-a:1.5", null, "1.5")]
|
||||
[InlineData("-a=1.5", null, "1.5")]
|
||||
[InlineData("-a", "1.5", "1.5")]
|
||||
|
||||
// Negative values
|
||||
[InlineData("-a:-1.5", null, "-1.5")]
|
||||
[InlineData("-a=-1.5", null, "-1.5")]
|
||||
[InlineData("-a", "-1.5", "-1.5")]
|
||||
public void Should_Accept_Option_With_Numeric_Value(string firstArg, string secondArg, string expectedValue)
|
||||
{
|
||||
// Given
|
||||
List<string> args = new List<string>();
|
||||
args.Add(firstArg);
|
||||
if (secondArg != null)
|
||||
{
|
||||
args.Add(secondArg);
|
||||
}
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(args);
|
||||
|
||||
// Then
|
||||
result.Remaining.ShouldBeEmpty();
|
||||
result.Tokens.Count.ShouldBe(2);
|
||||
|
||||
var t = result.Tokens.Consume();
|
||||
t.TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
|
||||
t.IsGrouped.ShouldBe(false);
|
||||
t.Position.ShouldBe(0);
|
||||
t.Value.ShouldBe("a");
|
||||
t.Representation.ShouldBe("-a");
|
||||
|
||||
t = result.Tokens.Consume();
|
||||
t.TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
t.IsGrouped.ShouldBe(false);
|
||||
t.Position.ShouldBe(3);
|
||||
t.Value.ShouldBe(expectedValue);
|
||||
t.Representation.ShouldBe(expectedValue);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Accept_Option_With_Negative_Numeric_Prefixed_String_Value()
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new[] { "-6..2 " });
|
||||
|
||||
// Then
|
||||
result.Remaining.ShouldBeEmpty();
|
||||
result.Tokens.ShouldHaveSingleItem();
|
||||
|
||||
var t = result.Tokens[0];
|
||||
t.TokenKind.ShouldBe(CommandTreeToken.Kind.String);
|
||||
t.IsGrouped.ShouldBe(false);
|
||||
t.Position.ShouldBe(0);
|
||||
t.Value.ShouldBe("-6..2");
|
||||
t.Representation.ShouldBe("-6..2");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-N ", "N")]
|
||||
public void Should_Remove_Trailing_Spaces_In_Option_Name(string actual, string expected)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = CommandTreeTokenizer.Tokenize(new string[] { actual });
|
||||
|
||||
// Then
|
||||
result.Tokens.Count.ShouldBe(1);
|
||||
result.Tokens[0].Value.ShouldBe(expected);
|
||||
result.Tokens[0].TokenKind.ShouldBe(CommandTreeToken.Kind.ShortOption);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-N-")]
|
||||
[InlineData("-N\"")]
|
||||
[InlineData("-a1")]
|
||||
public void Should_Throw_On_Invalid_Option_Name(string actual)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual }));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Short option does not have a valid name.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("-")]
|
||||
[InlineData("- ")]
|
||||
public void Should_Throw_On_Missing_Option_Name(string actual)
|
||||
{
|
||||
// Given
|
||||
|
||||
// When
|
||||
var result = Record.Exception(() => CommandTreeTokenizer.Tokenize(new string[] { actual }));
|
||||
|
||||
// Then
|
||||
result.ShouldBeOfType<CommandParseException>().And(ex =>
|
||||
{
|
||||
ex.Message.ShouldBe("Option does not have a name.");
|
||||
});
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
# Raw
|
||||
/c
|
||||
set && pause
|
@ -1,4 +0,0 @@
|
||||
Error: Encountered unterminated quoted string 'Rufus'.
|
||||
|
||||
--name "Rufus
|
||||
^^^^^^ Did you forget the closing quotation mark?
|
Reference in New Issue
Block a user