Skip to content

Commit 49d6d80

Browse files
authored
Improve tests, xml, auto-execute model commands (#38)
- Add extension method to verify parsing succeeded - Added more xml comments - Improved some tests - Allow to disable auto execution on model commands
1 parent b729b0b commit 49d6d80

File tree

13 files changed

+140
-49
lines changed

13 files changed

+140
-49
lines changed

CommandLineParser.Tests/Command/MultipleCommandTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class MultipleCommandTests
1010
[InlineData(new string[] { "cmd1", "-x", "8" }, false)]
1111
[InlineData(new string[] { "cmd2", "-x", "8" }, false)]
1212
[InlineData(new string[] { }, false)]
13-
public void NonRequiredCommandShouldNotSetResultInErrorStateWhenRequiredOptionsAreMissing(string[] args, bool hasErrors)
13+
public void NonRequiredCommandShouldNotSetResultInErrorStateWhenRequiredOptionsAreMissing(string[] args, bool _)
1414
{
1515
var parser = new CommandLineParser<object>();
1616

@@ -26,7 +26,7 @@ public void NonRequiredCommandShouldNotSetResultInErrorStateWhenRequiredOptionsA
2626

2727
var result = parser.Parse(args);
2828

29-
Assert.False(result.HasErrors);
29+
result.AssertNoErrors();
3030
}
3131

3232
private class MultipleCOmmandTestsOptions

CommandLineParser.Tests/Command/SubCommandTests.cs

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,24 @@ namespace MatthiWare.CommandLine.Tests.Command
1010
{
1111
public class SubCommandTests
1212
{
13-
[Fact]
14-
public void TestSubCommandWorksCorrectly()
13+
[Theory]
14+
[InlineData(true)]
15+
[InlineData(false)]
16+
public void TestSubCommandWorksCorrectlyInModel(bool autoExecute)
1517
{
1618
var lock1 = new ManualResetEventSlim();
1719
var lock2 = new ManualResetEventSlim();
1820

19-
var containerResolver = new CustomInstantiator(lock1, lock2);
21+
var containerResolver = new CustomInstantiator(lock1, lock2, autoExecute);
2022

2123
var parser = new CommandLineParser<MainModel>(containerResolver);
2224

2325
var result = parser.Parse(new[] { "main", "-b", "something", "sub", "-i", "15", "-n", "-1" });
2426

25-
Assert.False(result.HasErrors);
27+
result.AssertNoErrors();
28+
29+
if (!autoExecute)
30+
result.ExecuteCommands();
2631

2732
Assert.True(lock1.Wait(1000), "MainCommand didn't execute in time.");
2833
Assert.True(lock2.Wait(1000), "SubCommand didn't execute in time.");
@@ -32,19 +37,21 @@ private class CustomInstantiator : IContainerResolver
3237
{
3338
private readonly ManualResetEventSlim lock1;
3439
private readonly ManualResetEventSlim lock2;
40+
private readonly bool autoExecute;
3541

36-
public CustomInstantiator(ManualResetEventSlim lock1, ManualResetEventSlim lock2)
42+
public CustomInstantiator(ManualResetEventSlim lock1, ManualResetEventSlim lock2, bool autoExecute)
3743
{
3844
this.lock1 = lock1;
3945
this.lock2 = lock2;
46+
this.autoExecute = autoExecute;
4047
}
4148

4249
public T Resolve<T>()
4350
{
4451
if (typeof(T) == typeof(MainCommand))
45-
return (T)Activator.CreateInstance(typeof(T), lock1);
52+
return (T)Activator.CreateInstance(typeof(T), lock1, autoExecute);
4653
else if (typeof(T) == typeof(SubCommand))
47-
return (T)Activator.CreateInstance(typeof(T), lock2);
54+
return (T)Activator.CreateInstance(typeof(T), lock2, autoExecute);
4855
else
4956
return default;
5057
}
@@ -54,16 +61,19 @@ public T Resolve<T>()
5461
public class MainCommand : Command<MainModel, SubModel>
5562
{
5663
private readonly ManualResetEventSlim locker;
64+
private readonly bool autoExecute;
5765

58-
public MainCommand(ManualResetEventSlim locker)
66+
public MainCommand(ManualResetEventSlim locker, bool autoExecute)
5967
{
6068
this.locker = locker;
69+
this.autoExecute = autoExecute;
6170
}
6271

6372
public override void OnConfigure(ICommandConfigurationBuilder<SubModel> builder)
6473
{
6574
builder
6675
.Name("main")
76+
.AutoExecute(autoExecute)
6777
.Required();
6878
}
6979

@@ -78,16 +88,19 @@ public override void OnExecute(MainModel options, SubModel commandOptions)
7888
public class SubCommand : Command<MainModel, SubSubModel>
7989
{
8090
private readonly ManualResetEventSlim locker;
91+
private readonly bool autoExecute;
8192

82-
public SubCommand(ManualResetEventSlim locker)
93+
public SubCommand(ManualResetEventSlim locker, bool autoExecute)
8394
{
8495
this.locker = locker;
96+
this.autoExecute = autoExecute;
8597
}
8698

8799
public override void OnConfigure(ICommandConfigurationBuilder<SubSubModel> builder)
88100
{
89101
builder
90102
.Name("sub")
103+
.AutoExecute(autoExecute)
91104
.Required();
92105
}
93106

CommandLineParser.Tests/CommandLineParserTests.cs

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Linq;
33
using System.Threading;
44

5-
using MatthiWare.CommandLine;
65
using MatthiWare.CommandLine.Abstractions;
76
using MatthiWare.CommandLine.Abstractions.Command;
87
using MatthiWare.CommandLine.Abstractions.Models;
@@ -59,7 +58,7 @@ public void CommandLineParserUsesContainerCorrectly()
5958

6059
var result = parser.Parse(new[] { "app.exe", "my" });
6160

62-
Assert.False(result.HasErrors);
61+
result.AssertNoErrors();
6362

6463
commandMock.VerifyAll();
6564
containerMock.VerifyAll();
@@ -148,7 +147,7 @@ public void CommandLineParserUsesArgumentFactoryCorrectly()
148147

149148
var result = parser.Parse(new[] { "app.exe", "-m" });
150149

151-
Assert.False(result.HasErrors);
150+
result.AssertNoErrors();
152151

153152
resolverMock.VerifyAll();
154153
argResolverFactory.Verify();
@@ -168,7 +167,7 @@ public void ParseTests()
168167

169168
Assert.NotNull(parsed);
170169

171-
Assert.False(parsed.HasErrors);
170+
parsed.AssertNoErrors();
172171

173172
Assert.Equal("test", parsed.Result.Option1);
174173
}
@@ -222,9 +221,7 @@ public void ParseWithDefaults(string[] args, string result1, string result2, str
222221

223222
var parsed = parser.Parse(args);
224223

225-
Assert.NotNull(parsed);
226-
227-
Assert.False(parsed.HasErrors);
224+
parsed.AssertNoErrors();
228225

229226
Assert.Equal(result1, parsed.Result.Option1);
230227
Assert.Equal(result2, parsed.Result.Option2);
@@ -246,7 +243,7 @@ public void ParseWithCustomParserInAttributeConfiguredModelTests()
246243

247244
var result = parser.Parse(new[] { "app.exe", "-p", "sample" });
248245

249-
Assert.False(result.HasErrors);
246+
result.AssertNoErrors();
250247

251248
Assert.Same(obj, result.Result.Param);
252249
}
@@ -278,9 +275,7 @@ public void ParseWithCommandTests()
278275

279276
var parsed = parser.Parse(new string[] { "app.exe", "-o", "test", "add", "-m", "my message" });
280277

281-
Assert.False(parsed.HasErrors);
282-
283-
Assert.NotNull(parsed);
278+
parsed.AssertNoErrors();
284279

285280
Assert.Equal("test", parsed.Result.Option1);
286281

@@ -318,7 +313,7 @@ public void ParseCommandTests(string[] args, string result1, string result2)
318313

319314
var result = parser.Parse(args);
320315

321-
Assert.False(result.HasErrors);
316+
result.AssertNoErrors();
322317

323318
Assert.Equal(result1, result.Result.Message);
324319

@@ -335,16 +330,14 @@ public void BoolResolverSpecialCaseParsesCorrectly(string[] args, bool expected)
335330
{
336331
var parser = new CommandLineParser<Options>();
337332

338-
//parser.Configure(opt => opt.Option1)
339-
// .Name("o", "opt")
340-
// .Default("Default message");
341-
342333
parser.Configure(opt => opt.Option2)
343334
.Name("x", "xsomething")
344335
.Required();
345336

346337
var result = parser.Parse(args);
347338

339+
result.AssertNoErrors();
340+
348341
Assert.Equal(expected, result.Result.Option2);
349342
}
350343

CommandLineParser.Tests/CustomerReportedTests.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,7 @@ public void AutoPrintUsageAndErrorsShouldNotPrintWhenEverythingIsFIne(string ver
6060

6161
var parsed = parser.Parse(items.ToArray());
6262

63-
if (parsed.HasErrors)
64-
{
65-
foreach (var err in parsed.Errors)
66-
throw err;
67-
}
63+
parsed.AssertNoErrors();
6864

6965
void AddItemToArray(string item)
7066
{

CommandLineParser.Tests/Parsing/ParserResultTest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void TestMergeResultOfCommandResultWorks()
4646

4747
mockCmdResult.VerifyGet(x => x.HasErrors);
4848

49-
Assert.False(result.HasErrors);
49+
result.AssertNoErrors();
5050
}
5151

5252
[Fact]
@@ -58,7 +58,7 @@ public void TestMergeResultOfResultWorks()
5858

5959
result.MergeResult(obj);
6060

61-
Assert.False(result.HasErrors);
61+
result.AssertNoErrors();
6262

6363
Assert.Empty(result.Errors);
6464

CommandLineParser.Tests/Usage/HelpDisplayCommandTests.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
4-
using MatthiWare.CommandLine;
5-
using MatthiWare.CommandLine.Abstractions;
1+
using MatthiWare.CommandLine.Abstractions;
62
using MatthiWare.CommandLine.Abstractions.Command;
73
using MatthiWare.CommandLine.Abstractions.Usage;
84
using MatthiWare.CommandLine.Core.Attributes;

CommandLineParser.Tests/XUnitExtensions.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Linq.Expressions;
3+
using MatthiWare.CommandLine.Abstractions.Parsing;
34

45
namespace MatthiWare.CommandLine.Tests
56
{
@@ -9,5 +10,15 @@ public static LambdaExpression CreateLambda<TSource, TProperty>(Expression<Func<
910
{
1011
return expression;
1112
}
13+
14+
public static void AssertNoErrors<T>(this IParserResult<T> result)
15+
{
16+
if (result == null)
17+
throw new NullReferenceException("Parsing result was null");
18+
19+
foreach (var err in result.Errors)
20+
throw err;
21+
}
22+
1223
}
1324
}

CommandLineParser/Abstractions/Command/ICommandBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
namespace MatthiWare.CommandLine.Abstractions.Command
44
{
5+
/// <summary>
6+
/// Generic command builder
7+
/// </summary>
8+
/// <typeparam name="TOption"></typeparam>
59
public interface ICommandBuilder<TOption>
610
{
711
/// <summary>
@@ -36,6 +40,7 @@ public interface ICommandBuilder<TOption>
3640
/// <summary>
3741
/// Configures the execution of the command
3842
/// </summary>
43+
/// <param name="action">The execution action</param>
3944
/// <param name="required">True or false</param>
4045
/// <returns><see cref="ICommandBuilder{TOption}"/></returns>
4146
ICommandBuilder<TOption> OnExecuting(Action<TOption> action);

CommandLineParser/Abstractions/Command/ICommandConfigurationBuilder.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
namespace MatthiWare.CommandLine.Abstractions.Command
22
{
3+
/// <summary>
4+
/// Command builder
5+
/// </summary>
36
public interface ICommandConfigurationBuilder
47
{
58
/// <summary>
@@ -22,5 +25,12 @@ public interface ICommandConfigurationBuilder
2225
/// <param name="name">Command name</param>
2326
/// <returns><see cref="ICommandConfigurationBuilder"/></returns>
2427
ICommandConfigurationBuilder Name(string name);
28+
29+
/// <summary>
30+
/// Configures if the command should auto execute
31+
/// </summary>
32+
/// <param name="autoExecute">True for automated execution, false for manual</param>
33+
/// <returns><see cref="ICommandConfigurationBuilder"/></returns>
34+
ICommandConfigurationBuilder AutoExecute(bool autoExecute);
2535
}
2636
}

CommandLineParser/Abstractions/Command/ICommandConfigurationBuilder`.cs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33

44
namespace MatthiWare.CommandLine.Abstractions.Command
55
{
6+
/// <summary>
7+
/// Builder for a generic command
8+
/// </summary>
9+
/// <typeparam name="TSource"></typeparam>
610
public interface ICommandConfigurationBuilder<TSource>
711
: ICommandConfigurationBuilder
812
where TSource : class
@@ -19,21 +23,28 @@ public interface ICommandConfigurationBuilder<TSource>
1923
/// Configures if the command is required
2024
/// </summary>
2125
/// <param name="required">True or false</param>
22-
/// <returns><see cref="ICommandConfigurationBuilder"/></returns>
26+
/// <returns><see cref="ICommandConfigurationBuilder{TSource}"/></returns>
2327
new ICommandConfigurationBuilder<TSource> Required(bool required = true);
2428

2529
/// <summary>
2630
/// Configures the description text for the command
2731
/// </summary>
28-
/// <param name="required">True or false</param>
29-
/// <returns><see cref="ICommandConfigurationBuilder"/></returns>
32+
/// <param name="description">The description</param>
33+
/// <returns><see cref="ICommandConfigurationBuilder{TSource}"/></returns>
3034
new ICommandConfigurationBuilder<TSource> Description(string description);
3135

3236
/// <summary>
3337
/// Configures the command name
3438
/// </summary>
3539
/// <param name="name">Command name</param>
36-
/// <returns><see cref="ICommandConfigurationBuilder"/></returns>
40+
/// <returns><see cref="ICommandConfigurationBuilder{TSource}"/></returns>
3741
new ICommandConfigurationBuilder<TSource> Name(string name);
42+
43+
/// <summary>
44+
/// Configures if the command should auto execute
45+
/// </summary>
46+
/// <param name="autoExecute">True for automated execution, false for manual</param>
47+
/// <returns><see cref="ICommandConfigurationBuilder{TSource}"/></returns>
48+
new ICommandConfigurationBuilder<TSource> AutoExecute(bool autoExecute);
3849
}
3950
}

0 commit comments

Comments
 (0)