Skip to content

Commit fe2354a

Browse files
committed
fix: don't set DefaultValue to empty string for string[] options
1 parent 81199c7 commit fe2354a

File tree

3 files changed

+101
-25
lines changed

3 files changed

+101
-25
lines changed

src/CommandLineUtils/Conventions/OptionAttributeConventionBase.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,16 @@ private protected void AddOption(ConventionContext context, CommandOption option
8181
{
8282
if (getter.Invoke(modelAccessor.GetModel()) is IEnumerable<object> values)
8383
{
84+
var count = 0;
8485
foreach (var value in values)
8586
{
87+
count++;
8688
option.TryParse(value?.ToString());
8789
}
88-
option.DefaultValue = string.Join(", ", values.Select(x => x?.ToString()));
90+
if (count > 0)
91+
{
92+
option.DefaultValue = string.Join(", ", values.Select(x => x?.ToString()));
93+
}
8994
}
9095
}
9196
}

test/CommandLineUtils.Tests/FilePathExistsAttributeTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,5 +174,32 @@ public void ValidatesOnlyFiles()
174174
Assert.Equal(0, CommandLineApplication.Execute<OnlyFile>(context));
175175
Assert.NotEqual(0, CommandLineApplication.Execute<OnlyDir>(context));
176176
}
177+
178+
private class OptionalFileChecks
179+
{
180+
[DirectoryExists]
181+
[Option]
182+
public string[] Dir { get; } = new string[0];
183+
184+
[FileExists]
185+
[Option]
186+
public string? File { get; }
187+
188+
private void OnExecute() { }
189+
}
190+
191+
[Theory]
192+
[InlineData(0, new string[0])]
193+
[InlineData(1, new[] { "-f", "file.txt" })]
194+
[InlineData(1, new[] { "-d", "dir1" })]
195+
public void OnlyValidatesOptionsIfSpecified(int exitCode, string[] args)
196+
{
197+
var context = new DefaultCommandLineContext(
198+
new TestConsole(_output),
199+
AppContext.BaseDirectory,
200+
args);
201+
202+
Assert.Equal(exitCode, CommandLineApplication.Execute<OptionalFileChecks>(context));
203+
}
177204
}
178205
}

test/CommandLineUtils.Tests/OptionAttributeTests.cs

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -151,35 +151,79 @@ private class OptionHasDefaultValues
151151

152152
[Option("-a4")]
153153
public (bool hasValue, string value) Arg4 { get; } = (false, "Yellow");
154+
155+
[Option("-a5")]
156+
public string[] Arg5 { get; } = new string[0];
154157
}
155158

156159
[Fact]
157160
public void KeepsDefaultValues()
158161
{
159-
var app1 = Create<OptionHasDefaultValues>();
160-
app1.Parse("-a1", "z", "-a2", "y");
161-
Assert.Equal("z", app1.Model.Arg1);
162-
Assert.Equal(new[] { "y" }, app1.Model.Arg2);
163-
164-
var app2 = Create<OptionHasDefaultValues>();
165-
app2.Parse("-a1", "z");
166-
Assert.Equal("z", app2.Model.Arg1);
167-
Assert.Equal(new[] { "b", "c" }, app2.Model.Arg2);
168-
169-
var app3 = Create<OptionHasDefaultValues>();
170-
app3.Parse();
171-
Assert.Equal("a", app3.Model.Arg1);
172-
Assert.Equal(new[] { "b", "c" }, app3.Model.Arg2);
173-
Assert.False(app3.Model.Arg3.HasValue, "Should not have value");
174-
Assert.False(app3.Model.Arg4.hasValue, "Should not have value");
175-
Assert.Equal((false, "Yellow"), app3.Model.Arg4);
176-
177-
var app4 = Create<OptionHasDefaultValues>();
178-
app4.Parse("-a3", "-a4");
179-
Assert.True(app4.Model.Arg3.HasValue);
180-
Assert.True(app4.Model.Arg4.hasValue);
181-
Assert.True(app4.Model.Arg3);
182-
Assert.Equal((true, null), app4.Model.Arg4);
162+
{
163+
var app1 = Create<OptionHasDefaultValues>();
164+
app1.Parse("-a1", "z", "-a2", "y");
165+
Assert.Equal("z", app1.Model.Arg1);
166+
Assert.Equal(new[] { "y" }, app1.Model.Arg2);
167+
}
168+
169+
{
170+
var app2 = Create<OptionHasDefaultValues>();
171+
app2.Parse("-a1", "z");
172+
Assert.Equal("z", app2.Model.Arg1);
173+
Assert.Equal(new[] { "b", "c" }, app2.Model.Arg2);
174+
}
175+
{
176+
var app3 = Create<OptionHasDefaultValues>();
177+
app3.Parse();
178+
Assert.Equal("a", app3.Model.Arg1);
179+
Assert.Equal(new[] { "b", "c" }, app3.Model.Arg2);
180+
Assert.False(app3.Model.Arg3.HasValue, "Should not have value");
181+
Assert.False(app3.Model.Arg4.hasValue, "Should not have value");
182+
Assert.Equal((false, "Yellow"), app3.Model.Arg4);
183+
Assert.Equal(Array.Empty<string>(), app3.Model.Arg5);
184+
}
185+
186+
{
187+
var app4 = Create<OptionHasDefaultValues>();
188+
app4.Parse("-a3", "-a4");
189+
Assert.True(app4.Model.Arg3.HasValue);
190+
Assert.True(app4.Model.Arg4.hasValue);
191+
Assert.True(app4.Model.Arg3);
192+
Assert.Equal((true, null), app4.Model.Arg4);
193+
Assert.Equal(Array.Empty<string>(), app4.Model.Arg5);
194+
}
195+
196+
{
197+
var app5 = Create<OptionHasDefaultValues>();
198+
app5.Parse("-a5", "a", "-a5", "b");
199+
Assert.Equal(new[] { "a", "b" }, app5.Model.Arg5);
200+
}
201+
}
202+
203+
private class AppWithMultiValueStringOption
204+
{
205+
[Option("-o1")]
206+
string[] Opt1 { get; }
207+
208+
[Option("-o2")]
209+
string[] Opt2 { get; } = new string[0];
210+
}
211+
212+
[Fact]
213+
public void SetsDefaultValueRightForStringArrayOptions()
214+
{
215+
var app = Create<AppWithMultiValueStringOption>();
216+
app.Parse();
217+
{
218+
var opt1 = app.GetOptions().Single(o => o.ShortName == "o1");
219+
Assert.Null(opt1.DefaultValue);
220+
Assert.Empty(opt1.Values);
221+
}
222+
{
223+
var opt2 = app.GetOptions().Single(o => o.ShortName == "o2");
224+
Assert.Null(opt2.DefaultValue);
225+
Assert.Empty(opt2.Values);
226+
}
183227
}
184228

185229
private class PrivateSetterProgram

0 commit comments

Comments
 (0)