Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions TrayApp.Test/CommandLineParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,32 @@
Assert.IsTrue(ran);
}

[TestMethod]
public void TryParse_WithAFlagGivenUnderSeveralAliases_RunsItsHandlerOnce()
{
int calls = 0;
AppOption display = AppOption.Flag(["-d", "--display"], "Keep the display lit.", () => calls++);

Assert.IsTrue(CommandLineParser.TryParse(["-d", "--display", "-d"], [display], out CommandLineOptions options, out _));
Assert.IsTrue(TrayAppBuilder.TryApplyOptions(options, out _));

// A handler that toggles would otherwise be flipped back by the second spelling.
Assert.AreEqual(1, calls);
Assert.AreEqual(1, options.Matches.Count);

Check warning on line 150 in TrayApp.Test/CommandLineParserTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.HasCount' instead of 'Assert.AreEqual'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_TrayApp&issues=AaEU1rcbjNIvKL1jq-52&open=AaEU1rcbjNIvKL1jq-52&pullRequest=33
}

[TestMethod]
public void TryParse_WithAValueOptionGivenTwice_AppliesBothInOrder()
{
List<string> seen = [];
AppOption reason = AppOption.Value(["-r", "--reason"], "text", "Why.", seen.Add);

Assert.IsTrue(CommandLineParser.TryParse(["-r", "first", "--reason", "second"], [reason], out CommandLineOptions options, out _));
Assert.IsTrue(TrayAppBuilder.TryApplyOptions(options, out _));

CollectionAssert.AreEqual(new[] { "first", "second" }, seen);

Check warning on line 162 in TrayApp.Test/CommandLineParserTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use 'Assert.AreSequenceEqual' instead of 'CollectionAssert.AreEqual'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_TrayApp&issues=AaEU1rcbjNIvKL1jq-53&open=AaEU1rcbjNIvKL1jq-53&pullRequest=33
}

[TestMethod]
public void TryParse_WithAnApplicationValueOption_TakesTheNextArgument()
{
Expand Down
6 changes: 4 additions & 2 deletions TrayApp/Cli/AppOption.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ public static AppOption Flag(IReadOnlyList<string> aliases, string description,
/// <param name="valueName">The placeholder shown in <c>--help</c>, such as <c>text</c>.</param>
/// <param name="description">The one-line description shown in <c>--help</c>.</param>
/// <param name="onValue">
/// Called with the value. Throw <see cref="FormatException"/> or <see cref="ArgumentException"/> from
/// here to reject a value: the message becomes the usage error, and the tool exits 2 without starting.
/// Called with the value, once for each time the option was given and in the order given, so the last
/// one wins for a handler that stores it. Throw <see cref="FormatException"/> or
/// <see cref="ArgumentException"/> from here to reject a value: the message becomes the usage error, and
/// the tool exits 2 without starting.
/// </param>
/// <returns>The option.</returns>
/// <exception cref="ArgumentNullException">Any argument is <see langword="null"/>.</exception>
Expand Down
8 changes: 7 additions & 1 deletion TrayApp/Cli/CommandLineParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,13 @@ private static bool TryReadAppOption(
return false;
}

state.Matches.Add(new AppOptionMatch(matched, value));
// A switch runs its handler once however many times it was given, under whichever aliases, so a
// handler that toggles is not undone by "-d --display". A value option keeps every occurrence.
if (matched.TakesValue || !state.Matches.Exists(match => ReferenceEquals(match.Option, matched)))
{
state.Matches.Add(new AppOptionMatch(matched, value));
}

error = string.Empty;
return true;
}
Expand Down
7 changes: 4 additions & 3 deletions TrayApp/TrayAppBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ public TrayAppBuilder Separator()
/// </summary>
/// <param name="aliases">The spellings that select it, such as <c>["-d", "--display"]</c>.</param>
/// <param name="description">The one-line description shown in <c>--help</c>.</param>
/// <param name="onSet">Called once when the switch is present.</param>
/// <param name="onSet">Called once when the switch is present, however many times it was given.</param>
/// <returns>The builder, for chaining.</returns>
/// <exception cref="ArgumentNullException">Any argument is <see langword="null"/>.</exception>
public TrayAppBuilder Flag(IReadOnlyList<string> aliases, string description, Action onSet)
Expand All @@ -260,8 +260,9 @@ public TrayAppBuilder Flag(IReadOnlyList<string> aliases, string description, Ac
/// <param name="valueName">The placeholder shown in <c>--help</c>, such as <c>text</c>.</param>
/// <param name="description">The one-line description shown in <c>--help</c>.</param>
/// <param name="onValue">
/// Called with the value. Throw <see cref="FormatException"/> or <see cref="ArgumentException"/> to
/// reject it: the message becomes the usage error and the tool exits 2.
/// Called with the value, once for each time the option was given and in the order given. Throw
/// <see cref="FormatException"/> or <see cref="ArgumentException"/> to reject it: the message becomes
/// the usage error and the tool exits 2.
/// </param>
/// <returns>The builder, for chaining.</returns>
/// <exception cref="ArgumentNullException">Any argument is <see langword="null"/>.</exception>
Expand Down
Loading