This commit is contained in:
Alexey Golub
2019-07-17 22:49:46 +03:00
parent 3611aa51e6
commit 4ba9413012
13 changed files with 88 additions and 158 deletions

View File

@@ -1,33 +1,36 @@
using System.Threading.Tasks;
using CliFx.Attributes;
using CliFx.Models;
using CliFx.Services;
using CliFx.Tests.TestObjects;
using NUnit.Framework;
namespace CliFx.Tests
{
public partial class CliApplicationTests
{
[DefaultCommand]
public class TestCommand : Command
{
public override ExitCode Execute() => new ExitCode(13);
}
}
[TestFixture]
public class CliApplicationTests
public partial class CliApplicationTests
{
[Test]
public async Task RunAsync_Test()
{
// Arrange
var command = new TestCommand();
var expectedExitCode = await command.ExecuteAsync();
var commandOptionParser = new CommandOptionParser();
var typeProvider = new TypeProvider(typeof(TestCommand));
var commandOptionConverter = new CommandOptionConverter();
var commandResolver = new CommandResolver(typeProvider, commandOptionConverter);
var application = new CliApplication(commandOptionParser, commandResolver);
var application = new CliApplication(
new CommandOptionParser(),
new CommandResolver(new[] {typeof(TestCommand)}, new CommandOptionConverter()));
// Act
var exitCodeValue = await application.RunAsync();
// Assert
Assert.That(exitCodeValue, Is.EqualTo(expectedExitCode.Value), "Exit code");
Assert.That(exitCodeValue, Is.EqualTo(13), "Exit code");
}
}
}

View File

@@ -3,13 +3,44 @@ using System.Collections;
using System.Collections.Generic;
using CliFx.Models;
using CliFx.Services;
using CliFx.Tests.TestObjects;
using NUnit.Framework;
namespace CliFx.Tests
{
public partial class CommandOptionConverterTests
{
public enum TestEnum
{
Value1,
Value2,
Value3
}
public struct TestStringConstructable
{
public string Value { get; }
public TestStringConstructable(string value)
{
Value = value;
}
}
public struct TestStringParseable
{
public string Value { get; }
private TestStringParseable(string value)
{
Value = value;
}
public static TestStringParseable Parse(string value) => new TestStringParseable(value);
}
}
[TestFixture]
public class CommandOptionConverterTests
public partial class CommandOptionConverterTests
{
private static IEnumerable<TestCaseData> GetTestCases_ConvertOption()
{

View File

@@ -1,14 +1,28 @@
using System.Collections.Generic;
using CliFx.Attributes;
using CliFx.Exceptions;
using CliFx.Models;
using CliFx.Services;
using CliFx.Tests.TestObjects;
using NUnit.Framework;
namespace CliFx.Tests
{
public partial class CommandResolverTests
{
[DefaultCommand]
public class TestCommand : Command
{
[CommandOption("int", 'i', IsRequired = true)]
public int IntOption { get; set; } = 24;
[CommandOption("str", 's')] public string StringOption { get; set; } = "foo bar";
public override ExitCode Execute() => new ExitCode(IntOption, StringOption);
}
}
[TestFixture]
public class CommandResolverTests
public partial class CommandResolverTests
{
private static IEnumerable<TestCaseData> GetTestCases_ResolveCommand()
{
@@ -36,14 +50,6 @@ namespace CliFx.Tests
}),
new TestCommand {IntOption = 13}
);
yield return new TestCaseData(
new CommandOptionSet("command", new[]
{
new CommandOption("int", "13")
}),
new TestCommand {IntOption = 13}
);
}
[Test]
@@ -51,10 +57,7 @@ namespace CliFx.Tests
public void ResolveCommand_Test(CommandOptionSet commandOptionSet, TestCommand expectedCommand)
{
// Arrange
var typeProvider = new TypeProvider(typeof(TestCommand));
var optionConverter = new CommandOptionConverter();
var resolver = new CommandResolver(typeProvider, optionConverter);
var resolver = new CommandResolver(new[] {typeof(TestCommand)}, new CommandOptionConverter());
// Act
var command = resolver.ResolveCommand(commandOptionSet) as TestCommand;
@@ -85,10 +88,7 @@ namespace CliFx.Tests
public void ResolveCommand_IsRequired_Test(CommandOptionSet commandOptionSet)
{
// Arrange
var typeProvider = new TypeProvider(typeof(TestCommand));
var optionConverter = new CommandOptionConverter();
var resolver = new CommandResolver(typeProvider, optionConverter);
var resolver = new CommandResolver(new[] {typeof(TestCommand)}, new CommandOptionConverter());
// Act & Assert
Assert.Throws<CommandResolveException>(() => resolver.ResolveCommand(commandOptionSet));

View File

@@ -1,18 +0,0 @@
using CliFx.Attributes;
using CliFx.Models;
namespace CliFx.Tests.TestObjects
{
[DefaultCommand]
[Command("command")]
public class TestCommand : Command
{
[CommandOption("int", 'i', IsRequired = true)]
public int IntOption { get; set; } = 24;
[CommandOption("str", 's')]
public string StringOption { get; set; } = "foo bar";
public override ExitCode Execute() => new ExitCode(IntOption, StringOption);
}
}

View File

@@ -1,9 +0,0 @@
namespace CliFx.Tests.TestObjects
{
public enum TestEnum
{
Value1,
Value2,
Value3
}
}

View File

@@ -1,12 +0,0 @@
namespace CliFx.Tests.TestObjects
{
public struct TestStringConstructable
{
public string Value { get; }
public TestStringConstructable(string value)
{
Value = value;
}
}
}

View File

@@ -1,17 +0,0 @@
namespace CliFx.Tests.TestObjects
{
public partial struct TestStringParseable
{
public string Value { get; }
private TestStringParseable(string value)
{
Value = value;
}
}
public partial struct TestStringParseable
{
public static TestStringParseable Parse(string value) => new TestStringParseable(value);
}
}

View File

@@ -1,11 +1,10 @@
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using CliFx.Services;
namespace CliFx
{
public partial class CliApplication : ICliApplication
public class CliApplication : ICliApplication
{
private readonly ICommandOptionParser _commandOptionParser;
private readonly ICommandResolver _commandResolver;
@@ -17,7 +16,7 @@ namespace CliFx
}
public CliApplication()
: this(new CommandOptionParser(), GetDefaultCommandResolver(Assembly.GetCallingAssembly()))
: this(new CommandOptionParser(), new CommandResolver(new CommandOptionConverter()))
{
}
@@ -31,15 +30,4 @@ namespace CliFx
return exitCode.Value;
}
}
public partial class CliApplication
{
private static ICommandResolver GetDefaultCommandResolver(Assembly assembly)
{
var typeProvider = TypeProvider.FromAssembly(assembly);
var commandOptionConverter = new CommandOptionConverter();
return new CommandResolver(typeProvider, commandOptionConverter);
}
}
}

View File

@@ -1,12 +1,9 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Threading.Tasks;
namespace CliFx
{
public static class Extensions
{
public static Task<int> RunAsync(this ICliApplication application) =>
application.RunAsync(Environment.GetCommandLineArgs().Skip(1).ToArray());
public static Task<int> RunAsync(this ICliApplication application) => application.RunAsync(new string[0]);
}
}

View File

@@ -36,7 +36,7 @@ namespace CliFx.Internal
// Derives from Command
type.IsDerivedFrom(typeof(Command)) &&
// Marked with DefaultCommandAttribute or CommandAttribute
(type.IsDefined(typeof(DefaultCommandAttribute)) || type.IsDefined(typeof(CommandAttribute)));
type.IsDefined(typeof(CommandAttribute));
public static CommandType Initialize(Type type)
{

View File

@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using CliFx.Attributes;
using CliFx.Exceptions;
using CliFx.Internal;
@@ -8,18 +9,23 @@ using CliFx.Models;
namespace CliFx.Services
{
public class CommandResolver : ICommandResolver
public partial class CommandResolver : ICommandResolver
{
private readonly ITypeProvider _typeProvider;
private readonly IReadOnlyList<Type> _availableTypes;
private readonly ICommandOptionConverter _commandOptionConverter;
public CommandResolver(ITypeProvider typeProvider, ICommandOptionConverter commandOptionConverter)
public CommandResolver(IReadOnlyList<Type> availableTypes, ICommandOptionConverter commandOptionConverter)
{
_typeProvider = typeProvider;
_availableTypes = availableTypes;
_commandOptionConverter = commandOptionConverter;
}
private IEnumerable<CommandType> GetCommandTypes() => CommandType.GetCommandTypes(_typeProvider.GetTypes());
public CommandResolver(ICommandOptionConverter commandOptionConverter)
: this(GetDefaultAvailableTypes(), commandOptionConverter)
{
}
private IEnumerable<CommandType> GetCommandTypes() => CommandType.GetCommandTypes(_availableTypes);
private CommandType GetDefaultCommandType()
{
@@ -100,4 +106,9 @@ namespace CliFx.Services
return command;
}
}
public partial class CommandResolver
{
private static IReadOnlyList<Type> GetDefaultAvailableTypes() => Assembly.GetEntryAssembly()?.GetExportedTypes() ?? new Type[0];
}
}

View File

@@ -1,10 +0,0 @@
using System;
using System.Collections.Generic;
namespace CliFx.Services
{
public interface ITypeProvider
{
IReadOnlyList<Type> GetTypes();
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace CliFx.Services
{
public partial class TypeProvider : ITypeProvider
{
private readonly IReadOnlyList<Type> _types;
public TypeProvider(IReadOnlyList<Type> types)
{
_types = types;
}
public TypeProvider(params Type[] types)
: this((IReadOnlyList<Type>) types)
{
}
public IReadOnlyList<Type> GetTypes() => _types;
}
public partial class TypeProvider
{
public static TypeProvider FromAssembly(Assembly assembly) => new TypeProvider(assembly.GetExportedTypes());
public static TypeProvider FromAssemblies(IReadOnlyList<Assembly> assemblies) =>
new TypeProvider(assemblies.SelectMany(a => a.ExportedTypes).ToArray());
public static TypeProvider FromAssemblies(params Assembly[] assemblies) => FromAssemblies((IReadOnlyList<Assembly>) assemblies);
}
}