Skip to content

Commit 611882f

Browse files
authored
Allow RegisterCommand to receive Type arguments (#50)
* Add generic register command * Cover new methods as well * Make ExecuteRegisterCommand work for model commands. * Improve some tests. * Update interface * Add non happy path * Fix codefactor
1 parent 73d88d7 commit 611882f

File tree

12 files changed

+311
-129
lines changed

12 files changed

+311
-129
lines changed

CommandLineParser.Tests/Command/CommandTests.cs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
using System.Linq;
2-
3-
using MatthiWare.CommandLine;
1+
using MatthiWare.CommandLine;
42
using MatthiWare.CommandLine.Abstractions.Command;
5-
3+
using System.Linq;
64
using Xunit;
75

86
namespace MatthiWare.CommandLine.Tests.Command
@@ -37,36 +35,51 @@ public void AddOptionLessCommand()
3735
Assert.NotNull(parser.Commands.First(cmd => cmd.Name.Equals("y")));
3836
}
3937

40-
[Fact]
41-
public void AddCommandType()
38+
[Theory]
39+
[InlineData(true)]
40+
[InlineData(false)]
41+
public void AddCommandType(bool generic)
4242
{
4343
var parser = new CommandLineParser<object>();
4444

45-
parser.RegisterCommand<MyComand>();
45+
if (generic)
46+
parser.RegisterCommand<MyComand>();
47+
else
48+
parser.RegisterCommand(typeof(MyComand));
4649

4750
Assert.Equal(1, parser.Commands.Count);
4851

4952
Assert.NotNull(parser.Commands.First(cmd => cmd.Name.Equals("bla")));
5053
}
5154

52-
[Fact]
53-
public void AddOtherCommandType()
55+
[Theory]
56+
[InlineData(true)]
57+
[InlineData(false)]
58+
public void AddOtherCommandType(bool generic)
5459
{
5560
var parser = new CommandLineParser<object>();
5661

57-
parser.RegisterCommand<OtherCommand>();
62+
if (generic)
63+
parser.RegisterCommand<OtherCommand>();
64+
else
65+
parser.RegisterCommand(typeof(OtherCommand));
5866

5967
Assert.Equal(1, parser.Commands.Count);
6068

6169
Assert.NotNull(parser.Commands.First(cmd => cmd.Name.Equals("other")));
6270
}
6371

64-
[Fact]
65-
public void AddCommandTypeWithGenericOption()
72+
[Theory]
73+
[InlineData(true)]
74+
[InlineData(false)]
75+
public void AddCommandTypeWithGenericOption(bool generic)
6676
{
6777
var parser = new CommandLineParser<object>();
6878

69-
parser.RegisterCommand<MyComand, object>();
79+
if (generic)
80+
parser.RegisterCommand<MyComand, object>();
81+
else
82+
parser.RegisterCommand(typeof(MyComand), typeof(object));
7083

7184
Assert.Equal(1, parser.Commands.Count);
7285

CommandLineParser.Tests/Command/MultipleCommandTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public class MultipleCommandTests
1212
[InlineData(new string[] { }, false)]
1313
public void NonRequiredCommandShouldNotSetResultInErrorStateWhenRequiredOptionsAreMissing(string[] args, bool _)
1414
{
15-
var parser = new CommandLineParser<object>();
15+
var parser = new CommandLineParser();
1616

1717
parser.AddCommand<MultipleCOmmandTestsOptions>()
1818
.Name("cmd1")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
using MatthiWare.CommandLine.Abstractions.Command;
2+
using System;
3+
using Xunit;
4+
5+
namespace MatthiWare.CommandLine.Tests.Command
6+
{
7+
public class RegisterCommandTests
8+
{
9+
[Fact]
10+
public void RegisterCommandWithNoneCommandTypeThrowsException()
11+
{
12+
var parser = new CommandLineParser();
13+
14+
Assert.Throws<ArgumentException>(() => parser.RegisterCommand(typeof(object)));
15+
}
16+
17+
[Fact]
18+
public void RegisterCommandWithWrongParentTypeThrowsException()
19+
{
20+
var parser = new CommandLineParser();
21+
22+
Assert.Throws<ArgumentException>(() => parser.RegisterCommand(typeof(MyWrongCommand)));
23+
}
24+
25+
private class MyWrongCommand : Command<Exception, Exception>
26+
{
27+
}
28+
}
29+
}
Lines changed: 101 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,134 +1,166 @@
1-
using System;
2-
using System.Threading;
3-
using MatthiWare.CommandLine;
1+
using MatthiWare.CommandLine;
42
using MatthiWare.CommandLine.Abstractions;
53
using MatthiWare.CommandLine.Abstractions.Command;
64
using MatthiWare.CommandLine.Core.Attributes;
5+
using System;
6+
using System.Linq;
7+
using System.Threading;
78
using Xunit;
89

910
namespace MatthiWare.CommandLine.Tests.Command
1011
{
1112
public class SubCommandTests
1213
{
1314
[Theory]
14-
[InlineData(true)]
15-
[InlineData(false)]
16-
public void TestSubCommandWorksCorrectlyInModel(bool autoExecute)
15+
[InlineData(true, "something", 15, -1)]
16+
[InlineData(false, "something", 15, -1)]
17+
[InlineData(true, "", 15, -1)]
18+
public void TestSubCommandWorksCorrectlyInModel(bool autoExecute, string bla, int i, int n)
1719
{
1820
var lock1 = new ManualResetEventSlim();
1921
var lock2 = new ManualResetEventSlim();
2022

21-
var containerResolver = new CustomInstantiator(lock1, lock2, autoExecute);
23+
var containerResolver = new CustomInstantiator(lock1, lock2, autoExecute, bla, i, n);
2224

2325
var parser = new CommandLineParser<MainModel>(containerResolver);
2426

25-
var result = parser.Parse(new[] { "main", "-b", "something", "sub", "-i", "15", "-n", "-1" });
27+
var result = parser.Parse(new[] { "main", "-b", bla, "sub", "-i", i.ToString(), "-n", n.ToString() });
2628

2729
result.AssertNoErrors();
2830

2931
if (!autoExecute)
32+
{
33+
Assert.All(result.CommandResults.Select(r => r.Executed), Assert.False);
34+
3035
result.ExecuteCommands();
36+
}
3137

3238
Assert.True(lock1.Wait(1000), "MainCommand didn't execute in time.");
3339
Assert.True(lock2.Wait(1000), "SubCommand didn't execute in time.");
40+
41+
Assert.All(result.CommandResults.Select(r => r.Executed), Assert.True);
3442
}
3543

3644
private class CustomInstantiator : IContainerResolver
3745
{
3846
private readonly ManualResetEventSlim lock1;
3947
private readonly ManualResetEventSlim lock2;
4048
private readonly bool autoExecute;
49+
private readonly string bla;
50+
private readonly int i;
51+
private readonly int n;
4152

42-
public CustomInstantiator(ManualResetEventSlim lock1, ManualResetEventSlim lock2, bool autoExecute)
53+
public CustomInstantiator(ManualResetEventSlim lock1, ManualResetEventSlim lock2, bool autoExecute, string bla, int i, int n)
4354
{
4455
this.lock1 = lock1;
4556
this.lock2 = lock2;
4657
this.autoExecute = autoExecute;
58+
this.bla = bla;
59+
this.i = i;
60+
this.n = n;
4761
}
4862

4963
public T Resolve<T>()
5064
{
5165
if (typeof(T) == typeof(MainCommand))
52-
return (T)Activator.CreateInstance(typeof(T), lock1, autoExecute);
66+
return (T)Activator.CreateInstance(typeof(T), lock1, autoExecute, bla, i, n);
5367
else if (typeof(T) == typeof(SubCommand))
54-
return (T)Activator.CreateInstance(typeof(T), lock2, autoExecute);
68+
return (T)Activator.CreateInstance(typeof(T), lock2, autoExecute, bla, i, n);
5569
else
56-
return default;
70+
throw new InvalidCastException($"Unable to resolve {(typeof(T)).Name}");
5771
}
5872
}
59-
}
60-
61-
public class MainCommand : Command<MainModel, SubModel>
62-
{
63-
private readonly ManualResetEventSlim locker;
64-
private readonly bool autoExecute;
6573

66-
public MainCommand(ManualResetEventSlim locker, bool autoExecute)
74+
public class MainCommand : Command<MainModel, SubModel>
6775
{
68-
this.locker = locker;
69-
this.autoExecute = autoExecute;
70-
}
76+
private readonly ManualResetEventSlim locker;
77+
private readonly bool autoExecute;
78+
private readonly string bla;
79+
private readonly int i;
80+
private readonly int n;
7181

72-
public override void OnConfigure(ICommandConfigurationBuilder<SubModel> builder)
73-
{
74-
builder
75-
.Name("main")
76-
.AutoExecute(autoExecute)
77-
.Required();
82+
public MainCommand(ManualResetEventSlim locker, bool autoExecute, string bla, int i, int n)
83+
{
84+
this.locker = locker;
85+
this.autoExecute = autoExecute;
86+
this.bla = bla;
87+
this.i = i;
88+
this.n = n;
89+
}
90+
91+
public override void OnConfigure(ICommandConfigurationBuilder<SubModel> builder)
92+
{
93+
builder
94+
.Name("main")
95+
.AutoExecute(autoExecute)
96+
.Required();
97+
}
98+
99+
public override void OnExecute(MainModel options, SubModel commandOptions)
100+
{
101+
base.OnExecute(options, commandOptions);
102+
103+
Assert.Equal(bla, options.Bla);
104+
Assert.Equal(i, commandOptions.Item);
105+
106+
locker.Set();
107+
}
78108
}
79109

80-
public override void OnExecute(MainModel options, SubModel commandOptions)
110+
public class SubCommand : Command<MainModel, SubSubModel>
81111
{
82-
base.OnExecute(options, commandOptions);
112+
private readonly ManualResetEventSlim locker;
113+
private readonly bool autoExecute;
114+
private readonly string bla;
115+
private readonly int i;
116+
private readonly int n;
83117

84-
locker.Set();
85-
}
86-
}
118+
public SubCommand(ManualResetEventSlim locker, bool autoExecute, string bla, int i, int n)
119+
{
120+
this.locker = locker;
121+
this.autoExecute = autoExecute;
122+
this.bla = bla;
123+
this.i = i;
124+
this.n = n;
125+
}
87126

88-
public class SubCommand : Command<MainModel, SubSubModel>
89-
{
90-
private readonly ManualResetEventSlim locker;
91-
private readonly bool autoExecute;
127+
public override void OnConfigure(ICommandConfigurationBuilder<SubSubModel> builder)
128+
{
129+
builder
130+
.Name("sub")
131+
.AutoExecute(autoExecute)
132+
.Required();
133+
}
92134

93-
public SubCommand(ManualResetEventSlim locker, bool autoExecute)
94-
{
95-
this.locker = locker;
96-
this.autoExecute = autoExecute;
135+
public override void OnExecute(MainModel options, SubSubModel commandOptions)
136+
{
137+
base.OnExecute(options, commandOptions);
138+
139+
Assert.Equal(bla, options.Bla);
140+
Assert.Equal(n, commandOptions.Nothing);
141+
142+
locker.Set();
143+
}
97144
}
98145

99-
public override void OnConfigure(ICommandConfigurationBuilder<SubSubModel> builder)
146+
public class MainModel
100147
{
101-
builder
102-
.Name("sub")
103-
.AutoExecute(autoExecute)
104-
.Required();
148+
[Required, Name("b")]
149+
public string Bla { get; set; }
150+
public MainCommand MainCommand { get; set; }
105151
}
106152

107-
public override void OnExecute(MainModel options, SubSubModel commandOptions)
153+
public class SubModel
108154
{
109-
base.OnExecute(options, commandOptions);
110-
111-
locker.Set();
155+
[Required, Name("i")]
156+
public int Item { get; set; }
157+
public SubCommand SubCommand { get; set; }
112158
}
113-
}
114-
115-
public class MainModel
116-
{
117-
[Required, Name("b")]
118-
public string Bla { get; set; }
119-
public MainCommand MainCommand { get; set; }
120-
}
121159

122-
public class SubModel
123-
{
124-
[Required, Name("i")]
125-
public int Item { get; set; }
126-
public SubCommand SubCommand { get; set; }
127-
}
128-
129-
public class SubSubModel
130-
{
131-
[Required, Name("n")]
132-
public int Nothing { get; set; }
160+
public class SubSubModel
161+
{
162+
[Required, Name("n")]
163+
public int Nothing { get; set; }
164+
}
133165
}
134166
}

CommandLineParser.Tests/CommandLineParserTests.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
using System;
2-
using System.Linq;
3-
using System.Threading;
4-
5-
using MatthiWare.CommandLine.Abstractions;
1+
using MatthiWare.CommandLine.Abstractions;
62
using MatthiWare.CommandLine.Abstractions.Command;
73
using MatthiWare.CommandLine.Abstractions.Models;
84
using MatthiWare.CommandLine.Abstractions.Parsing;
95
using MatthiWare.CommandLine.Core.Attributes;
10-
116
using Moq;
12-
7+
using System;
8+
using System.Linq;
9+
using System.Threading;
1310
using Xunit;
1411

1512
namespace MatthiWare.CommandLine.Tests
@@ -57,8 +54,10 @@ public void CommandLineParserUsesCorrectOptions()
5754
Assert.Equal(opt, parser.ParserOptions);
5855
}
5956

60-
[Fact]
61-
public void CommandLineParserUsesContainerCorrectly()
57+
[Theory]
58+
[InlineData(true)]
59+
[InlineData(false)]
60+
public void CommandLineParserUsesContainerCorrectly(bool generic)
6261
{
6362
var commandMock = new Mock<MyCommand>();
6463
commandMock.Setup(
@@ -72,7 +71,10 @@ public void CommandLineParserUsesContainerCorrectly()
7271

7372
var parser = new CommandLineParser<object>(containerMock.Object);
7473

75-
parser.RegisterCommand<MyCommand, object>();
74+
if (generic)
75+
parser.RegisterCommand<MyCommand, object>();
76+
else
77+
parser.RegisterCommand(typeof(MyCommand), typeof(object));
7678

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

0 commit comments

Comments
 (0)