From bd7841c263b8caf3af34ae613ad8d3d7bb8b2f62 Mon Sep 17 00:00:00 2001 From: darthsharp <48331467+darthsharp@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:29:47 +0100 Subject: [PATCH 1/3] Enhance `OptionParameterAttribute` to support multi-character short names and update related tests for validation --- .../OptionParameterAttribute.cs | 12 +++-- .../OptionParameterProperty.cs | 18 ++++--- .../OptionParserTests.cs | 48 ++++++++++++++----- .../TestData/TestOptionForParser.cs | 2 + 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs b/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs index fc8ec0a3..a8f03726 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs @@ -7,15 +7,21 @@ namespace CreativeCoders.SysConsole.Cli.Parsing; [AttributeUsage(AttributeTargets.Property)] public sealed class OptionParameterAttribute : OptionBaseAttribute { - public OptionParameterAttribute(char shortName, string longName) + public OptionParameterAttribute(string shortName, string longName) { ShortName = shortName; LongName = longName; } + public OptionParameterAttribute(char shortName, string longName) + { + ShortName = shortName.ToString(); + LongName = longName; + } + public OptionParameterAttribute(char shortName) { - ShortName = shortName; + ShortName = shortName.ToString(); } public OptionParameterAttribute(string longName) @@ -23,7 +29,7 @@ public OptionParameterAttribute(string longName) LongName = longName; } - public char? ShortName { get; } + public string? ShortName { get; } public string? LongName { get; } } diff --git a/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionProperties/OptionParameterProperty.cs b/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionProperties/OptionParameterProperty.cs index 1207460f..0b1607e1 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionProperties/OptionParameterProperty.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionProperties/OptionParameterProperty.cs @@ -1,26 +1,24 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; +using CreativeCoders.Core; namespace CreativeCoders.SysConsole.Cli.Parsing.OptionProperties; -public class OptionParameterProperty : OptionPropertyBase +public class OptionParameterProperty( + PropertyInfo propertyInfo, + OptionParameterAttribute optionParameterAttribute) + : OptionPropertyBase(propertyInfo, optionParameterAttribute) { - private readonly OptionParameterAttribute _optionParameterAttribute; - - public OptionParameterProperty(PropertyInfo propertyInfo, - OptionParameterAttribute optionParameterAttribute) - : base(propertyInfo, optionParameterAttribute) - { - _optionParameterAttribute = optionParameterAttribute; - } + private readonly OptionParameterAttribute _optionParameterAttribute = + Ensure.NotNull(optionParameterAttribute); public override bool Read(IEnumerable optionArguments, object optionObject) { var optionArgument = optionArguments .FirstOrDefault(x => (x.Kind == OptionArgumentKind.ShortName - && x.OptionName == _optionParameterAttribute.ShortName.ToString()) || + && x.OptionName == _optionParameterAttribute.ShortName) || (x.Kind == OptionArgumentKind.LongName && x.OptionName == _optionParameterAttribute.LongName)); diff --git a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs index 9351812e..899c2b51 100644 --- a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs +++ b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs @@ -24,7 +24,7 @@ public void Parse_ValueAndParameterWithLongName_PropertiesAreSetCorrect() .Should() .NotBeNull(); - option!.HelloWorld + option.HelloWorld .Should() .Be("hello"); @@ -48,7 +48,7 @@ public void Parse_ValueAndParameterWithShortName_PropertiesAreSetCorrect() .Should() .NotBeNull(); - option!.HelloWorld + option.HelloWorld .Should() .Be("hello"); @@ -57,6 +57,30 @@ public void Parse_ValueAndParameterWithShortName_PropertiesAreSetCorrect() .Be("TestText"); } + [Fact] + public void Parse_ValueAndParameterWithMultiCharShortName_PropertiesAreSetCorrect() + { + var args = new[] { "hello", "-te", "TestText" }; + + var parser = new OptionParser(typeof(TestOptionForParser)); + + // Act + var option = parser.Parse(args) as TestOptionForParser; + + // Assert + option + .Should() + .NotBeNull(); + + option.HelloWorld + .Should() + .Be("hello"); + + option.TextValueMultiCharShortName + .Should() + .Be("TestText"); + } + [Fact] public void Parse_IntValueAndParameterWithLongName_PropertiesAreSetCorrect() { @@ -76,7 +100,7 @@ public void Parse_IntValueAndParameterWithLongName_PropertiesAreSetCorrect() .Should() .NotBeNull(); - option!.IntValue + option.IntValue .Should() .Be(expectedValue0); @@ -103,7 +127,7 @@ public void Parse_IntValueAndParameterWithShortName_PropertiesAreSetCorrect() .Should() .NotBeNull(); - option!.IntValue + option.IntValue .Should() .Be(expectedValue0); @@ -158,7 +182,7 @@ public void Parse_BoolValues_PropertiesAreSetCorrect() .Should() .NotBeNull(); - option!.Verbose + option.Verbose .Should() .BeTrue(); @@ -182,7 +206,7 @@ public void Parse_BoolValuesInvalidFormat_PropertyIsSetFalse() .Should() .NotBeNull(); - option!.Verbose + option.Verbose .Should() .BeTrue(); @@ -232,7 +256,7 @@ public void Parse_ValueDefaultValue_DefaultValueIsSet() .Should() .NotBeNull(); - options!.FirstValue + options.FirstValue .Should() .Be(null); @@ -307,7 +331,7 @@ public void Parse_EnumValue_PropertyIsSetCorrect(string argValue, TestEnum enumV .Should() .NotBeNull(); - option!.EnumValue + option.EnumValue .Should() .Be(enumValue); } @@ -331,7 +355,7 @@ public void Parse_PropertyWithConverter_PropertyIsSetCorrect(string argValue, st .Should() .NotBeNull(); - option!.Text + option.Text .Should() .Be(propertyValue); } @@ -355,7 +379,7 @@ public void Parse_PropertyIsIEnumerableOfInt_PropertyIsSetCorrect(string argValu .Should() .NotBeNull(); - option!.IntValues + option.IntValues .Should() .ContainInOrder(intValues); } @@ -379,7 +403,7 @@ public void Parse_PropertyIsEnumWithFlags_EnumFlagsAreSetCorrect(string argValue .Should() .NotBeNull(); - option!.EnumValue + option.EnumValue .Should() .Be(enumWithFlags); } @@ -406,7 +430,7 @@ public void Parse_NotAllArgsMatch_ThrowsException(params string[] args) .Should() .HaveCount(1) .And - .BeEquivalentTo(new[] { new OptionArgument { Kind = OptionArgumentKind.Value, Value = "test" } }); + .BeEquivalentTo([new OptionArgument { Kind = OptionArgumentKind.Value, Value = "test" }]); } [Theory] diff --git a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs index 6e83b8c8..f9c16af2 100644 --- a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs +++ b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs @@ -7,4 +7,6 @@ public class TestOptionForParser [OptionValue(0)] public string? HelloWorld { get; [UsedImplicitly] set; } [OptionParameter('t', "text")] public string? TextValue { get; [UsedImplicitly] set; } + + [OptionParameter("te", "text")] public string? TextValueMultiCharShortName { get; [UsedImplicitly] set; } } From 36df9348ad4289da6f98c367be12fccf17ef7aa8 Mon Sep 17 00:00:00 2001 From: darthsharp <48331467+darthsharp@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:41:42 +0100 Subject: [PATCH 2/3] Add test case for parsing CLI options with multi-character and single-character short names. --- .../OptionParserTests.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs index 899c2b51..39208c10 100644 --- a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs +++ b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/OptionParserTests.cs @@ -81,6 +81,30 @@ public void Parse_ValueAndParameterWithMultiCharShortName_PropertiesAreSetCorrec .Be("TestText"); } + [Fact] + public void Parse_ValueAndParameterWithMultiCharShortNameAndSingleChar_PropertiesAreSetCorrect() + { + var args = new[] { "hello", "-te", "TestText", "-t", "ShotText" }; + + var parser = new OptionParser(typeof(TestOptionForParser)); + + // Act + var option = parser.Parse(args) as TestOptionForParser; + + // Assert + option + .Should().NotBeNull(); + + option.HelloWorld + .Should().Be("hello"); + + option.TextValue + .Should().Be("ShotText"); + + option.TextValueMultiCharShortName + .Should().Be("TestText"); + } + [Fact] public void Parse_IntValueAndParameterWithLongName_PropertiesAreSetCorrect() { From 30c7f8cab7b596a18de0d203951af7fe7f188d3e Mon Sep 17 00:00:00 2001 From: darthsharp <48331467+darthsharp@users.noreply.github.com> Date: Sat, 31 Jan 2026 12:45:44 +0100 Subject: [PATCH 3/3] Ensure non-null values for constructor parameters in `OptionParameterAttribute` using `Ensure.IsNotNullOrWhitespace`. Update related test data for consistency. --- .../OptionParameterAttribute.cs | 9 +++++---- .../TestData/TestOptionForParser.cs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs b/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs index a8f03726..011eb8bd 100644 --- a/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs +++ b/source/SysConsole/CreativeCoders.SysConsole.Cli.Parsing/OptionParameterAttribute.cs @@ -1,4 +1,5 @@ using System; +using CreativeCoders.Core; using JetBrains.Annotations; namespace CreativeCoders.SysConsole.Cli.Parsing; @@ -9,14 +10,14 @@ public sealed class OptionParameterAttribute : OptionBaseAttribute { public OptionParameterAttribute(string shortName, string longName) { - ShortName = shortName; - LongName = longName; + ShortName = Ensure.IsNotNullOrWhitespace(shortName); + LongName = Ensure.IsNotNullOrWhitespace(longName); } public OptionParameterAttribute(char shortName, string longName) { ShortName = shortName.ToString(); - LongName = longName; + LongName = Ensure.IsNotNullOrWhitespace(longName); } public OptionParameterAttribute(char shortName) @@ -26,7 +27,7 @@ public OptionParameterAttribute(char shortName) public OptionParameterAttribute(string longName) { - LongName = longName; + LongName = Ensure.IsNotNullOrWhitespace(longName); } public string? ShortName { get; } diff --git a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs index f9c16af2..f2a6bda3 100644 --- a/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs +++ b/tests/CreativeCoders.SysConsole.Cli.Parsing.UnitTests/TestData/TestOptionForParser.cs @@ -8,5 +8,5 @@ public class TestOptionForParser [OptionParameter('t', "text")] public string? TextValue { get; [UsedImplicitly] set; } - [OptionParameter("te", "text")] public string? TextValueMultiCharShortName { get; [UsedImplicitly] set; } + [OptionParameter("te", "text2")] public string? TextValueMultiCharShortName { get; [UsedImplicitly] set; } }