mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Get rid of internal tests
Move all tests to e2e+ level
This commit is contained in:
@@ -18,7 +18,7 @@ namespace CliFx.Benchmarks
|
|||||||
|
|
||||||
[Benchmark(Description = "CliFx", Baseline = true)]
|
[Benchmark(Description = "CliFx", Baseline = true)]
|
||||||
public async ValueTask<int> ExecuteWithCliFx() =>
|
public async ValueTask<int> ExecuteWithCliFx() =>
|
||||||
await new CliApplicationBuilder().AddCommand(typeof(CliFxCommand)).Build().RunAsync(Arguments, new Dictionary<string, string>());
|
await new CliApplicationBuilder().AddCommand<CliFxCommand>().Build().RunAsync(Arguments, new Dictionary<string, string>());
|
||||||
|
|
||||||
[Benchmark(Description = "System.CommandLine")]
|
[Benchmark(Description = "System.CommandLine")]
|
||||||
public async Task<int> ExecuteWithSystemCommandLine() =>
|
public async Task<int> ExecuteWithSystemCommandLine() =>
|
||||||
|
|||||||
@@ -1,179 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class ApplicationSpecs
|
|
||||||
{
|
|
||||||
[Command]
|
|
||||||
private class DefaultCommand : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class AnotherDefaultCommand : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class NonImplementedCommand
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private class NonAnnotatedCommand : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("dup")]
|
|
||||||
private class DuplicateNameCommandA : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("dup")]
|
|
||||||
private class DuplicateNameCommandB : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class DuplicateParameterOrderCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(13)]
|
|
||||||
public string? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(13)]
|
|
||||||
public string? ParameterB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class DuplicateParameterNameCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0, Name = "param")]
|
|
||||||
public string? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(1, Name = "param")]
|
|
||||||
public string? ParameterB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class MultipleNonScalarParametersCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0)]
|
|
||||||
public IReadOnlyList<string>? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(1)]
|
|
||||||
public IReadOnlyList<string>? ParameterB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class NonLastNonScalarParameterCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0)]
|
|
||||||
public IReadOnlyList<string>? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(1)]
|
|
||||||
public string? ParameterB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class EmptyOptionNameCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("")]
|
|
||||||
public string? Apples { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class SingleCharacterOptionNameCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("a")]
|
|
||||||
public string? Apples { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class DuplicateOptionNamesCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("fruits")]
|
|
||||||
public string? Apples { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("fruits")]
|
|
||||||
public string? Oranges { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class DuplicateOptionShortNamesCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption('x')]
|
|
||||||
public string? OptionA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption('x')]
|
|
||||||
public string? OptionB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class ConflictWithHelpOptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("option-h", 'h')]
|
|
||||||
public string? OptionH { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class ConflictWithVersionOptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("version")]
|
|
||||||
public string? Version { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class DuplicateOptionEnvironmentVariableNamesCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("option-a", EnvironmentVariableName = "ENV_VAR")]
|
|
||||||
public string? OptionA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-b", EnvironmentVariableName = "ENV_VAR")]
|
|
||||||
public string? OptionB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("hidden", Description = "Description")]
|
|
||||||
private class HiddenPropertiesCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(13, Name = "param", Description = "Param description")]
|
|
||||||
public string? Parameter { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option", 'o', Description = "Option description", EnvironmentVariableName = "ENV")]
|
|
||||||
public string? Option { get; set; }
|
|
||||||
|
|
||||||
public string? HiddenA { get; set; }
|
|
||||||
|
|
||||||
public bool? HiddenB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using CliFx.Domain;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Exceptions;
|
using CliFx.Tests.Commands;
|
||||||
|
using CliFx.Tests.Commands.Invalid;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class ApplicationSpecs
|
public class ApplicationSpecs
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
@@ -31,7 +32,7 @@ namespace CliFx.Tests
|
|||||||
{
|
{
|
||||||
// Act
|
// Act
|
||||||
var app = new CliApplicationBuilder()
|
var app = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(DefaultCommand))
|
.AddCommand<DefaultCommand>()
|
||||||
.AddCommandsFrom(typeof(DefaultCommand).Assembly)
|
.AddCommandsFrom(typeof(DefaultCommand).Assembly)
|
||||||
.AddCommands(new[] {typeof(DefaultCommand)})
|
.AddCommands(new[] {typeof(DefaultCommand)})
|
||||||
.AddCommandsFrom(new[] {typeof(DefaultCommand).Assembly})
|
.AddCommandsFrom(new[] {typeof(DefaultCommand).Assembly})
|
||||||
@@ -51,219 +52,356 @@ namespace CliFx.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void At_least_one_command_must_be_defined_in_an_application()
|
public async Task At_least_one_command_must_be_defined_in_an_application()
|
||||||
{
|
{
|
||||||
// Arrange
|
await using var stdErr = new MemoryStream();
|
||||||
var commandTypes = Array.Empty<Type>();
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
// Act & assert
|
var application = new CliApplicationBuilder()
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
.UseConsole(console)
|
||||||
_output.WriteLine(ex.Message);
|
.Build();
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Commands_must_implement_the_corresponding_interface()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(NonImplementedCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Commands_must_be_annotated_by_an_attribute()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(NonAnnotatedCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Commands_must_have_unique_names()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DuplicateNameCommandA), typeof(DuplicateNameCommandB)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_can_be_default_but_only_if_it_is_the_only_such_command()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DefaultCommand), typeof(AnotherDefaultCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_parameters_must_have_unique_order()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DuplicateParameterOrderCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_parameters_must_have_unique_names()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DuplicateParameterNameCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_parameter_can_be_non_scalar_only_if_no_other_such_parameter_is_present()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(MultipleNonScalarParametersCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_parameter_can_be_non_scalar_only_if_it_is_the_last_in_order()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(NonLastNonScalarParameterCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_have_names_that_are_not_empty()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(EmptyOptionNameCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_have_names_that_are_longer_than_one_character()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(SingleCharacterOptionNameCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_have_unique_names()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DuplicateOptionNamesCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_have_unique_short_names()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DuplicateOptionShortNamesCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_not_have_conflicts_with_the_implicit_help_option()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(ConflictWithHelpOptionCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_not_have_conflicts_with_the_implicit_version_option()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(ConflictWithVersionOptionCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_must_have_unique_environment_variable_names()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(DuplicateOptionEnvironmentVariableNamesCommand)};
|
|
||||||
|
|
||||||
// Act & assert
|
|
||||||
var ex = Assert.Throws<CliFxException>(() => RootSchema.Resolve(commandTypes));
|
|
||||||
_output.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public void Command_options_and_parameters_must_be_annotated_by_corresponding_attributes()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandTypes = new[] {typeof(HiddenPropertiesCommand)};
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var schema = RootSchema.Resolve(commandTypes);
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
schema.Should().BeEquivalentTo(new RootSchema(new[]
|
exitCode.Should().NotBe(0);
|
||||||
{
|
stdErrData.Should().NotBeEmpty();
|
||||||
new CommandSchema(
|
|
||||||
typeof(HiddenPropertiesCommand),
|
|
||||||
"hidden",
|
|
||||||
"Description",
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
new CommandParameterSchema(
|
|
||||||
typeof(HiddenPropertiesCommand).GetProperty(nameof(HiddenPropertiesCommand.Parameter))!,
|
|
||||||
13,
|
|
||||||
"param",
|
|
||||||
"Param description")
|
|
||||||
},
|
|
||||||
new[]
|
|
||||||
{
|
|
||||||
new CommandOptionSchema(
|
|
||||||
typeof(HiddenPropertiesCommand).GetProperty(nameof(HiddenPropertiesCommand.Option))!,
|
|
||||||
"option",
|
|
||||||
'o',
|
|
||||||
"ENV",
|
|
||||||
false,
|
|
||||||
"Option description"),
|
|
||||||
CommandOptionSchema.HelpOption
|
|
||||||
})
|
|
||||||
}));
|
|
||||||
|
|
||||||
schema.ToString().Should().NotBeNullOrWhiteSpace(); // this is only for coverage, I'm sorry
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Commands_must_implement_the_corresponding_interface()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand(typeof(NonImplementedCommand))
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Commands_must_be_annotated_by_an_attribute()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<NonAnnotatedCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Commands_must_have_unique_names()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<GenericExceptionCommand>()
|
||||||
|
.AddCommand<CommandExceptionCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_can_be_default_but_only_if_it_is_the_only_such_command()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DefaultCommand>()
|
||||||
|
.AddCommand<OtherDefaultCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_parameters_must_have_unique_order()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DuplicateParameterOrderCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_parameters_must_have_unique_names()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DuplicateParameterNameCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_parameter_can_be_non_scalar_only_if_no_other_such_parameter_is_present()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<MultipleNonScalarParametersCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_parameter_can_be_non_scalar_only_if_it_is_the_last_in_order()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<NonLastNonScalarParameterCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_have_names_that_are_not_empty()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<EmptyOptionNameCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_have_names_that_are_longer_than_one_character()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<SingleCharacterOptionNameCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_have_unique_names()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DuplicateOptionNamesCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_have_unique_short_names()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DuplicateOptionShortNamesCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_have_unique_environment_variable_names()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DuplicateOptionEnvironmentVariableNamesCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_not_have_conflicts_with_the_implicit_help_option()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<ConflictWithHelpOptionCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Command_options_must_not_have_conflicts_with_the_implicit_version_option()
|
||||||
|
{
|
||||||
|
await using var stdErr = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(error: stdErr);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<ConflictWithVersionOptionCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().NotBe(0);
|
||||||
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class ArgumentBindingSpecs
|
|
||||||
{
|
|
||||||
[Command]
|
|
||||||
private class AllSupportedTypesCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption(nameof(Object))]
|
|
||||||
public object? Object { get; set; } = 42;
|
|
||||||
|
|
||||||
[CommandOption(nameof(String))]
|
|
||||||
public string? String { get; set; } = "foo bar";
|
|
||||||
|
|
||||||
[CommandOption(nameof(Bool))]
|
|
||||||
public bool Bool { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Char))]
|
|
||||||
public char Char { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Sbyte))]
|
|
||||||
public sbyte Sbyte { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Byte))]
|
|
||||||
public byte Byte { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Short))]
|
|
||||||
public short Short { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Ushort))]
|
|
||||||
public ushort Ushort { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Int))]
|
|
||||||
public int Int { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Uint))]
|
|
||||||
public uint Uint { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Long))]
|
|
||||||
public long Long { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Ulong))]
|
|
||||||
public ulong Ulong { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Float))]
|
|
||||||
public float Float { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Double))]
|
|
||||||
public double Double { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Decimal))]
|
|
||||||
public decimal Decimal { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(DateTime))]
|
|
||||||
public DateTime DateTime { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(DateTimeOffset))]
|
|
||||||
public DateTimeOffset DateTimeOffset { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(TimeSpan))]
|
|
||||||
public TimeSpan TimeSpan { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(CustomEnum))]
|
|
||||||
public CustomEnum CustomEnum { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(IntNullable))]
|
|
||||||
public int? IntNullable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(CustomEnumNullable))]
|
|
||||||
public CustomEnum? CustomEnumNullable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(TimeSpanNullable))]
|
|
||||||
public TimeSpan? TimeSpanNullable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(TestStringConstructable))]
|
|
||||||
public StringConstructable? TestStringConstructable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(TestStringParseable))]
|
|
||||||
public StringParseable? TestStringParseable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(TestStringParseableWithFormatProvider))]
|
|
||||||
public StringParseableWithFormatProvider? TestStringParseableWithFormatProvider { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(ObjectArray))]
|
|
||||||
public object[]? ObjectArray { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(StringArray))]
|
|
||||||
public string[]? StringArray { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(IntArray))]
|
|
||||||
public int[]? IntArray { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(CustomEnumArray))]
|
|
||||||
public CustomEnum[]? CustomEnumArray { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(IntNullableArray))]
|
|
||||||
public int?[]? IntNullableArray { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(TestStringConstructableArray))]
|
|
||||||
public StringConstructable[]? TestStringConstructableArray { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(Enumerable))]
|
|
||||||
public IEnumerable? Enumerable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(StringEnumerable))]
|
|
||||||
public IEnumerable<string>? StringEnumerable { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(StringReadOnlyList))]
|
|
||||||
public IReadOnlyList<string>? StringReadOnlyList { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(StringList))]
|
|
||||||
public List<string>? StringList { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(StringHashSet))]
|
|
||||||
public HashSet<string>? StringHashSet { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class ArrayOptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("option", 'o')]
|
|
||||||
public IReadOnlyList<string>? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class RequiredOptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption(nameof(Option), IsRequired = true)]
|
|
||||||
public string? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class RequiredArrayOptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption(nameof(Option), IsRequired = true)]
|
|
||||||
public IReadOnlyList<string>? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class ParametersCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0)]
|
|
||||||
public string? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(1)]
|
|
||||||
public string? ParameterB { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(2)]
|
|
||||||
public IReadOnlyList<string>? ParameterC { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class UnsupportedPropertyTypeCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption(nameof(Option))]
|
|
||||||
public DummyType? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class UnsupportedEnumerablePropertyTypeCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption(nameof(Option))]
|
|
||||||
public CustomEnumerable<string>? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class NoParameterCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption(nameof(OptionA))]
|
|
||||||
public string? OptionA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption(nameof(OptionB))]
|
|
||||||
public string? OptionB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class ArgumentBindingSpecs
|
|
||||||
{
|
|
||||||
private enum CustomEnum
|
|
||||||
{
|
|
||||||
Value1 = 1,
|
|
||||||
Value2 = 2,
|
|
||||||
Value3 = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StringConstructable
|
|
||||||
{
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
public StringConstructable(string value) => Value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StringParseable
|
|
||||||
{
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
private StringParseable(string value) => Value = value;
|
|
||||||
|
|
||||||
public static StringParseable Parse(string value) => new StringParseable(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class StringParseableWithFormatProvider
|
|
||||||
{
|
|
||||||
public string Value { get; }
|
|
||||||
|
|
||||||
private StringParseableWithFormatProvider(string value) => Value = value;
|
|
||||||
|
|
||||||
public static StringParseableWithFormatProvider Parse(string value, IFormatProvider formatProvider) =>
|
|
||||||
new StringParseableWithFormatProvider(value + " " + formatProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DummyType
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CustomEnumerable<T> : IEnumerable<T>
|
|
||||||
{
|
|
||||||
public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>) Array.Empty<T>()).GetEnumerator();
|
|
||||||
|
|
||||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
1453
CliFx.Tests/ArgumentConversionSpecs.cs
Normal file
1453
CliFx.Tests/ArgumentConversionSpecs.cs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,378 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using CliFx.Domain;
|
|
||||||
using CliFx.Tests.Internal;
|
|
||||||
using FluentAssertions;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public class ArgumentSyntaxSpecs
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Input_is_empty_if_no_arguments_are_provided()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var arguments = Array.Empty<string>();
|
|
||||||
var commandNames = Array.Empty<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var input = CommandInput.Parse(arguments, commandNames);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
input.Should().BeEquivalentTo(CommandInput.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[][] DirectivesTestData => new[]
|
|
||||||
{
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"[preview]"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddDirective("preview")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"[preview]", "[debug]"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddDirective("preview")
|
|
||||||
.AddDirective("debug")
|
|
||||||
.Build()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(DirectivesTestData))]
|
|
||||||
internal void Directive_can_be_enabled_by_specifying_its_name_in_square_brackets(IReadOnlyList<string> arguments, CommandInput expectedInput)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandNames = Array.Empty<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var input = CommandInput.Parse(arguments, commandNames);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
input.Should().BeEquivalentTo(expectedInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[][] OptionsTestData => new[]
|
|
||||||
{
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option", "value"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option", "value")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option", "value1", "value2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option", "value1", "value2")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option", "same value"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option", "same value")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option1", "--option2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option1")
|
|
||||||
.AddOption("option2")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option1", "value1", "--option2", "value2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option1", "value1")
|
|
||||||
.AddOption("option2", "value2")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option1", "value1", "value2", "--option2", "value3", "value4"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option1", "value1", "value2")
|
|
||||||
.AddOption("option2", "value3", "value4")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"--option1", "value1", "value2", "--option2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("option1", "value1", "value2")
|
|
||||||
.AddOption("option2")
|
|
||||||
.Build()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(OptionsTestData))]
|
|
||||||
internal void Option_can_be_set_by_specifying_its_name_after_two_dashes(IReadOnlyList<string> arguments, CommandInput expectedInput)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandNames = Array.Empty<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var input = CommandInput.Parse(arguments, commandNames);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
input.Should().BeEquivalentTo(expectedInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[][] ShortOptionsTestData => new[]
|
|
||||||
{
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-o"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("o")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-o", "value"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("o", "value")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-o", "value1", "value2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("o", "value1", "value2")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-o", "same value"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("o", "same value")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-a", "-b"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a")
|
|
||||||
.AddOption("b")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-a", "value1", "-b", "value2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a", "value1")
|
|
||||||
.AddOption("b", "value2")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-a", "value1", "value2", "-b", "value3", "value4"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a", "value1", "value2")
|
|
||||||
.AddOption("b", "value3", "value4")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-a", "value1", "value2", "-b"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a", "value1", "value2")
|
|
||||||
.AddOption("b")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-abc"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a")
|
|
||||||
.AddOption("b")
|
|
||||||
.AddOption("c")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-abc", "value"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a")
|
|
||||||
.AddOption("b")
|
|
||||||
.AddOption("c", "value")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"-abc", "value1", "value2"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddOption("a")
|
|
||||||
.AddOption("b")
|
|
||||||
.AddOption("c", "value1", "value2")
|
|
||||||
.Build()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(ShortOptionsTestData))]
|
|
||||||
internal void Option_can_be_set_by_specifying_its_short_name_after_a_single_dash(IReadOnlyList<string> arguments, CommandInput expectedInput)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandNames = Array.Empty<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var input = CommandInput.Parse(arguments, commandNames);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
input.Should().BeEquivalentTo(expectedInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[][] ParametersTestData => new[]
|
|
||||||
{
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"foo"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddParameter("foo")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"foo", "bar"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddParameter("foo")
|
|
||||||
.AddParameter("bar")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"[preview]", "foo"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddDirective("preview")
|
|
||||||
.AddParameter("foo")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"foo", "--option", "value", "-abc"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddParameter("foo")
|
|
||||||
.AddOption("option", "value")
|
|
||||||
.AddOption("a")
|
|
||||||
.AddOption("b")
|
|
||||||
.AddOption("c")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"[preview]", "[debug]", "foo", "bar", "--option", "value", "-abc"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.AddDirective("preview")
|
|
||||||
.AddDirective("debug")
|
|
||||||
.AddParameter("foo")
|
|
||||||
.AddParameter("bar")
|
|
||||||
.AddOption("option", "value")
|
|
||||||
.AddOption("a")
|
|
||||||
.AddOption("b")
|
|
||||||
.AddOption("c")
|
|
||||||
.Build()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(ParametersTestData))]
|
|
||||||
internal void Parameter_can_be_set_by_specifying_the_value_directly(IReadOnlyList<string> arguments, CommandInput expectedInput)
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
var commandNames = Array.Empty<string>();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var input = CommandInput.Parse(arguments, commandNames);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
input.Should().BeEquivalentTo(expectedInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static object[][] CommandNameTestData => new[]
|
|
||||||
{
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"cmd"},
|
|
||||||
new[] {"cmd"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.SetCommandName("cmd")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"cmd"},
|
|
||||||
new[] {"cmd", "foo", "bar", "-o", "value"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.SetCommandName("cmd")
|
|
||||||
.AddParameter("foo")
|
|
||||||
.AddParameter("bar")
|
|
||||||
.AddOption("o", "value")
|
|
||||||
.Build()
|
|
||||||
},
|
|
||||||
|
|
||||||
new object[]
|
|
||||||
{
|
|
||||||
new[] {"cmd", "cmd sub"},
|
|
||||||
new[] {"cmd", "sub", "foo"},
|
|
||||||
new CommandInputBuilder()
|
|
||||||
.SetCommandName("cmd sub")
|
|
||||||
.AddParameter("foo")
|
|
||||||
.Build()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
[Theory]
|
|
||||||
[MemberData(nameof(CommandNameTestData))]
|
|
||||||
internal void Command_name_is_matched_from_arguments_that_come_before_parameters(
|
|
||||||
IReadOnlyList<string> commandNames,
|
|
||||||
IReadOnlyList<string> arguments,
|
|
||||||
CommandInput expectedInput)
|
|
||||||
{
|
|
||||||
// Act
|
|
||||||
var input = CommandInput.Parse(arguments, commandNames);
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
input.Should().BeEquivalentTo(expectedInput);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class CancellationSpecs
|
|
||||||
{
|
|
||||||
[Command("cancel")]
|
|
||||||
private class CancellableCommand : ICommand
|
|
||||||
{
|
|
||||||
public async ValueTask ExecuteAsync(IConsole console)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await Task.Delay(TimeSpan.FromSeconds(3), console.GetCancellationToken());
|
|
||||||
console.Output.WriteLine("Never printed");
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
console.Output.WriteLine("Cancellation requested");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,14 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Tests.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class CancellationSpecs
|
public class CancellationSpecs
|
||||||
{
|
{
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Command_can_perform_additional_cleanup_if_cancellation_is_requested()
|
public async Task Command_can_perform_additional_cleanup_if_cancellation_is_requested()
|
||||||
@@ -22,22 +22,19 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut, cancellationToken: cts.Token);
|
var console = new VirtualConsole(output: stdOut, cancellationToken: cts.Token);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(CancellableCommand))
|
.AddCommand<CancellableCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
cts.CancelAfter(TimeSpan.FromSeconds(0.2));
|
cts.CancelAfter(TimeSpan.FromSeconds(0.2));
|
||||||
|
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"cmd"});
|
||||||
new[] {"cancel"},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
stdOutData.Should().Be("Cancellation requested");
|
stdOutData.Should().Be(CancellableCommand.CancellationOutputText);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -19,6 +19,7 @@
|
|||||||
<PackageReference Include="FluentAssertions" Version="5.10.2" />
|
<PackageReference Include="FluentAssertions" Version="5.10.2" />
|
||||||
<PackageReference Include="GitHubActionsTestLogger" Version="1.0.0" />
|
<PackageReference Include="GitHubActionsTestLogger" Version="1.0.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1" />
|
||||||
<PackageReference Include="coverlet.msbuild" Version="2.8.0" PrivateAssets="all" />
|
<PackageReference Include="coverlet.msbuild" Version="2.8.0" PrivateAssets="all" />
|
||||||
|
|||||||
31
CliFx.Tests/Commands/CancellableCommand.cs
Normal file
31
CliFx.Tests/Commands/CancellableCommand.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class CancellableCommand : ICommand
|
||||||
|
{
|
||||||
|
public const string CompletionOutputText = "Finished";
|
||||||
|
public const string CancellationOutputText = "Canceled";
|
||||||
|
|
||||||
|
public async ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Delay(
|
||||||
|
TimeSpan.FromSeconds(3),
|
||||||
|
console.GetCancellationToken()
|
||||||
|
);
|
||||||
|
|
||||||
|
console.Output.WriteLine(CompletionOutputText);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
console.Output.WriteLine(CancellationOutputText);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
CliFx.Tests/Commands/CommandExceptionCommand.cs
Normal file
21
CliFx.Tests/Commands/CommandExceptionCommand.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using CliFx.Exceptions;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class CommandExceptionCommand : ICommand
|
||||||
|
{
|
||||||
|
[CommandOption("code", 'c')]
|
||||||
|
public int ExitCode { get; set; } = 133;
|
||||||
|
|
||||||
|
[CommandOption("msg", 'm')]
|
||||||
|
public string? Message { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("show-help")]
|
||||||
|
public bool ShowHelp { get; set; }
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => throw new CommandException(Message, ExitCode, ShowHelp);
|
||||||
|
}
|
||||||
|
}
|
||||||
17
CliFx.Tests/Commands/DefaultCommand.cs
Normal file
17
CliFx.Tests/Commands/DefaultCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command(Description = nameof(DefaultCommand))]
|
||||||
|
public class DefaultCommand : ICommand
|
||||||
|
{
|
||||||
|
public const string ExpectedOutputText = nameof(DefaultCommand);
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
console.Output.WriteLine(ExpectedOutputText);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
CliFx.Tests/Commands/GenericExceptionCommand.cs
Normal file
15
CliFx.Tests/Commands/GenericExceptionCommand.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class GenericExceptionCommand : ICommand
|
||||||
|
{
|
||||||
|
[CommandOption("msg", 'm')]
|
||||||
|
public string? Message { get; set; }
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => throw new Exception(Message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class ConflictWithHelpOptionCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("option-h", 'h')]
|
||||||
|
public string? OptionH { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
// Must be default because version option is available only on default commands
|
||||||
|
[Command]
|
||||||
|
public class ConflictWithVersionOptionCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("version")]
|
||||||
|
public string? Version { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class DuplicateOptionEnvironmentVariableNamesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("option-a", EnvironmentVariableName = "ENV_VAR")]
|
||||||
|
public string? OptionA { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("option-b", EnvironmentVariableName = "ENV_VAR")]
|
||||||
|
public string? OptionB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
CliFx.Tests/Commands/Invalid/DuplicateOptionNamesCommand.cs
Normal file
14
CliFx.Tests/Commands/Invalid/DuplicateOptionNamesCommand.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class DuplicateOptionNamesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("fruits")]
|
||||||
|
public string? Apples { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("fruits")]
|
||||||
|
public string? Oranges { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class DuplicateOptionShortNamesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption('x')]
|
||||||
|
public string? OptionA { get; set; }
|
||||||
|
|
||||||
|
[CommandOption('x')]
|
||||||
|
public string? OptionB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class DuplicateParameterNameCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandParameter(0, Name = "param")]
|
||||||
|
public string? ParamA { get; set; }
|
||||||
|
|
||||||
|
[CommandParameter(1, Name = "param")]
|
||||||
|
public string? ParamB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class DuplicateParameterOrderCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandParameter(13)]
|
||||||
|
public string? ParamA { get; set; }
|
||||||
|
|
||||||
|
[CommandParameter(13)]
|
||||||
|
public string? ParamB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
CliFx.Tests/Commands/Invalid/EmptyOptionNameCommand.cs
Normal file
11
CliFx.Tests/Commands/Invalid/EmptyOptionNameCommand.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class EmptyOptionNameCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("")]
|
||||||
|
public string? Apples { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class MultipleNonScalarParametersCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandParameter(0)]
|
||||||
|
public IReadOnlyList<string>? ParamA { get; set; }
|
||||||
|
|
||||||
|
[CommandParameter(1)]
|
||||||
|
public IReadOnlyList<string>? ParamB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
6
CliFx.Tests/Commands/Invalid/NonAnnotatedCommand.cs
Normal file
6
CliFx.Tests/Commands/Invalid/NonAnnotatedCommand.cs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
public class NonAnnotatedCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
9
CliFx.Tests/Commands/Invalid/NonImplementedCommand.cs
Normal file
9
CliFx.Tests/Commands/Invalid/NonImplementedCommand.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command]
|
||||||
|
public class NonImplementedCommand
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class NonLastNonScalarParameterCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandParameter(0)]
|
||||||
|
public IReadOnlyList<string>? ParamA { get; set; }
|
||||||
|
|
||||||
|
[CommandParameter(1)]
|
||||||
|
public string? ParamB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
9
CliFx.Tests/Commands/Invalid/OtherDefaultCommand.cs
Normal file
9
CliFx.Tests/Commands/Invalid/OtherDefaultCommand.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command]
|
||||||
|
public class OtherDefaultCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands.Invalid
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class SingleCharacterOptionNameCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("a")]
|
||||||
|
public string? Apples { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
17
CliFx.Tests/Commands/NamedCommand.cs
Normal file
17
CliFx.Tests/Commands/NamedCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("named", Description = nameof(NamedCommand))]
|
||||||
|
public class NamedCommand : ICommand
|
||||||
|
{
|
||||||
|
public const string ExpectedOutputText = nameof(NamedCommand);
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
console.Output.WriteLine(ExpectedOutputText);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
CliFx.Tests/Commands/NamedSubCommand.cs
Normal file
17
CliFx.Tests/Commands/NamedSubCommand.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("named sub", Description = nameof(NamedSubCommand))]
|
||||||
|
public class NamedSubCommand : ICommand
|
||||||
|
{
|
||||||
|
public const string ExpectedOutputText = nameof(NamedSubCommand);
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
console.Output.WriteLine(ExpectedOutputText);
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
CliFx.Tests/Commands/SelfSerializeCommandBase.cs
Normal file
14
CliFx.Tests/Commands/SelfSerializeCommandBase.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
public abstract class SelfSerializeCommandBase : ICommand
|
||||||
|
{
|
||||||
|
public ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
console.Output.WriteLine(JsonConvert.SerializeObject(this));
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
155
CliFx.Tests/Commands/SupportedArgumentTypesCommand.cs
Normal file
155
CliFx.Tests/Commands/SupportedArgumentTypesCommand.cs
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public partial class SupportedArgumentTypesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("obj")]
|
||||||
|
public object? Object { get; set; } = 42;
|
||||||
|
|
||||||
|
[CommandOption("str")]
|
||||||
|
public string? String { get; set; } = "foo bar";
|
||||||
|
|
||||||
|
[CommandOption("bool")]
|
||||||
|
public bool Bool { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("char")]
|
||||||
|
public char Char { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("sbyte")]
|
||||||
|
public sbyte Sbyte { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("byte")]
|
||||||
|
public byte Byte { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("short")]
|
||||||
|
public short Short { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("ushort")]
|
||||||
|
public ushort Ushort { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("int")]
|
||||||
|
public int Int { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("uint")]
|
||||||
|
public uint Uint { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("long")]
|
||||||
|
public long Long { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("ulong")]
|
||||||
|
public ulong Ulong { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("float")]
|
||||||
|
public float Float { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("double")]
|
||||||
|
public double Double { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("decimal")]
|
||||||
|
public decimal Decimal { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("datetime")]
|
||||||
|
public DateTime DateTime { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("datetime-offset")]
|
||||||
|
public DateTimeOffset DateTimeOffset { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("timespan")]
|
||||||
|
public TimeSpan TimeSpan { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("enum")]
|
||||||
|
public CustomEnum Enum { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("int-nullable")]
|
||||||
|
public int? IntNullable { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("enum-nullable")]
|
||||||
|
public CustomEnum? EnumNullable { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("timespan-nullable")]
|
||||||
|
public TimeSpan? TimeSpanNullable { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-constructible")]
|
||||||
|
public CustomStringConstructible? StringConstructible { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-parseable")]
|
||||||
|
public CustomStringParseable? StringParseable { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-parseable-format")]
|
||||||
|
public CustomStringParseableWithFormatProvider? StringParseableWithFormatProvider { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("obj-array")]
|
||||||
|
public object[]? ObjectArray { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-array")]
|
||||||
|
public string[]? StringArray { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("int-array")]
|
||||||
|
public int[]? IntArray { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("enum-array")]
|
||||||
|
public CustomEnum[]? EnumArray { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("int-nullable-array")]
|
||||||
|
public int?[]? IntNullableArray { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-constructible-array")]
|
||||||
|
public CustomStringConstructible[]? StringConstructibleArray { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-enumerable")]
|
||||||
|
public IEnumerable<string>? StringEnumerable { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-read-only-list")]
|
||||||
|
public IReadOnlyList<string>? StringReadOnlyList { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-list")]
|
||||||
|
public List<string>? StringList { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-set")]
|
||||||
|
public HashSet<string>? StringHashSet { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class SupportedArgumentTypesCommand
|
||||||
|
{
|
||||||
|
public enum CustomEnum
|
||||||
|
{
|
||||||
|
Value1 = 1,
|
||||||
|
Value2 = 2,
|
||||||
|
Value3 = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomStringConstructible
|
||||||
|
{
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
public CustomStringConstructible(string value) => Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomStringParseable
|
||||||
|
{
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
private CustomStringParseable(string value) => Value = value;
|
||||||
|
|
||||||
|
public static CustomStringParseable Parse(string value) => new CustomStringParseable(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomStringParseableWithFormatProvider
|
||||||
|
{
|
||||||
|
public string Value { get; }
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
private CustomStringParseableWithFormatProvider(string value) => Value = value;
|
||||||
|
|
||||||
|
public static CustomStringParseableWithFormatProvider Parse(string value, IFormatProvider formatProvider) =>
|
||||||
|
new CustomStringParseableWithFormatProvider(value + " " + formatProvider);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
CliFx.Tests/Commands/UnsupportedArgumentTypesCommand.cs
Normal file
31
CliFx.Tests/Commands/UnsupportedArgumentTypesCommand.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public partial class UnsupportedArgumentTypesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("str-non-initializable")]
|
||||||
|
public CustomType? StringNonInitializable { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("str-enumerable-non-initializable")]
|
||||||
|
public CustomEnumerable<string>? StringEnumerableNonInitializable { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class UnsupportedArgumentTypesCommand
|
||||||
|
{
|
||||||
|
public class CustomType
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CustomEnumerable<T> : IEnumerable<T>
|
||||||
|
{
|
||||||
|
public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>) Array.Empty<T>()).GetEnumerator();
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
CliFx.Tests/Commands/WithDefaultValuesCommand.cs
Normal file
44
CliFx.Tests/Commands/WithDefaultValuesCommand.cs
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithDefaultValuesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
public enum CustomEnum { Value1, Value2, Value3 };
|
||||||
|
|
||||||
|
[CommandOption("obj")]
|
||||||
|
public object? Object { get; set; } = 42;
|
||||||
|
|
||||||
|
[CommandOption("str")]
|
||||||
|
public string? String { get; set; } = "foo";
|
||||||
|
|
||||||
|
[CommandOption("str-empty")]
|
||||||
|
public string StringEmpty { get; set; } = "";
|
||||||
|
|
||||||
|
[CommandOption("str-array")]
|
||||||
|
public string[]? StringArray { get; set; } = { "foo", "bar", "baz" };
|
||||||
|
|
||||||
|
[CommandOption("bool")]
|
||||||
|
public bool Bool { get; set; } = true;
|
||||||
|
|
||||||
|
[CommandOption("char")]
|
||||||
|
public char Char { get; set; } = 't';
|
||||||
|
|
||||||
|
[CommandOption("int")]
|
||||||
|
public int Int { get; set; } = 1337;
|
||||||
|
|
||||||
|
[CommandOption("int-nullable")]
|
||||||
|
public int? IntNullable { get; set; } = 1337;
|
||||||
|
|
||||||
|
[CommandOption("int-array")]
|
||||||
|
public int[]? IntArray { get; set; } = { 1, 2, 3 };
|
||||||
|
|
||||||
|
[CommandOption("timespan")]
|
||||||
|
public TimeSpan TimeSpan { get; set; } = TimeSpan.FromMinutes(123);
|
||||||
|
|
||||||
|
[CommandOption("enum")]
|
||||||
|
public CustomEnum Enum { get; set; } = CustomEnum.Value2;
|
||||||
|
}
|
||||||
|
}
|
||||||
28
CliFx.Tests/Commands/WithDependenciesCommand.cs
Normal file
28
CliFx.Tests/Commands/WithDependenciesCommand.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithDependenciesCommand : ICommand
|
||||||
|
{
|
||||||
|
public class DependencyA
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class DependencyB
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly DependencyA _dependencyA;
|
||||||
|
private readonly DependencyB _dependencyB;
|
||||||
|
|
||||||
|
public WithDependenciesCommand(DependencyA dependencyA, DependencyB dependencyB)
|
||||||
|
{
|
||||||
|
_dependencyA = dependencyA;
|
||||||
|
_dependencyB = dependencyB;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
CliFx.Tests/Commands/WithEnumArgumentsCommand.cs
Normal file
19
CliFx.Tests/Commands/WithEnumArgumentsCommand.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithEnumArgumentsCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
public enum CustomEnum { Value1, Value2, Value3 };
|
||||||
|
|
||||||
|
[CommandParameter(0, Name = "enum")]
|
||||||
|
public CustomEnum EnumParameter { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("enum")]
|
||||||
|
public CustomEnum? EnumOption { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("required-enum", IsRequired = true)]
|
||||||
|
public CustomEnum RequiredEnumOption { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
15
CliFx.Tests/Commands/WithEnvironmentVariablesCommand.cs
Normal file
15
CliFx.Tests/Commands/WithEnvironmentVariablesCommand.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithEnvironmentVariablesCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("opt-a", 'a', EnvironmentVariableName = "ENV_OPT_A")]
|
||||||
|
public string? OptA { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("opt-b", 'b', EnvironmentVariableName = "ENV_OPT_B")]
|
||||||
|
public IReadOnlyList<string>? OptB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
CliFx.Tests/Commands/WithParametersCommand.cs
Normal file
18
CliFx.Tests/Commands/WithParametersCommand.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithParametersCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandParameter(0)]
|
||||||
|
public string? ParamA { get; set; }
|
||||||
|
|
||||||
|
[CommandParameter(1)]
|
||||||
|
public int? ParamB { get; set; }
|
||||||
|
|
||||||
|
[CommandParameter(2)]
|
||||||
|
public IReadOnlyList<string>? ParamC { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
18
CliFx.Tests/Commands/WithRequiredOptionsCommand.cs
Normal file
18
CliFx.Tests/Commands/WithRequiredOptionsCommand.cs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithRequiredOptionsCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("opt-a", 'a', IsRequired = true)]
|
||||||
|
public string? OptA { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("opt-b", 'b')]
|
||||||
|
public int? OptB { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("opt-c", 'c', IsRequired = true)]
|
||||||
|
public IReadOnlyList<char>? OptC { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
11
CliFx.Tests/Commands/WithSingleParameterCommand.cs
Normal file
11
CliFx.Tests/Commands/WithSingleParameterCommand.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithSingleParameterCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandParameter(0)]
|
||||||
|
public string? ParamA { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
14
CliFx.Tests/Commands/WithSingleRequiredOptionCommand.cs
Normal file
14
CliFx.Tests/Commands/WithSingleRequiredOptionCommand.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithSingleRequiredOptionCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("opt-a")]
|
||||||
|
public string? OptA { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("opt-b", IsRequired = true)]
|
||||||
|
public string? OptB { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
CliFx.Tests/Commands/WithStringArrayOptionCommand.cs
Normal file
12
CliFx.Tests/Commands/WithStringArrayOptionCommand.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using CliFx.Attributes;
|
||||||
|
|
||||||
|
namespace CliFx.Tests.Commands
|
||||||
|
{
|
||||||
|
[Command("cmd")]
|
||||||
|
public class WithStringArrayOptionCommand : SelfSerializeCommandBase
|
||||||
|
{
|
||||||
|
[CommandOption("opt", 'o')]
|
||||||
|
public IReadOnlyList<string>? Opt { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -38,7 +38,8 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(
|
var console = new VirtualConsole(
|
||||||
input: stdIn,
|
input: stdIn,
|
||||||
output: stdOut,
|
output: stdOut,
|
||||||
error: stdErr);
|
error: stdErr
|
||||||
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
console.Output.Write("output");
|
console.Output.Write("output");
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class DependencyInjectionSpecs
|
|
||||||
{
|
|
||||||
[Command]
|
|
||||||
private class WithoutDependenciesCommand : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DependencyA
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DependencyB
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class WithDependenciesCommand : ICommand
|
|
||||||
{
|
|
||||||
private readonly DependencyA _dependencyA;
|
|
||||||
private readonly DependencyB _dependencyB;
|
|
||||||
|
|
||||||
public WithDependenciesCommand(DependencyA dependencyA, DependencyB dependencyB)
|
|
||||||
{
|
|
||||||
_dependencyA = dependencyA;
|
|
||||||
_dependencyB = dependencyB;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
using CliFx.Exceptions;
|
using CliFx.Exceptions;
|
||||||
|
using CliFx.Tests.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class DependencyInjectionSpecs
|
public class DependencyInjectionSpecs
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
@@ -18,10 +19,10 @@ namespace CliFx.Tests
|
|||||||
var activator = new DefaultTypeActivator();
|
var activator = new DefaultTypeActivator();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var obj = activator.CreateInstance(typeof(WithoutDependenciesCommand));
|
var obj = activator.CreateInstance(typeof(DefaultCommand));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
obj.Should().BeOfType<WithoutDependenciesCommand>();
|
obj.Should().BeOfType<DefaultCommand>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -40,7 +41,10 @@ namespace CliFx.Tests
|
|||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var activator = new DelegateTypeActivator(_ =>
|
var activator = new DelegateTypeActivator(_ =>
|
||||||
new WithDependenciesCommand(new DependencyA(), new DependencyB()));
|
new WithDependenciesCommand(
|
||||||
|
new WithDependenciesCommand.DependencyA(),
|
||||||
|
new WithDependenciesCommand.DependencyB())
|
||||||
|
);
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var obj = activator.CreateInstance(typeof(WithDependenciesCommand));
|
var obj = activator.CreateInstance(typeof(WithDependenciesCommand));
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class DirectivesSpecs
|
|
||||||
{
|
|
||||||
[Command("cmd")]
|
|
||||||
private class NamedCommand : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Tests.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class DirectivesSpecs
|
public class DirectivesSpecs
|
||||||
{
|
{
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
public DirectivesSpecs(ITestOutputHelper output) => _output = output;
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Preview_directive_can_be_specified_to_print_provided_arguments_as_they_were_parsed()
|
public async Task Preview_directive_can_be_specified_to_print_provided_arguments_as_they_were_parsed()
|
||||||
{
|
{
|
||||||
@@ -16,21 +22,23 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(NamedCommand))
|
.AddCommand<NamedCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.AllowPreviewMode()
|
.AllowPreviewMode()
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(
|
||||||
new[] {"[preview]", "cmd", "param", "-abc", "--option", "foo"},
|
new[] {"[preview]", "named", "param", "-abc", "--option", "foo"},
|
||||||
new Dictionary<string, string>());
|
new Dictionary<string, string>());
|
||||||
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().Be(0);
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().ContainAll("cmd", "<param>", "[-a]", "[-b]", "[-c]", "[--option \"foo\"]");
|
stdOutData.Should().ContainAll("named", "<param>", "[-a]", "[-b]", "[-c]", "[--option \"foo\"]");
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class EnvironmentVariablesSpecs
|
|
||||||
{
|
|
||||||
[Command]
|
|
||||||
private class EnvironmentVariableCollectionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("opt", EnvironmentVariableName = "ENV_OPT")]
|
|
||||||
public IReadOnlyList<string>? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command]
|
|
||||||
private class EnvironmentVariableCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("opt", EnvironmentVariableName = "ENV_OPT")]
|
|
||||||
public string? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +1,20 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using CliFx.Domain;
|
using CliFx.Tests.Commands;
|
||||||
using CliFx.Tests.Internal;
|
|
||||||
using CliWrap;
|
using CliWrap;
|
||||||
using CliWrap.Buffered;
|
using CliWrap.Buffered;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
|
using Newtonsoft.Json;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class EnvironmentVariablesSpecs
|
public class EnvironmentVariablesSpecs
|
||||||
{
|
{
|
||||||
// This test uses a real application to make sure environment variables are actually read correctly
|
// This test uses a real application to make sure environment variables are actually read correctly
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Option_can_use_a_specific_environment_variable_as_fallback()
|
public async Task Option_can_use_an_environment_variable_as_fallback()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var command = Cli.Wrap("dotnet")
|
var command = Cli.Wrap("dotnet")
|
||||||
@@ -32,7 +32,7 @@ namespace CliFx.Tests
|
|||||||
|
|
||||||
// This test uses a real application to make sure environment variables are actually read correctly
|
// This test uses a real application to make sure environment variables are actually read correctly
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Option_only_uses_environment_variable_as_fallback_if_the_value_was_not_directly_provided()
|
public async Task Option_only_uses_an_environment_variable_as_fallback_if_the_value_is_not_directly_provided()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var command = Cli.Wrap("dotnet")
|
var command = Cli.Wrap("dotnet")
|
||||||
@@ -51,65 +51,97 @@ namespace CliFx.Tests
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Option_of_non_scalar_type_can_take_multiple_separated_values_from_an_environment_variable()
|
public async Task Option_only_uses_an_environment_variable_as_fallback_if_the_name_matches_case_sensitively()
|
||||||
{
|
{
|
||||||
// Arrange
|
await using var stdOut = new MemoryStream();
|
||||||
var input = CommandInput.Empty;
|
var console = new VirtualConsole(output: stdOut);
|
||||||
var envVars = new Dictionary<string, string>
|
|
||||||
{
|
var application = new CliApplicationBuilder()
|
||||||
["ENV_OPT"] = $"foo{Path.PathSeparator}bar"
|
.AddCommand<WithEnvironmentVariablesCommand>()
|
||||||
};
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var instance = CommandHelper.ResolveCommand<EnvironmentVariableCollectionCommand>(input, envVars);
|
var exitCode = await application.RunAsync(
|
||||||
|
new[] {"cmd"},
|
||||||
|
new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["ENV_opt_A"] = "incorrect",
|
||||||
|
["ENV_OPT_A"] = "correct"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
var commandInstance = JsonConvert.DeserializeObject<WithEnvironmentVariablesCommand>(stdOutData);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
instance.Should().BeEquivalentTo(new EnvironmentVariableCollectionCommand
|
exitCode.Should().Be(0);
|
||||||
|
commandInstance.Should().BeEquivalentTo(new WithEnvironmentVariablesCommand
|
||||||
{
|
{
|
||||||
Option = new[] {"foo", "bar"}
|
OptA = "correct"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Option_of_scalar_type_can_only_take_a_single_value_from_an_environment_variable_even_if_it_contains_separators()
|
public async Task Option_of_non_scalar_type_can_use_an_environment_variable_as_fallback_and_extract_multiple_values()
|
||||||
{
|
{
|
||||||
// Arrange
|
await using var stdOut = new MemoryStream();
|
||||||
var input = CommandInput.Empty;
|
var console = new VirtualConsole(output: stdOut);
|
||||||
var envVars = new Dictionary<string, string>
|
|
||||||
{
|
var application = new CliApplicationBuilder()
|
||||||
["ENV_OPT"] = $"foo{Path.PathSeparator}bar"
|
.AddCommand<WithEnvironmentVariablesCommand>()
|
||||||
};
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var instance = CommandHelper.ResolveCommand<EnvironmentVariableCommand>(input, envVars);
|
var exitCode = await application.RunAsync(
|
||||||
|
new[] {"cmd"},
|
||||||
|
new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["ENV_OPT_B"] = $"foo{Path.PathSeparator}bar"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
var commandInstance = JsonConvert.DeserializeObject<WithEnvironmentVariablesCommand>(stdOutData);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
instance.Should().BeEquivalentTo(new EnvironmentVariableCommand
|
exitCode.Should().Be(0);
|
||||||
|
commandInstance.Should().BeEquivalentTo(new WithEnvironmentVariablesCommand
|
||||||
{
|
{
|
||||||
Option = $"foo{Path.PathSeparator}bar"
|
OptB = new[] {"foo", "bar"}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Option_can_use_a_specific_environment_variable_as_fallback_while_respecting_case()
|
public async Task Option_of_scalar_type_can_use_an_environment_variable_as_fallback_regardless_of_separators()
|
||||||
{
|
{
|
||||||
// Arrange
|
await using var stdOut = new MemoryStream();
|
||||||
const string expected = "foobar";
|
var console = new VirtualConsole(output: stdOut);
|
||||||
var input = CommandInput.Empty;
|
|
||||||
var envVars = new Dictionary<string, string>
|
var application = new CliApplicationBuilder()
|
||||||
{
|
.AddCommand<WithEnvironmentVariablesCommand>()
|
||||||
["ENV_OPT"] = expected,
|
.UseConsole(console)
|
||||||
["env_opt"] = "2"
|
.Build();
|
||||||
};
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var instance = CommandHelper.ResolveCommand<EnvironmentVariableCommand>(input, envVars);
|
var exitCode = await application.RunAsync(
|
||||||
|
new[] {"cmd"},
|
||||||
|
new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
["ENV_OPT_A"] = $"foo{Path.PathSeparator}bar"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
var commandInstance = JsonConvert.DeserializeObject<WithEnvironmentVariablesCommand>(stdOutData);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
instance.Should().BeEquivalentTo(new EnvironmentVariableCommand
|
exitCode.Should().Be(0);
|
||||||
|
commandInstance.Should().BeEquivalentTo(new WithEnvironmentVariablesCommand
|
||||||
{
|
{
|
||||||
Option = expected
|
OptA = $"foo{Path.PathSeparator}bar"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
using CliFx.Exceptions;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class ErrorReportingSpecs
|
|
||||||
{
|
|
||||||
[Command("exc")]
|
|
||||||
private class GenericExceptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("msg", 'm')]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => throw new Exception(Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("exc")]
|
|
||||||
private class CommandExceptionCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("code", 'c')]
|
|
||||||
public int ExitCode { get; set; } = 133;
|
|
||||||
|
|
||||||
[CommandOption("msg", 'm')]
|
|
||||||
public string? Message { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("show-help")]
|
|
||||||
public bool ShowHelp { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => throw new CommandException(Message, ExitCode, ShowHelp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
using System.Collections.Generic;
|
using System.IO;
|
||||||
using System.IO;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Tests.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class ErrorReportingSpecs
|
public class ErrorReportingSpecs
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
@@ -17,28 +17,32 @@ namespace CliFx.Tests
|
|||||||
public async Task Command_may_throw_a_generic_exception_which_exits_and_prints_error_message_and_stack_trace()
|
public async Task Command_may_throw_a_generic_exception_which_exits_and_prints_error_message_and_stack_trace()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
await using var stdErr = new MemoryStream();
|
await using var stdErr = new MemoryStream();
|
||||||
var console = new VirtualConsole(error: stdErr);
|
|
||||||
|
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(GenericExceptionCommand))
|
.AddCommand<GenericExceptionCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"cmd", "-m", "Kaput"});
|
||||||
new[] {"exc", "-m", "Kaput"},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
|
stdOutData.Should().BeEmpty();
|
||||||
stdErrData.Should().ContainAll(
|
stdErrData.Should().ContainAll(
|
||||||
"System.Exception:",
|
"System.Exception:",
|
||||||
"Kaput", "at",
|
"Kaput", "at",
|
||||||
"CliFx.Tests");
|
"CliFx.Tests"
|
||||||
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
_output.WriteLine(stdErrData);
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -46,25 +50,28 @@ namespace CliFx.Tests
|
|||||||
public async Task Command_may_throw_a_specialized_exception_which_exits_with_custom_code_and_prints_minimal_error_details()
|
public async Task Command_may_throw_a_specialized_exception_which_exits_with_custom_code_and_prints_minimal_error_details()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
await using var stdErr = new MemoryStream();
|
await using var stdErr = new MemoryStream();
|
||||||
var console = new VirtualConsole(error: stdErr);
|
|
||||||
|
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(CommandExceptionCommand))
|
.AddCommand<CommandExceptionCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"cmd", "-m", "Kaput", "-c", "69"});
|
||||||
new[] {"exc", "-m", "Kaput", "-c", "69"},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().Be(69);
|
exitCode.Should().Be(69);
|
||||||
|
stdOutData.Should().BeEmpty();
|
||||||
stdErrData.Should().Be("Kaput");
|
stdErrData.Should().Be("Kaput");
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
_output.WriteLine(stdErrData);
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,28 +79,32 @@ namespace CliFx.Tests
|
|||||||
public async Task Command_may_throw_a_specialized_exception_without_error_message_which_exits_and_prints_full_error_details()
|
public async Task Command_may_throw_a_specialized_exception_without_error_message_which_exits_and_prints_full_error_details()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
await using var stdErr = new MemoryStream();
|
await using var stdErr = new MemoryStream();
|
||||||
var console = new VirtualConsole(error: stdErr);
|
|
||||||
|
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(CommandExceptionCommand))
|
.AddCommand<CommandExceptionCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"cmd"});
|
||||||
new[] {"exc"},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
|
stdOutData.Should().BeEmpty();
|
||||||
stdErrData.Should().ContainAll(
|
stdErrData.Should().ContainAll(
|
||||||
"CliFx.Exceptions.CommandException:",
|
"CliFx.Exceptions.CommandException:",
|
||||||
"at",
|
"at",
|
||||||
"CliFx.Tests");
|
"CliFx.Tests"
|
||||||
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
_output.WriteLine(stdErrData);
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,30 +118,27 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(CommandExceptionCommand))
|
.AddCommand<CommandExceptionCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"cmd", "-m", "Kaput", "--show-help"});
|
||||||
new[] {"exc", "-m", "Kaput", "--show-help"},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
stdErrData.Should().Be("Kaput");
|
|
||||||
|
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
"Usage",
|
||||||
"Options",
|
"Options",
|
||||||
"-h|--help", "Shows help text."
|
"-h|--help"
|
||||||
);
|
);
|
||||||
|
stdErrData.Should().Be("Kaput");
|
||||||
|
|
||||||
_output.WriteLine(stdErrData);
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -139,34 +147,31 @@ namespace CliFx.Tests
|
|||||||
// Arrange
|
// Arrange
|
||||||
await using var stdOut = new MemoryStream();
|
await using var stdOut = new MemoryStream();
|
||||||
await using var stdErr = new MemoryStream();
|
await using var stdErr = new MemoryStream();
|
||||||
|
|
||||||
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
var console = new VirtualConsole(output: stdOut, error: stdErr);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(CommandExceptionCommand))
|
.AddCommand<DefaultCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"not-a-valid-command", "-r", "foo"});
|
||||||
new[] {"not-a-valid-command", "-r", "foo"},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
stdErrData.Should().NotBeNullOrWhiteSpace();
|
|
||||||
|
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
"Usage",
|
||||||
"[command]",
|
|
||||||
"Options",
|
"Options",
|
||||||
"-h|--help", "Shows help text."
|
"-h|--help"
|
||||||
);
|
);
|
||||||
|
stdErrData.Should().NotBeNullOrWhiteSpace();
|
||||||
|
|
||||||
_output.WriteLine(stdErrData);
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,155 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class HelpTextSpecs
|
|
||||||
{
|
|
||||||
[Command(Description = "DefaultCommand description.")]
|
|
||||||
private class DefaultCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("option-a", 'a', Description = "OptionA description.")]
|
|
||||||
public string? OptionA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-b", 'b', Description = "OptionB description.")]
|
|
||||||
public string? OptionB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd", Description = "NamedCommand description.")]
|
|
||||||
private class NamedCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0, Name = "param-a", Description = "ParameterA description.")]
|
|
||||||
public string? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-c", 'c', Description = "OptionC description.")]
|
|
||||||
public string? OptionC { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-d", 'd', Description = "OptionD description.")]
|
|
||||||
public string? OptionD { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd sub", Description = "NamedSubCommand description.")]
|
|
||||||
private class NamedSubCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0, Name = "param-b", Description = "ParameterB description.")]
|
|
||||||
public string? ParameterB { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(1, Name = "param-c", Description = "ParameterC description.")]
|
|
||||||
public string? ParameterC { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-e", 'e', Description = "OptionE description.")]
|
|
||||||
public string? OptionE { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd-with-params")]
|
|
||||||
private class ParametersCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandParameter(0, Name = "first")]
|
|
||||||
public string? ParameterA { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(10)]
|
|
||||||
public int? ParameterB { get; set; }
|
|
||||||
|
|
||||||
[CommandParameter(20, Description = "A list of numbers", Name = "third list")]
|
|
||||||
public IEnumerable<int>? ParameterC { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option", 'o')]
|
|
||||||
public string? Option { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd-with-req-opts")]
|
|
||||||
private class RequiredOptionsCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("option-a", 'a', IsRequired = true)]
|
|
||||||
public string? OptionA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-b", 'b', IsRequired = true)]
|
|
||||||
public IEnumerable<int>? OptionB { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-c", 'c')]
|
|
||||||
public string? OptionC { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd-with-enum-args")]
|
|
||||||
private class EnumArgumentsCommand : ICommand
|
|
||||||
{
|
|
||||||
public enum CustomEnum { Value1, Value2, Value3 };
|
|
||||||
|
|
||||||
[CommandParameter(0, Name = "value", Description = "Enum parameter.")]
|
|
||||||
public CustomEnum ParamA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("value", Description = "Enum option.", IsRequired = true)]
|
|
||||||
public CustomEnum OptionA { get; set; } = CustomEnum.Value1;
|
|
||||||
|
|
||||||
[CommandOption("nullable-value", Description = "Nullable enum option.")]
|
|
||||||
public CustomEnum? OptionB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd-with-env-vars")]
|
|
||||||
private class EnvironmentVariableCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("option-a", 'a', IsRequired = true, EnvironmentVariableName = "ENV_OPT_A")]
|
|
||||||
public string? OptionA { get; set; }
|
|
||||||
|
|
||||||
[CommandOption("option-b", 'b', EnvironmentVariableName = "ENV_OPT_B")]
|
|
||||||
public string? OptionB { get; set; }
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("cmd-with-defaults")]
|
|
||||||
private class ArgumentsWithDefaultValuesCommand : ICommand
|
|
||||||
{
|
|
||||||
public enum CustomEnum { Value1, Value2, Value3 };
|
|
||||||
|
|
||||||
[CommandOption(nameof(Object))]
|
|
||||||
public object? Object { get; set; } = 42;
|
|
||||||
|
|
||||||
[CommandOption(nameof(String))]
|
|
||||||
public string? String { get; set; } = "foo";
|
|
||||||
|
|
||||||
[CommandOption(nameof(EmptyString))]
|
|
||||||
public string EmptyString { get; set; } = "";
|
|
||||||
|
|
||||||
[CommandOption(nameof(Bool))]
|
|
||||||
public bool Bool { get; set; } = true;
|
|
||||||
|
|
||||||
[CommandOption(nameof(Char))]
|
|
||||||
public char Char { get; set; } = 't';
|
|
||||||
|
|
||||||
[CommandOption(nameof(Int))]
|
|
||||||
public int Int { get; set; } = 1337;
|
|
||||||
|
|
||||||
[CommandOption(nameof(TimeSpan))]
|
|
||||||
public TimeSpan TimeSpan { get; set; } = TimeSpan.FromMinutes(123);
|
|
||||||
|
|
||||||
[CommandOption(nameof(Enum))]
|
|
||||||
public CustomEnum Enum { get; set; } = CustomEnum.Value2;
|
|
||||||
|
|
||||||
[CommandOption(nameof(IntNullable))]
|
|
||||||
public int? IntNullable { get; set; } = 1337;
|
|
||||||
|
|
||||||
[CommandOption(nameof(StringArray))]
|
|
||||||
public string[]? StringArray { get; set; } = { "foo", "bar", "baz" };
|
|
||||||
|
|
||||||
[CommandOption(nameof(IntArray))]
|
|
||||||
public int[]? IntArray { get; set; } = { 1, 2, 3 };
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +1,18 @@
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Tests.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class HelpTextSpecs
|
public class HelpTextSpecs
|
||||||
{
|
{
|
||||||
private readonly ITestOutputHelper _output;
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
public HelpTextSpecs(ITestOutputHelper output) => _output = output;
|
public HelpTextSpecs(ITestOutputHelper output) => _output = output;
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Version_information_can_be_requested_by_providing_the_version_option_without_other_arguments()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
await using var stdOut = new MemoryStream();
|
|
||||||
var console = new VirtualConsole(output: stdOut);
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
|
||||||
.AddCommand(typeof(DefaultCommand))
|
|
||||||
.AddCommand(typeof(NamedCommand))
|
|
||||||
.AddCommand(typeof(NamedSubCommand))
|
|
||||||
.UseVersionText("v6.9")
|
|
||||||
.UseConsole(console)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var exitCode = await application.RunAsync(new[] {"--version"});
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
exitCode.Should().Be(0);
|
|
||||||
stdOutData.Should().Be("v6.9");
|
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Help_text_can_be_requested_by_providing_the_help_option()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
await using var stdOut = new MemoryStream();
|
|
||||||
var console = new VirtualConsole(output: stdOut);
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
|
||||||
.AddCommand(typeof(DefaultCommand))
|
|
||||||
.AddCommand(typeof(NamedCommand))
|
|
||||||
.AddCommand(typeof(NamedSubCommand))
|
|
||||||
.UseTitle("AppTitle")
|
|
||||||
.UseVersionText("AppVer")
|
|
||||||
.UseDescription("AppDesc")
|
|
||||||
.UseExecutableName("AppExe")
|
|
||||||
.UseConsole(console)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await application.RunAsync(new[] {"--help"});
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
stdOutData.Should().ContainAll(
|
|
||||||
"AppTitle", "AppVer",
|
|
||||||
"AppDesc",
|
|
||||||
"Usage",
|
|
||||||
"AppExe", "[command]", "[options]",
|
|
||||||
"Options",
|
|
||||||
"-a|--option-a", "OptionA description.",
|
|
||||||
"-b|--option-b", "OptionB description.",
|
|
||||||
"-h|--help", "Shows help text.",
|
|
||||||
"--version", "Shows version information.",
|
|
||||||
"Commands",
|
|
||||||
"cmd", "NamedCommand description.",
|
|
||||||
"You can run", "to show help on a specific command."
|
|
||||||
);
|
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Help_text_can_be_requested_on_a_specific_named_command()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
await using var stdOut = new MemoryStream();
|
|
||||||
var console = new VirtualConsole(output: stdOut);
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
|
||||||
.AddCommand(typeof(DefaultCommand))
|
|
||||||
.AddCommand(typeof(NamedCommand))
|
|
||||||
.AddCommand(typeof(NamedSubCommand))
|
|
||||||
.UseConsole(console)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await application.RunAsync(new[] {"cmd", "--help"});
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
stdOutData.Should().ContainAll(
|
|
||||||
"Description",
|
|
||||||
"NamedCommand description.",
|
|
||||||
"Usage",
|
|
||||||
"cmd", "[command]", "<param-a>", "[options]",
|
|
||||||
"Parameters",
|
|
||||||
"* param-a", "ParameterA description.",
|
|
||||||
"Options",
|
|
||||||
"-c|--option-c", "OptionC description.",
|
|
||||||
"-d|--option-d", "OptionD description.",
|
|
||||||
"-h|--help", "Shows help text.",
|
|
||||||
"Commands",
|
|
||||||
"sub", "SubCommand description.",
|
|
||||||
"You can run", "to show help on a specific command."
|
|
||||||
);
|
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Help_text_can_be_requested_on_a_specific_named_sub_command()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
await using var stdOut = new MemoryStream();
|
|
||||||
var console = new VirtualConsole(output: stdOut);
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
|
||||||
.AddCommand(typeof(DefaultCommand))
|
|
||||||
.AddCommand(typeof(NamedCommand))
|
|
||||||
.AddCommand(typeof(NamedSubCommand))
|
|
||||||
.UseConsole(console)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await application.RunAsync(new[] {"cmd", "sub", "--help"});
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
stdOutData.Should().ContainAll(
|
|
||||||
"Description",
|
|
||||||
"SubCommand description.",
|
|
||||||
"Usage",
|
|
||||||
"cmd sub", "<param-b>", "<param-c>", "[options]",
|
|
||||||
"Parameters",
|
|
||||||
"* param-b", "ParameterB description.",
|
|
||||||
"* param-c", "ParameterC description.",
|
|
||||||
"Options",
|
|
||||||
"-e|--option-e", "OptionE description.",
|
|
||||||
"-h|--help", "Shows help text."
|
|
||||||
);
|
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
|
||||||
public async Task Help_text_can_be_requested_without_specifying_command_even_if_default_command_is_not_defined()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
await using var stdOut = new MemoryStream();
|
|
||||||
var console = new VirtualConsole(output: stdOut);
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
|
||||||
.AddCommand(typeof(NamedCommand))
|
|
||||||
.AddCommand(typeof(NamedSubCommand))
|
|
||||||
.UseConsole(console)
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await application.RunAsync(new[] {"--help"});
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
stdOutData.Should().ContainAll(
|
|
||||||
"Usage",
|
|
||||||
"[command]",
|
|
||||||
"Options",
|
|
||||||
"-h|--help", "Shows help text.",
|
|
||||||
"--version", "Shows version information.",
|
|
||||||
"Commands",
|
|
||||||
"cmd", "NamedCommand description.",
|
|
||||||
"You can run", "to show help on a specific command."
|
|
||||||
);
|
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Help_text_shows_usage_format_which_lists_all_parameters()
|
public async Task Help_text_shows_usage_format_which_lists_all_parameters()
|
||||||
{
|
{
|
||||||
@@ -192,18 +21,19 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(ParametersCommand))
|
.AddCommand<WithParametersCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] {"cmd-with-params", "--help"});
|
var exitCode = await application.RunAsync(new[] {"cmd", "--help"});
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
"Usage",
|
||||||
"cmd-with-params", "<first>", "<parameterb>", "<third list...>", "[options]"
|
"cmd", "<parama>", "<paramb>", "<paramc...>"
|
||||||
);
|
);
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
@@ -217,78 +47,79 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(RequiredOptionsCommand))
|
.AddCommand<WithRequiredOptionsCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] {"cmd-with-req-opts", "--help"});
|
var exitCode = await application.RunAsync(new[] {"cmd", "--help"});
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
"Usage",
|
||||||
"cmd-with-req-opts", "--option-a <value>", "--option-b <values...>", "[options]",
|
"cmd", "--opt-a <value>", "--opt-c <values...>", "[options]",
|
||||||
"Options",
|
"Options",
|
||||||
"* -a|--option-a",
|
"* -a|--opt-a",
|
||||||
"* -b|--option-b",
|
"-b|--opt-b",
|
||||||
"-c|--option-c"
|
"* -c|--opt-c"
|
||||||
);
|
);
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Help_text_lists_all_valid_values_for_enum_arguments()
|
public async Task Help_text_shows_all_valid_values_for_enum_arguments()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var stdOut = new MemoryStream();
|
await using var stdOut = new MemoryStream();
|
||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(EnumArgumentsCommand))
|
.AddCommand<WithEnumArgumentsCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] {"cmd-with-enum-args", "--help"});
|
var exitCode = await application.RunAsync(new[] {"cmd", "--help"});
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
|
||||||
"cmd-with-enum-args", "[options]",
|
|
||||||
"Parameters",
|
"Parameters",
|
||||||
"value", "Valid values: \"Value1\", \"Value2\", \"Value3\".",
|
"enum", "Valid values: \"Value1\", \"Value2\", \"Value3\".",
|
||||||
"Options",
|
"Options",
|
||||||
"* --value", "Enum option.", "Valid values: \"Value1\", \"Value2\", \"Value3\".",
|
"--enum", "Valid values: \"Value1\", \"Value2\", \"Value3\".",
|
||||||
"--nullable-value", "Nullable enum option.", "Valid values: \"Value1\", \"Value2\", \"Value3\"."
|
"* --required-enum", "Valid values: \"Value1\", \"Value2\", \"Value3\"."
|
||||||
);
|
);
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Help_text_lists_environment_variable_names_for_options_that_have_them_defined()
|
public async Task Help_text_shows_environment_variable_names_for_options_that_have_them_defined()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
await using var stdOut = new MemoryStream();
|
await using var stdOut = new MemoryStream();
|
||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(EnvironmentVariableCommand))
|
.AddCommand<WithEnvironmentVariablesCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] {"cmd-with-env-vars", "--help"});
|
var exitCode = await application.RunAsync(new[] {"cmd", "--help"});
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Options",
|
"Options",
|
||||||
"* -a|--option-a", "Environment variable:", "ENV_OPT_A",
|
"-a|--opt-a", "Environment variable:", "ENV_OPT_A",
|
||||||
"-b|--option-b", "Environment variable:", "ENV_OPT_B"
|
"-b|--opt-b", "Environment variable:", "ENV_OPT_B"
|
||||||
);
|
);
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
@@ -302,30 +133,29 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(ArgumentsWithDefaultValuesCommand))
|
.AddCommand<WithDefaultValuesCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] {"cmd-with-defaults", "--help"});
|
var exitCode = await application.RunAsync(new[] {"cmd", "--help"});
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
|
||||||
"cmd-with-defaults", "[options]",
|
|
||||||
"Options",
|
"Options",
|
||||||
"--Object", "Default: \"42\"",
|
"--obj", "Default: \"42\"",
|
||||||
"--String", "Default: \"foo\"",
|
"--str", "Default: \"foo\"",
|
||||||
"--EmptyString", "Default: \"\"",
|
"--str-empty", "Default: \"\"",
|
||||||
"--Bool", "Default: \"True\"",
|
"--str-array", "Default: \"foo\" \"bar\" \"baz\"",
|
||||||
"--Char", "Default: \"t\"",
|
"--bool", "Default: \"True\"",
|
||||||
"--Int", "Default: \"1337\"",
|
"--char", "Default: \"t\"",
|
||||||
"--TimeSpan", "Default: \"02:03:00\"",
|
"--int", "Default: \"1337\"",
|
||||||
"--Enum", "Default: \"Value2\"",
|
"--int-nullable", "Default: \"1337\"",
|
||||||
"--IntNullable", "Default: \"1337\"",
|
"--int-array", "Default: \"1\" \"2\" \"3\"",
|
||||||
"--StringArray", "Default: \"foo\" \"bar\" \"baz\"",
|
"--timespan", "Default: \"02:03:00\"",
|
||||||
"--IntArray", "Default: \"1\" \"2\" \"3\""
|
"--enum", "Default: \"Value2\""
|
||||||
);
|
);
|
||||||
|
|
||||||
_output.WriteLine(stdOutData);
|
_output.WriteLine(stdOutData);
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using CliFx.Domain;
|
|
||||||
|
|
||||||
namespace CliFx.Tests.Internal
|
|
||||||
{
|
|
||||||
internal static class CommandHelper
|
|
||||||
{
|
|
||||||
public static TCommand ResolveCommand<TCommand>(CommandInput input, IReadOnlyDictionary<string, string> environmentVariables)
|
|
||||||
where TCommand : ICommand, new()
|
|
||||||
{
|
|
||||||
var schema = CommandSchema.TryResolve(typeof(TCommand))!;
|
|
||||||
|
|
||||||
var instance = new TCommand();
|
|
||||||
schema.Bind(instance, input, environmentVariables);
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TCommand ResolveCommand<TCommand>(CommandInput input)
|
|
||||||
where TCommand : ICommand, new() =>
|
|
||||||
ResolveCommand<TCommand>(input, new Dictionary<string, string>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using CliFx.Domain;
|
|
||||||
|
|
||||||
namespace CliFx.Tests.Internal
|
|
||||||
{
|
|
||||||
internal class CommandInputBuilder
|
|
||||||
{
|
|
||||||
private readonly List<CommandDirectiveInput> _directives = new List<CommandDirectiveInput>();
|
|
||||||
private readonly List<CommandParameterInput> _parameters = new List<CommandParameterInput>();
|
|
||||||
private readonly List<CommandOptionInput> _options = new List<CommandOptionInput>();
|
|
||||||
|
|
||||||
private string? _commandName;
|
|
||||||
|
|
||||||
public CommandInputBuilder SetCommandName(string commandName)
|
|
||||||
{
|
|
||||||
_commandName = commandName;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandInputBuilder AddDirective(string directive)
|
|
||||||
{
|
|
||||||
_directives.Add(new CommandDirectiveInput(directive));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandInputBuilder AddParameter(string parameter)
|
|
||||||
{
|
|
||||||
_parameters.Add(new CommandParameterInput(parameter));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandInputBuilder AddOption(string alias, params string[] values)
|
|
||||||
{
|
|
||||||
_options.Add(new CommandOptionInput(alias, values));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CommandInput Build() => new CommandInput(
|
|
||||||
_directives,
|
|
||||||
_commandName,
|
|
||||||
_parameters,
|
|
||||||
_options
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace CliFx.Tests.Internal
|
|
||||||
{
|
|
||||||
internal static class TaskExtensions
|
|
||||||
{
|
|
||||||
public static async Task IgnoreCancellation(this Task task)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
await task;
|
|
||||||
}
|
|
||||||
catch (OperationCanceledException)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using CliFx.Attributes;
|
|
||||||
|
|
||||||
namespace CliFx.Tests
|
|
||||||
{
|
|
||||||
public partial class RoutingSpecs
|
|
||||||
{
|
|
||||||
[Command]
|
|
||||||
private class DefaultCommand : ICommand
|
|
||||||
{
|
|
||||||
public ValueTask ExecuteAsync(IConsole console)
|
|
||||||
{
|
|
||||||
console.Output.WriteLine("Hello world!");
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("concat", Description = "Concatenate strings.")]
|
|
||||||
private class ConcatCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption('i', IsRequired = true, Description = "Input strings.")]
|
|
||||||
public IReadOnlyList<string> Inputs { get; set; } = Array.Empty<string>();
|
|
||||||
|
|
||||||
[CommandOption('s', Description = "String separator.")]
|
|
||||||
public string Separator { get; set; } = "";
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console)
|
|
||||||
{
|
|
||||||
console.Output.WriteLine(string.Join(Separator, Inputs));
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Command("div", Description = "Divide one number by another.")]
|
|
||||||
private class DivideCommand : ICommand
|
|
||||||
{
|
|
||||||
[CommandOption("dividend", 'D', IsRequired = true, Description = "The number to divide.")]
|
|
||||||
public double Dividend { get; set; } = 0;
|
|
||||||
|
|
||||||
[CommandOption("divisor", 'd', IsRequired = true, Description = "The number to divide by.")]
|
|
||||||
public double Divisor { get; set; } = 0;
|
|
||||||
|
|
||||||
public ValueTask ExecuteAsync(IConsole console)
|
|
||||||
{
|
|
||||||
console.Output.WriteLine(Dividend / Divisor);
|
|
||||||
return default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using CliFx.Tests.Commands;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class RoutingSpecs
|
public class RoutingSpecs
|
||||||
{
|
{
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
public RoutingSpecs(ITestOutputHelper testOutput) => _output = testOutput;
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Default_command_is_executed_if_provided_arguments_do_not_match_any_named_command()
|
public async Task Default_command_is_executed_if_provided_arguments_do_not_match_any_named_command()
|
||||||
{
|
{
|
||||||
@@ -17,48 +22,21 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(DefaultCommand))
|
.AddCommand<DefaultCommand>()
|
||||||
.AddCommand(typeof(ConcatCommand))
|
.AddCommand<NamedCommand>()
|
||||||
.AddCommand(typeof(DivideCommand))
|
.AddCommand<NamedSubCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
Array.Empty<string>(),
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().Be(0);
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().Be("Hello world!");
|
stdOutData.Should().Be(DefaultCommand.ExpectedOutputText);
|
||||||
}
|
|
||||||
|
|
||||||
[Fact]
|
_output.WriteLine(stdOutData);
|
||||||
public async Task Help_text_is_printed_if_no_arguments_were_provided_and_default_command_is_not_defined()
|
|
||||||
{
|
|
||||||
// Arrange
|
|
||||||
await using var stdOut = new MemoryStream();
|
|
||||||
var console = new VirtualConsole(output: stdOut);
|
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
|
||||||
.AddCommand(typeof(ConcatCommand))
|
|
||||||
.AddCommand(typeof(DivideCommand))
|
|
||||||
.UseConsole(console)
|
|
||||||
.UseDescription("This will be visible in help")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
var exitCode = await application.RunAsync(
|
|
||||||
Array.Empty<string>(),
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
exitCode.Should().Be(0);
|
|
||||||
stdOutData.Should().Contain("This will be visible in help");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -69,22 +47,208 @@ namespace CliFx.Tests
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var application = new CliApplicationBuilder()
|
var application = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(DefaultCommand))
|
.AddCommand<DefaultCommand>()
|
||||||
.AddCommand(typeof(ConcatCommand))
|
.AddCommand<NamedCommand>()
|
||||||
.AddCommand(typeof(DivideCommand))
|
.AddCommand<NamedSubCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(new[] {"named"});
|
||||||
new[] {"concat", "-i", "foo", "bar", "-s", ", "},
|
|
||||||
new Dictionary<string, string>());
|
|
||||||
|
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().Be(0);
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().Be("foo, bar");
|
stdOutData.Should().Be(NamedCommand.ExpectedOutputText);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Specific_named_sub_command_is_executed_if_provided_arguments_match_its_name()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DefaultCommand>()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(new[] {"named", "sub"});
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().Be(NamedSubCommand.ExpectedOutputText);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_is_printed_if_no_arguments_were_provided_and_default_command_is_not_defined()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.UseDescription("This will be visible in help")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(Array.Empty<string>());
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().Contain("This will be visible in help");
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_is_printed_if_provided_arguments_contain_the_help_option()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DefaultCommand>()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(new[] {"--help"});
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().ContainAll(
|
||||||
|
nameof(DefaultCommand),
|
||||||
|
"Usage"
|
||||||
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_is_printed_if_provided_arguments_contain_the_help_option_even_if_default_command_is_not_defined()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseDescription("This will be visible in help")
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(new[] {"--help"});
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().Contain("This will be visible in help");
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_for_a_specific_named_command_is_printed_if_provided_arguments_match_its_name_and_contain_the_help_option()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DefaultCommand>()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(new[] {"named", "--help"});
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().ContainAll(
|
||||||
|
nameof(NamedCommand),
|
||||||
|
"Usage",
|
||||||
|
"named"
|
||||||
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_for_a_specific_named_sub_command_is_printed_if_provided_arguments_match_its_name_and_contain_the_help_option()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DefaultCommand>()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(new[] {"named", "sub", "--help"});
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().ContainAll(
|
||||||
|
nameof(NamedSubCommand),
|
||||||
|
"Usage",
|
||||||
|
"named", "sub"
|
||||||
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Version_is_printed_if_the_only_provided_argument_is_the_version_option()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
await using var stdOut = new MemoryStream();
|
||||||
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand<DefaultCommand>()
|
||||||
|
.AddCommand<NamedCommand>()
|
||||||
|
.AddCommand<NamedSubCommand>()
|
||||||
|
.UseVersionText("v6.9")
|
||||||
|
.UseConsole(console)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(new[] {"--version"});
|
||||||
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOutData.Should().Be("v6.9");
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,12 @@ namespace CliFx
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a command of specified type to the application.
|
||||||
|
/// </summary>
|
||||||
|
public CliApplicationBuilder AddCommand<TCommand>() where TCommand : ICommand =>
|
||||||
|
AddCommand(typeof(TCommand));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds multiple commands to the application.
|
/// Adds multiple commands to the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -23,15 +23,6 @@
|
|||||||
<Nullable>annotations</Nullable>
|
<Nullable>annotations</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
|
|
||||||
<_Parameter1>$(AssemblyName).Tests</_Parameter1>
|
|
||||||
</AssemblyAttribute>
|
|
||||||
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
|
|
||||||
<_Parameter1>$(AssemblyName).Analyzers</_Parameter1>
|
|
||||||
</AssemblyAttribute>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="all" />
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
|
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.0.0" PrivateAssets="all" />
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ namespace CliFx.Domain
|
|||||||
? ConvertScalar(value, nullableUnderlyingType)
|
? ConvertScalar(value, nullableUnderlyingType)
|
||||||
: null;
|
: null;
|
||||||
|
|
||||||
// String-constructable
|
// String-constructible
|
||||||
var stringConstructor = targetType.GetConstructor(new[] {typeof(string)});
|
var stringConstructor = targetType.GetConstructor(new[] {typeof(string)});
|
||||||
if (stringConstructor != null)
|
if (stringConstructor != null)
|
||||||
return stringConstructor.Invoke(new object[] {value!});
|
return stringConstructor.Invoke(new object[] {value!});
|
||||||
@@ -83,7 +83,7 @@ namespace CliFx.Domain
|
|||||||
if (targetEnumerableType.IsAssignableFrom(arrayType))
|
if (targetEnumerableType.IsAssignableFrom(arrayType))
|
||||||
return array;
|
return array;
|
||||||
|
|
||||||
// Constructable from an array
|
// Constructible from an array
|
||||||
var arrayConstructor = targetEnumerableType.GetConstructor(new[] {arrayType});
|
var arrayConstructor = targetEnumerableType.GetConstructor(new[] {arrayType});
|
||||||
if (arrayConstructor != null)
|
if (arrayConstructor != null)
|
||||||
return arrayConstructor.Invoke(new object[] {array});
|
return arrayConstructor.Invoke(new object[] {array});
|
||||||
|
|||||||
@@ -573,7 +573,7 @@ public async Task ConcatCommand_Test()
|
|||||||
var console = new VirtualConsole(output: stdOut);
|
var console = new VirtualConsole(output: stdOut);
|
||||||
|
|
||||||
var app = new CliApplicationBuilder()
|
var app = new CliApplicationBuilder()
|
||||||
.AddCommand(typeof(ConcatCommand))
|
.AddCommand<ConcatCommand>()
|
||||||
.UseConsole(console)
|
.UseConsole(console)
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user