-
-
Notifications
You must be signed in to change notification settings - Fork 499
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
Fixes #587 Support negative numbers as command option values #732
Fixes #587 Support negative numbers as command option values #732
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had some time to look through this. At a glance it looks good. But think we could get some tests that cover the invalid cases as described in the comments of the tokenizer for invalid items?
Hey @phil-scott-78, thanks for reviewing. Actually, CommandTreeTokenizer does have (logical) 100 % test coverage already. It is not immediately obvious, as this PR is the first stage that actually adds tests specifically for the Tokenizer; previously, the tokenizer has only been tested through CommandAppTests which test the entire parsing and execution pipeline. However, some of the test scenarios there (say, CommandAppTests.Parsing.InvalidShortOptionName.Should_Return_Correct_Text) do exercise these failure modes. I would personally prefer to have 100 % coverage specifically with the tokenizer test suite, but writing all that is slightly beyond the scope of this issue. Would you expect to see (tokenizer level) tests for the failure scenarios of the ScanShortOptions tokenizer branch or what? TBH, the tokenizer test set in this PR doesn't cover all short option tokenization features (even the happy paths) either, grouped properties probably being the most important missing part. Given that there's no precedent for tokenizer tests in the project, I'm happy to do extra work to set a good example, let's just agree on the goal first. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for submitting this PR, my review comments are largely stylistic to ensure the changes fit stylistically with the rest of the codebase.
@@ -235,13 +235,34 @@ private static IEnumerable<CommandTreeToken> ScanShortOptions(CommandTreeTokeniz | |||
? new CommandTreeToken(CommandTreeToken.Kind.ShortOption, position, value, $"-{value}") | |||
: new CommandTreeToken(CommandTreeToken.Kind.ShortOption, position + result.Count, value, value)); | |||
} | |||
else if (result.Count == 0 && char.IsDigit(current)) | |||
{ | |||
/* We require short options to be named with letters. Short options that start with a number |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Everywhere else in the codebase, multi-line comments have simply used the // convention at the beginning of each line. I suggest this comment be updated accordingly to ensure consistency (for better or worse) with the rest of the codebase.
var represntation = current.ToString(CultureInfo.InvariantCulture); | ||
var token = new CommandTreeToken(CommandTreeToken.Kind.ShortOption, tokenPosition, represntation, represntation); | ||
throw CommandParseException.InvalidShortOptionName(reader.Original, token); | ||
ThrowInvalidShortOptionName(current.ToString(CultureInfo.InvariantCulture)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not see the benefit of putting the local three-line logic into a separate private method, which also still references reader.Original
from the calling function without passing this in as an explicit argument. I suggest this private method be removed and the logic relocated inline here, instead.
@@ -0,0 +1,90 @@ | |||
namespace Spectre.Console.Tests.Unit.Cli; | |||
|
|||
public static class CommandTreeTokenizerTests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fantastic that you have introduced explicit unit tests for the tokenizer! A positive move away from the general approach of 'unit tests' being located at the CommandApp level (nb. more like integration tests really).
Does this class need to be static? Unit test classes aren't generally made static - suggest this is also removed to ensure consistency with the rest of the codebase.
{ | ||
[Fact] | ||
public void Valueless() | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// When
var result = CommandTreeTokenizer.Tokenize(new[] { "-a" }); | ||
Assert.Empty(result.Remaining); | ||
var t = Assert.Single(result.Tokens); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Then
[InlineData("-a=-1.5", null)] | ||
[InlineData("-a", "-1.5")] | ||
public void With_Negative_Numeric_Value(string firstArg, string secondArg) | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Given
{ | ||
args.Add(secondArg); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// When
var result = CommandTreeTokenizer.Tokenize(args); | ||
Assert.Empty(result.Remaining); | ||
Assert.Equal(2, result.Tokens.Count); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Then
|
||
[Fact] | ||
public void With_Negative_Numeric_Prefixed_String_Value() | ||
{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// When
public void With_Negative_Numeric_Prefixed_String_Value() | ||
{ | ||
var result = CommandTreeTokenizer.Tokenize(new[] { "-6..2 " }); | ||
Assert.Empty(result.Remaining); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Then
This is a significant improvement that @jouniheikniemi has introduced and I would be keen to see this PR, once review comments are addressed 😊, merged into the codebase. Particularly since I have a raft of CommandTreeTokenizer tests I would like to add myself, ideally into the same class being introduced in this PR. (given this PR has been languishing since Feb, I'm happy to fork Jouni's branch, bring it up to date, and make the review comments myself to get this over the line, if asked...) |
This should be closed as it's been fully included in #1048 [I don't have permission to close another person's PR] |
I was about to get back to your review comments, but it seems you'd rather pick up the ball yourself; fine with me. Closing the PR. Thanks! |
I was keen to merge your excellent work @jouniheikniemi - thanks for your contribution. |
Decided to create some unit tests for the tokenizer, as it would've been too much of a mess to test all of this through CommandApp. That's obviously up for debate, but at least the tokenizer tests helped me with this.