Skip to content

Commit a401c08

Browse files
Releases/0.4 (#79)
* Improve Dependency Injection (#71) * Rename to BaseArgumentResolver * Add resolvers as part of DI * Remove obsolete IArgumentResolverFactory * Remove IContainerResolver will be replaced with IServiceProvider * Use IServiceProvider for resolving fixes #70 * Update readme * Add more services to the ServiceCollection * Use DI in printer tests * Add IUsagePrinter to interface * Improve DI flow * Use ActivatorUtilities to instantiate CommandLineOption * Use CreateInstance instead of newing up a new instance. * Improve extension method to allow chaining * Add basic logger * Add logger service in tests * Add logging in all tests! * Allow non generic command to be registered (#78) * Allow non generic commands to be registered (#77) * Refactor RegisterCommand<TCommand> * Allow non generic commands to be discovered * Register non generic command using model (#83) * Allow non generic commands to be registered using models fixes #82 and improves #77 * Fix issue with registering non generic sub command * Remove IgnoreAttribute fixes #80 (#84) * Update version to 0.4 * Bump FluentValidation from 8.5.1 to 9.2.2 (#86) * Bump FluentValidation from 8.5.1 to 9.2.2 Bumps [FluentValidation](https://github.com/JeremySkinner/fluentvalidation) from 8.5.1 to 9.2.2. - [Release notes](https://github.com/JeremySkinner/fluentvalidation/releases) - [Changelog](https://github.com/FluentValidation/FluentValidation/blob/master/Changelog.txt) - [Commits](FluentValidation/FluentValidation@8.5.1...9.2.2) Signed-off-by: dependabot[bot] <support@github.com> * Fix build, upgrade fluent validation https://docs.fluentvalidation.net/en/latest/upgrading-to-9.html * bump version in nuspec Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Matthias Beerens <3512339+Matthiee@users.noreply.github.com> * Cleanup 0.4 release (#85) * Refactor InitializeModel into seperate class * Split complex ParseOptions method * Add braces * Split complex ParseCommands and ParseCommandsAsync method * Refactor if statements in HelpRequested method * Remove redundant assignment * Remove unused usings * Revert returning help requested state to caller * Improve if statements * Add xml comment * Add more xml documentation * Add validator xml comments * Update sample app * Improve test coverage * Add xml comment Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1 parent 46b2fee commit a401c08

File tree

102 files changed

+2262
-1454
lines changed

Some content is hidden

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

102 files changed

+2262
-1454
lines changed
+81
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
using MatthiWare.CommandLine.Abstractions.Command;
2+
using Microsoft.Extensions.DependencyInjection;
3+
using Moq;
4+
using Xunit;
5+
using Xunit.Abstractions;
6+
7+
namespace MatthiWare.CommandLine.Tests
8+
{
9+
public class BasicDITests : TestBase
10+
{
11+
public BasicDITests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
12+
{
13+
}
14+
15+
[Fact]
16+
public void CommandLineParserUsesInjectedServiceCorrectly()
17+
{
18+
var mockedService = new Mock<MySerice>();
19+
20+
mockedService
21+
.Setup(_ => _.Call())
22+
.Verifiable();
23+
24+
Services.AddSingleton(mockedService.Object);
25+
26+
var parser = new CommandLineParser(Services);
27+
28+
parser.RegisterCommand<MyCommandThatUsesService>();
29+
30+
var result = parser.Parse(new[] { "app.exe", "cmd" });
31+
32+
result.AssertNoErrors();
33+
34+
mockedService.Verify(_ => _.Call(), Times.Once());
35+
}
36+
37+
[Fact]
38+
public void CommandLineParserServiceResolvesCorrectly()
39+
{
40+
var mockedService = Mock.Of<MySerice>();
41+
42+
Services.AddSingleton(mockedService);
43+
44+
var parser = new CommandLineParser(Services);
45+
46+
var resolved = parser.Services.GetRequiredService<MySerice>();
47+
48+
Assert.Equal(mockedService, resolved);
49+
}
50+
51+
public class MyCommandThatUsesService : Command<object>
52+
{
53+
private readonly MySerice serice;
54+
55+
public MyCommandThatUsesService(MySerice serice)
56+
{
57+
this.serice = serice ?? throw new System.ArgumentNullException(nameof(serice));
58+
}
59+
60+
public override void OnConfigure(ICommandConfigurationBuilder builder)
61+
{
62+
builder
63+
.Name("cmd")
64+
.AutoExecute(true)
65+
.Required(true);
66+
}
67+
68+
public override void OnExecute()
69+
{
70+
base.OnExecute();
71+
72+
serice.Call();
73+
}
74+
}
75+
76+
public interface MySerice
77+
{
78+
void Call();
79+
}
80+
}
81+
}

CommandLineParser.Tests/Command/CommandDiscoveryTests.cs

+112-9
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,30 @@
22
using Xunit;
33
using MatthiWare.CommandLine.Abstractions.Command;
44
using System.Reflection;
5+
using System.Threading.Tasks;
6+
using System.Threading;
7+
using MatthiWare.CommandLine.Abstractions.Usage;
8+
using Moq;
9+
using Microsoft.Extensions.DependencyInjection;
10+
using Xunit.Abstractions;
11+
using TestAssembly;
12+
using MatthiWare.CommandLine.Abstractions.Parsing;
13+
using MatthiWare.CommandLine.Abstractions.Models;
514

615
namespace MatthiWare.CommandLine.Tests.Command
716
{
8-
public class CommandDiscoveryTests
17+
public class CommandDiscoveryTests : TestBase
918
{
19+
public CommandDiscoveryTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
20+
{
21+
}
22+
1023
[Fact]
1124
public void DiscoverCommandFromAssemblyContainsCorrectTypes()
1225
{
1326
var cmdDiscovery = new CommandDiscoverer();
1427

15-
var resultTypes = cmdDiscovery.DiscoverCommandTypes(typeof(CommandDiscoveryTests), new[] { Assembly.GetExecutingAssembly() });
28+
var resultTypes = cmdDiscovery.DiscoverCommandTypes(typeof(SomeBaseType), new[] { Assembly.GetExecutingAssembly() });
1629

1730
var invalidAbstractCommand = typeof(AbstractCommand);
1831
var wrongGenericTypeCommand = typeof(WrongGenericTypeCommand);
@@ -28,27 +41,117 @@ public void DiscoverCommandFromAssemblyContainsCorrectTypes()
2841
[Fact]
2942
public void DiscoveredCommandsAreRegisteredCorrectly()
3043
{
31-
var parser = new CommandLineParser<CommandDiscoveryTests>();
44+
var parser = new CommandLineParser<SomeBaseType>(Services);
3245

3346
parser.DiscoverCommands(Assembly.GetExecutingAssembly());
3447

35-
Assert.Equal(2, parser.Commands.Count);
48+
Assert.Equal(3, parser.Commands.Count);
49+
}
50+
51+
[Fact]
52+
public async Task CommandDiscoveryWithInjectedServices()
53+
{
54+
var envMock = new Mock<IEnvironmentVariablesService>();
55+
envMock
56+
.SetupGet(_ => _.NoColorRequested)
57+
.Returns(true);
58+
59+
var myServiceMock = new Mock<IMyService>();
60+
myServiceMock
61+
.Setup(_ => _.Call())
62+
.Verifiable();
63+
64+
Services.AddSingleton(envMock.Object);
65+
Services.AddSingleton(myServiceMock.Object);
66+
67+
var parser = new CommandLineParser<MyCommandWithInjectionsOptions>(Services);
68+
69+
parser.DiscoverCommands(Assembly.GetExecutingAssembly());
70+
71+
var result = await parser.ParseAsync(new[] { "app.exe", "cmd" });
72+
73+
myServiceMock.Verify(_ => _.Call(), Times.Once());
74+
}
75+
76+
[Fact]
77+
public async Task NonGenericCommandCanBeDiscovered()
78+
{
79+
var argResolverMock = new Mock<IArgumentResolver<NonGenericDiscoverableCommand>>();
80+
argResolverMock
81+
.Setup(_ => _.CanResolve(It.IsAny<ArgumentModel>()))
82+
.Verifiable();
83+
84+
Services.AddSingleton(argResolverMock.Object);
85+
86+
var parser = new CommandLineParser(Services);
87+
88+
parser.DiscoverCommands(typeof(NonGenericDiscoverableCommand).Assembly);
89+
90+
var result = await parser.ParseAsync(new[] { "app.exe", "cmd" });
91+
92+
Assert.True(parser.Commands.Count == 1);
93+
94+
argResolverMock.Verify(_ => _.CanResolve(It.IsAny<ArgumentModel>()), Times.Once());
95+
}
96+
97+
public abstract class AbstractCommand : Command<SomeBaseType>
98+
{
3699
}
37100

38-
public abstract class AbstractCommand : Command<CommandDiscoveryTests>
101+
public class WrongGenericTypeCommand : Command<object>
39102
{
40103
}
41104

42-
public abstract class WrongGenericTypeCommand : Command<object>
105+
public class ValidCommand : Command<SomeBaseType>
43106
{
44107
}
45108

46-
public class ValidCommand : Command<CommandDiscoveryTests>
109+
public class ValidCommand2 : Command<SomeBaseType, object>
47110
{
48111
}
49112

50-
public class ValidCommand2 : Command<CommandDiscoveryTests, object>
51-
{
113+
public class MyCommandWithInjectionsOptions
114+
{
115+
}
116+
117+
public class SomeBaseType
118+
{
119+
}
120+
121+
public interface IMyService
122+
{
123+
void Call();
124+
}
125+
126+
public class MyCommandWithInjections : Command<MyCommandWithInjectionsOptions>
127+
{
128+
private readonly IMyService service;
129+
private readonly IUsagePrinter usagePrinter;
130+
private readonly IEnvironmentVariablesService environment;
131+
132+
public MyCommandWithInjections(IMyService service, IUsagePrinter usagePrinter, IEnvironmentVariablesService environment)
133+
{
134+
this.service = service ?? throw new System.ArgumentNullException(nameof(service));
135+
this.usagePrinter = usagePrinter ?? throw new System.ArgumentNullException(nameof(usagePrinter));
136+
this.environment = environment ?? throw new System.ArgumentNullException(nameof(environment));
137+
}
138+
139+
public override void OnConfigure(ICommandConfigurationBuilder builder)
140+
{
141+
builder
142+
.Name("cmd")
143+
.AutoExecute(true);
144+
}
145+
146+
public override Task OnExecuteAsync(MyCommandWithInjectionsOptions options, CancellationToken cancellationToken)
147+
{
148+
service.Call();
149+
usagePrinter.PrintUsage();
150+
151+
Assert.True(environment.NoColorRequested);
152+
153+
return base.OnExecuteAsync(options, cancellationToken);
154+
}
52155
}
53156
}
54157
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using MatthiWare.CommandLine.Abstractions.Command;
2+
using MatthiWare.CommandLine.Core.Attributes;
3+
using System.Threading.Tasks;
4+
using Xunit;
5+
using Xunit.Abstractions;
6+
7+
namespace MatthiWare.CommandLine.Tests.Command
8+
{
9+
public class CommandInModelTests : TestBase
10+
{
11+
public CommandInModelTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
12+
{
13+
}
14+
15+
#region FindCommandsInModel
16+
17+
[Fact]
18+
public async Task FindCommandsInModel()
19+
{
20+
var parser = new CommandLineParser<ModelWithCommands>(Services);
21+
22+
Assert.Equal(3, parser.Commands.Count);
23+
}
24+
25+
public class ModelWithCommands
26+
{
27+
public NonGenericCommand NonGenericCommand { get; set; }
28+
public GenericCommandWithParentOptions GenericCommandWithParentOptions { get; set; }
29+
public GenericCommandWithOwnOptions GenericCommandWithOwnOptions { get; set; }
30+
}
31+
32+
public class ModelWithOptions
33+
{
34+
[Name("i", "input")]
35+
public string MyOption { get; set; }
36+
}
37+
38+
public class NonGenericCommand : Abstractions.Command.Command
39+
{
40+
public override void OnConfigure(ICommandConfigurationBuilder builder)
41+
{
42+
builder.Name(nameof(NonGenericCommand));
43+
}
44+
}
45+
46+
public class GenericCommandWithParentOptions : Command<ModelWithCommands>
47+
{
48+
public override void OnConfigure(ICommandConfigurationBuilder builder)
49+
{
50+
builder.Name(nameof(GenericCommandWithParentOptions));
51+
}
52+
}
53+
54+
public class GenericCommandWithOwnOptions : Command<ModelWithCommands, ModelWithOptions>
55+
{
56+
public override void OnConfigure(ICommandConfigurationBuilder builder)
57+
{
58+
builder.Name(nameof(GenericCommandWithOwnOptions));
59+
}
60+
}
61+
62+
#endregion
63+
64+
#region SubCommandFindCommandsInModel
65+
66+
[Fact]
67+
public async Task FindCommandsInCommandModel()
68+
{
69+
var parser = new CommandLineParser(Services);
70+
71+
parser.RegisterCommand<GenericSubCommandWithOwnOptions, SubCommandModelWithCommands>();
72+
73+
Assert.Equal(3, ((ICommandLineCommandContainer)parser.Commands[0]).Commands.Count);
74+
}
75+
76+
public class SubCommandModelWithCommands
77+
{
78+
public NonGenericCommand NonGenericCommand { get; set; }
79+
public SubCommandWithModelOptions SubCommandWithModelOptions { get; set; }
80+
public SimpleGenericCommand SimpleGenericCommand { get; set; }
81+
}
82+
83+
public class GenericSubCommandWithOwnOptions : Command<object, SubCommandModelWithCommands>
84+
{
85+
public override void OnConfigure(ICommandConfigurationBuilder builder)
86+
{
87+
builder.Name(nameof(GenericSubCommandWithOwnOptions));
88+
}
89+
}
90+
91+
public class SubCommandWithModelOptions : Command<object, ModelWithOptions>
92+
{
93+
public override void OnConfigure(ICommandConfigurationBuilder builder)
94+
{
95+
builder.Name(nameof(SubCommandWithModelOptions));
96+
}
97+
}
98+
99+
public class SimpleGenericCommand : Command<object>
100+
{
101+
public override void OnConfigure(ICommandConfigurationBuilder builder)
102+
{
103+
builder.Name(nameof(SimpleGenericCommand));
104+
}
105+
}
106+
107+
#endregion
108+
}
109+
}

0 commit comments

Comments
 (0)