Skip to content

Commit 95d86f3

Browse files
authored
Command improvements (#18)
* WIP Improve commands * Improve command system. * Update resolvers to use generic base class instead of interface * Add enum resolver * Accessing Result variable no longer results in exception when there are errors parsing it. * Add tests for enums * Ignore attribute. * Make command more generic * Adds optionless command see issue #11 * Remove not needed class * Enhancement for optionless commands #11 Fixes #16 * Remove and sort usings * Split commands up into seperate files.
1 parent f0219b8 commit 95d86f3

47 files changed

Lines changed: 1133 additions & 283 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CommandLineParser.Tests/Command/CommandTests.cs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Linq;
4-
using System.Text;
1+
using System.Linq;
52
using MatthiWare.CommandLine;
3+
using MatthiWare.CommandLine.Abstractions;
4+
using MatthiWare.CommandLine.Abstractions.Command;
65
using Xunit;
76

87
namespace MatthiWare.CommandLineParser.Tests.Command
@@ -24,5 +23,58 @@ public void ConfiguringCommandsIncreasesTotalCommandInParser()
2423
Assert.NotNull(parser.Commands.First(cmd => cmd.ShortName.Equals("y")));
2524
}
2625

26+
[Fact]
27+
public void AddOptionLessCommand()
28+
{
29+
var parser = new CommandLineParser<object>();
30+
31+
parser.AddCommand().Name("x");
32+
parser.AddCommand().Name("y");
33+
34+
Assert.Equal(2, parser.Commands.Count);
35+
36+
Assert.NotNull(parser.Commands.First(cmd => cmd.ShortName.Equals("x")));
37+
Assert.NotNull(parser.Commands.First(cmd => cmd.ShortName.Equals("y")));
38+
}
39+
40+
[Fact]
41+
public void AddCommandType()
42+
{
43+
var parser = new CommandLineParser<object>();
44+
45+
parser.RegisterCommand<MyComand>();
46+
47+
Assert.Equal(1, parser.Commands.Count);
48+
49+
Assert.NotNull(parser.Commands.First(cmd => cmd.ShortName.Equals("-bla")));
50+
}
51+
52+
[Fact]
53+
public void AddCommandTypeWithGenericOption()
54+
{
55+
var parser = new CommandLineParser<object>();
56+
57+
parser.RegisterCommand<MyComand, object>();
58+
59+
Assert.Equal(1, parser.Commands.Count);
60+
61+
Assert.NotNull(parser.Commands.First(cmd => cmd.ShortName.Equals("-bla")));
62+
}
63+
64+
private class MyComand : Command<object, object>
65+
{
66+
public override void OnConfigure(ICommandConfigurationBuilder builder)
67+
{
68+
base.OnConfigure(builder);
69+
70+
builder.Name("-bla").Required();
71+
}
72+
73+
public override void OnExecute(object options, object commandOptions)
74+
{
75+
base.OnExecute(options, commandOptions);
76+
}
77+
}
78+
2779
}
2880
}

CommandLineParser.Tests/CommandLineParser.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
<IsPackable>false</IsPackable>
77

88
<RootNamespace>MatthiWare.CommandLineParser.Tests</RootNamespace>
9+
10+
<LangVersion>7.1</LangVersion>
911
</PropertyGroup>
1012

1113
<ItemGroup>

CommandLineParser.Tests/CommandLineParserTests.cs

Lines changed: 121 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,70 @@
11
using System.Threading;
22
using MatthiWare.CommandLine;
3+
using MatthiWare.CommandLine.Abstractions;
4+
using MatthiWare.CommandLine.Abstractions.Command;
5+
using MatthiWare.CommandLine.Abstractions.Models;
6+
using MatthiWare.CommandLine.Abstractions.Parsing;
7+
using MatthiWare.CommandLine.Core.Attributes;
8+
using Moq;
39
using Xunit;
410

511
namespace MatthiWare.CommandLineParser.Tests
612
{
713
public class CommandLineParserTests
814
{
15+
public class MyCommand : Command<object, object>
16+
{
17+
public override void OnConfigure(ICommandConfigurationBuilder builder)
18+
{
19+
builder.Name("my");
20+
}
21+
}
22+
23+
[Fact]
24+
public void CommandLineParserUsesContainerCorrectly()
25+
{
26+
var commandMock = new Mock<MyCommand>();
27+
//commandMock.Setup(c => c.OnConfigure(It.IsAny<ICommandConfigurationBuilder>())).Verifiable("OnConfigure not called");
28+
commandMock.Setup(c => c.OnExecute(It.IsAny<object>(), It.IsAny<object>())).Verifiable("OnExecute not called");
29+
30+
var containerMock = new Mock<IContainerResolver>();
31+
containerMock.Setup(c => c.Resolve<MyCommand>()).Returns(commandMock.Object).Verifiable();
32+
33+
var parser = new CommandLineParser<object>(containerMock.Object);
34+
35+
parser.RegisterCommand<MyCommand, object>();
36+
37+
var result = parser.Parse(new[] { "app.exe", "my" });
38+
39+
Assert.False(result.HasErrors);
40+
41+
commandMock.VerifyAll();
42+
containerMock.VerifyAll();
43+
}
44+
45+
[Fact]
46+
public void CommandLineParserUsesArgumentFactoryCorrectly()
47+
{
48+
var resolverMock = new Mock<ArgumentResolver<string>>();
49+
resolverMock.Setup(_ => _.CanResolve(It.IsAny<ArgumentModel>())).Returns(true).Verifiable();
50+
resolverMock.Setup(_ => _.Resolve(It.IsAny<ArgumentModel>())).Returns("return").Verifiable();
51+
52+
var argResolverFactory = new Mock<IArgumentResolverFactory>();
53+
argResolverFactory.Setup(c => c.Contains(typeof(string))).Returns(true);
54+
argResolverFactory.Setup(c => c.CreateResolver(typeof(string))).Returns(resolverMock.Object).Verifiable();
55+
56+
var parser = new CommandLineParser<AddOption>(argResolverFactory.Object);
57+
58+
parser.Configure(p => p.Message).Name("-m");
59+
60+
var result = parser.Parse(new[] { "app.exe", "-m" });
61+
62+
Assert.False(result.HasErrors);
63+
64+
resolverMock.VerifyAll();
65+
argResolverFactory.Verify();
66+
}
67+
968
[Fact]
1069
public void ParseTests()
1170
{
@@ -25,6 +84,27 @@ public void ParseTests()
2584
Assert.Equal("test", parsed.Result.Option1);
2685
}
2786

87+
[Theory]
88+
[InlineData(new[] { "app.exe", "-e", "Opt1" }, false, EnumOption.Opt1)]
89+
[InlineData(new[] { "app.exe", "-e", "opt1" }, false, EnumOption.Opt1)]
90+
[InlineData(new[] { "app.exe", "-e", "Opt2" }, false, EnumOption.Opt2)]
91+
[InlineData(new[] { "app.exe", "-e", "bla" }, true, default(EnumOption))]
92+
[InlineData(new[] { "app.exe", "-e" }, true, default(EnumOption))]
93+
public void ParseEnumInArguments(string[] args, bool hasErrors, EnumOption enumOption)
94+
{
95+
var parser = new CommandLineParser<EnumOptions>();
96+
97+
parser.Configure(opt => opt.EnumOption)
98+
.Name("-e")
99+
.Required();
100+
101+
var result = parser.Parse(args);
102+
103+
Assert.Equal(hasErrors, result.HasErrors);
104+
105+
Assert.Equal(enumOption, result.Result.EnumOption);
106+
}
107+
28108
[Theory]
29109
[InlineData(new[] { "app.exe", "-1", "message1", "-2", "-3" }, "message1", "message2", "message3")]
30110
[InlineData(new[] { "app.exe", "-1", "-2", "message2", "-3" }, "message1", "message2", "message3")]
@@ -59,6 +139,26 @@ public void ParseWithDefaults(string[] args, string result1, string result2, str
59139
Assert.Equal(result3, parsed.Result.Option3);
60140
}
61141

142+
[Fact]
143+
public void ParseWithCustomParserInAttributeConfiguredModelTests()
144+
{
145+
var resolver = new Mock<ArgumentResolver<object>>();
146+
147+
var obj = new object();
148+
149+
resolver.Setup(_ => _.CanResolve(It.IsAny<ArgumentModel>())).Returns(true);
150+
resolver.Setup(_ => _.Resolve(It.IsAny<ArgumentModel>())).Returns(obj);
151+
152+
var parser = new CommandLineParser<ObjOption>();
153+
parser.ArgumentResolverFactory.Register(resolver.Object);
154+
155+
var result = parser.Parse(new[] { "app.exe", "--p", "sample" });
156+
157+
Assert.False(result.HasErrors);
158+
159+
Assert.Same(obj, result.Result.Param);
160+
}
161+
62162
[Fact]
63163
public void ParseWithCommandTests()
64164
{
@@ -73,9 +173,10 @@ public void ParseWithCommandTests()
73173

74174
var addCmd = parser.AddCommand<AddOption>()
75175
.Name("-A", "--Add")
76-
.OnExecuting(x =>
176+
.OnExecuting((opt, cmdOpt) =>
77177
{
78-
Assert.Equal("my message", x.Message);
178+
Assert.Equal("test", opt.Option1);
179+
Assert.Equal("my message", cmdOpt.Message);
79180
wait.Set();
80181
});
81182

@@ -107,7 +208,7 @@ public void ParseCommandTests(string[] args, string result1, string result2)
107208
parser.AddCommand<AddOption>()
108209
.Name("-a", "--add")
109210
.Required()
110-
.OnExecuting(r => Assert.Equal(result2, r.Message))
211+
.OnExecuting((opt1, opt2) => Assert.Equal(result2, opt2.Message))
111212
.Configure(c => c.Message)
112213
.Name("-m", "--message")
113214
.Required();
@@ -120,8 +221,6 @@ public void ParseCommandTests(string[] args, string result1, string result2)
120221

121222
Assert.False(result.HasErrors);
122223

123-
result.ExecuteCommands();
124-
125224
Assert.Equal(result1, result.Result.Message);
126225
}
127226

@@ -156,6 +255,12 @@ public void ConfigureTests()
156255
Assert.False(option.HasDefault);
157256
}
158257

258+
private class ObjOption
259+
{
260+
[Name("--p"), Required]
261+
public object Param { get; set; }
262+
}
263+
159264
private class AddOption
160265
{
161266
public string Message { get; set; }
@@ -167,6 +272,17 @@ private class Options
167272
public bool Option2 { get; set; }
168273
}
169274

275+
public enum EnumOption
276+
{
277+
Opt1,
278+
Opt2
279+
}
280+
281+
public class EnumOptions
282+
{
283+
public EnumOption EnumOption { get; set; }
284+
}
285+
170286
private class OptionsWithThreeParams
171287
{
172288
public string Option1 { get; set; }

CommandLineParser.Tests/OptionBuilderTest.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using MatthiWare.CommandLine.Abstractions;
1+
using System;
2+
using MatthiWare.CommandLine.Abstractions;
23
using MatthiWare.CommandLine.Abstractions.Parsing;
34
using MatthiWare.CommandLine.Core;
45
using Moq;
@@ -12,7 +13,10 @@ public class OptionBuilderTest
1213
public void OptionBuilderConfiguresOptionCorrectly()
1314
{
1415
var resolverMock = new Mock<ICommandLineArgumentResolver<string>>();
15-
var option = new CommandLineOption(new object(), XUnitExtensions.CreateLambda<object, string>(o => o.ToString()), resolverMock.Object);
16+
var resolverFactoryMock = new Mock<IArgumentResolverFactory>();
17+
resolverFactoryMock.Setup(_ => _.CreateResolver(It.IsAny<Type>())).Returns(resolverMock.Object);
18+
19+
var option = new CommandLineOption(new object(), XUnitExtensions.CreateLambda<object, string>(o => o.ToString()), resolverFactoryMock.Object);
1620
var builder = option as IOptionBuilder;
1721

1822
string sDefault = "default";

CommandLineParser.Tests/Parsing/ResolverFactoryTest.cs

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,51 @@ public class ResolverFactoryTest
1212
{
1313
public class RandomType { }
1414

15+
public enum Output
16+
{
17+
Verbose,
18+
Info,
19+
Error
20+
}
21+
1522
[Fact]
1623
public void ContainsWork()
1724
{
18-
var factory = new ResolverFactory();
25+
var factory = new DefaultArgumentResolverFactory();
1926

2027
Assert.True(factory.Contains<string>());
2128
Assert.True(factory.Contains<int>());
22-
Assert.True(factory.Contains<int>());
29+
Assert.True(factory.Contains<double>());
30+
Assert.True(factory.Contains<bool>());
31+
Assert.True(factory.Contains<Output>());
2332

2433
Assert.False(factory.Contains<RandomType>());
2534
}
2635

36+
[Fact]
37+
public void CreateEnumResolver()
38+
{
39+
var factory = new DefaultArgumentResolverFactory();
40+
41+
var output = factory.CreateResolver<Output>();
42+
var output2 = factory.CreateResolver(typeof(Output));
43+
44+
Assert.NotNull(output);
45+
Assert.NotNull(output2);
46+
47+
Assert.Same(output, output2);
48+
}
49+
2750
[Fact]
2851
public void RegisterAndGet()
2952
{
3053
var instance = new RandomType();
3154

32-
var mockResolver = new Mock<ICommandLineArgumentResolver<RandomType>>();
55+
var mockResolver = new Mock<ArgumentResolver<RandomType>>();
3356
mockResolver.Setup(_ => _.CanResolve(It.IsAny<ArgumentModel>())).Returns(true);
3457
mockResolver.Setup(_ => _.Resolve(It.IsAny<ArgumentModel>())).Returns(instance);
3558

36-
var factory = new ResolverFactory();
59+
var factory = new DefaultArgumentResolverFactory();
3760

3861
factory.Register(mockResolver.Object);
3962

@@ -51,9 +74,9 @@ public void RegisterAndGet()
5174
[Fact]
5275
public void RegisterOverrideWorks()
5376
{
54-
var mockResolver = new Mock<ICommandLineArgumentResolver<string>>();
77+
var mockResolver = new Mock<ArgumentResolver<string>>();
5578

56-
var factory = new ResolverFactory();
79+
var factory = new DefaultArgumentResolverFactory();
5780

5881
factory.Register(typeof(string), mockResolver.Object.GetType(), true);
5982
factory.Register<string, StringResolver>(true);
@@ -62,11 +85,39 @@ public void RegisterOverrideWorks()
6285
[Fact]
6386
public void RegisterThrowsException()
6487
{
65-
var mockResolver = new Mock<ICommandLineArgumentResolver<string>>();
88+
var mockResolver = new Mock<ArgumentResolver<string>>();
6689

67-
var factory = new ResolverFactory();
90+
var factory = new DefaultArgumentResolverFactory();
6891

6992
Assert.Throws<ArgumentException>(() => factory.Register<string, StringResolver>());
7093
}
94+
95+
[Fact]
96+
public void RegisterObjectResolver()
97+
{
98+
var resolver = new Mock<ArgumentResolver<object>>();
99+
100+
var obj = new object();
101+
102+
resolver.Setup(_ => _.CanResolve(It.IsAny<ArgumentModel>())).Returns(true);
103+
resolver.Setup(_ => _.Resolve(It.IsAny<ArgumentModel>())).Returns(obj);
104+
105+
var factory = new DefaultArgumentResolverFactory();
106+
var dummyArg = new ArgumentModel();
107+
108+
factory.Register(resolver.Object);
109+
110+
var createdResolver_1 = factory.CreateResolver(typeof(object));
111+
var createdResolver_2 = factory.CreateResolver<object>();
112+
113+
Assert.NotNull(createdResolver_1);
114+
Assert.NotNull(createdResolver_2);
115+
116+
Assert.True(createdResolver_1.CanResolve(dummyArg));
117+
Assert.True(createdResolver_2.CanResolve(dummyArg));
118+
119+
Assert.Same(obj, createdResolver_1.Resolve(dummyArg));
120+
Assert.Same(obj, createdResolver_2.Resolve(dummyArg));
121+
}
71122
}
72123
}

0 commit comments

Comments
 (0)