Refactor a bit

This commit is contained in:
Alexey Golub
2020-05-05 22:23:27 +03:00
parent c58629e999
commit cbb72b16ae
7 changed files with 82 additions and 23 deletions

View File

@@ -3,11 +3,16 @@ using System.IO;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;
namespace CliFx.Tests
{
public partial class ErrorReportingSpecs
{
private readonly ITestOutputHelper _output;
public ErrorReportingSpecs(ITestOutputHelper output) => _output = output;
[Fact]
public async Task Command_may_throw_a_generic_exception_which_exits_and_prints_error_message_and_stack_trace()
{
@@ -31,8 +36,10 @@ namespace CliFx.Tests
exitCode.Should().NotBe(0);
stdErrData.Should().ContainAll(
"System.Exception:",
"Kaput", "at",
"Kaput", "at",
"CliFx.Tests");
_output.WriteLine(stdErrData);
}
[Fact]
@@ -57,6 +64,8 @@ namespace CliFx.Tests
// Assert
exitCode.Should().Be(69);
stdErrData.Should().Be("Kaput");
_output.WriteLine(stdErrData);
}
[Fact]
@@ -81,6 +90,8 @@ namespace CliFx.Tests
// Assert
exitCode.Should().NotBe(0);
stdErrData.Should().NotBeEmpty();
_output.WriteLine(stdErrData);
}
[Fact]
@@ -99,7 +110,7 @@ namespace CliFx.Tests
.Build();
// Act
await application.RunAsync(new[] { "exc" });
await application.RunAsync(new[] {"exc"});
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
var stdErrData = console.Output.Encoding.GetString(stdErr.ToArray()).TrimEnd();
@@ -114,6 +125,9 @@ namespace CliFx.Tests
"sub",
"You can run", "to show help on a specific command."
);
_output.WriteLine(stdOutData);
_output.WriteLine(stdErrData);
}
[Fact]
@@ -131,7 +145,7 @@ namespace CliFx.Tests
.Build();
// Act
await application.RunAsync(new[] { "exc" });
await application.RunAsync(new[] {"exc"});
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
@@ -146,6 +160,9 @@ namespace CliFx.Tests
"sub",
"You can run", "to show help on a specific command."
);
_output.WriteLine(stdOutData);
_output.WriteLine(stdErrData);
}
[Fact]
@@ -162,7 +179,7 @@ namespace CliFx.Tests
// Act
var exitCode = await application.RunAsync(
new[] { "exc", "-m", "Kaput" },
new[] {"exc", "-m", "Kaput"},
new Dictionary<string, string>());
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
@@ -173,6 +190,8 @@ namespace CliFx.Tests
"System.Exception:",
"Kaput", "at",
"CliFx.Tests");
_output.WriteLine(stdErrData);
}
[Fact]
@@ -190,7 +209,7 @@ namespace CliFx.Tests
// Act
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();
@@ -199,8 +218,9 @@ namespace CliFx.Tests
// Assert
exitCode.Should().NotBe(0);
stdErrData.Should().ContainAll(
"Can't find a command that matches the following arguments:",
"not-a-valid-command");
"Can't find a command that matches the following arguments:",
"not-a-valid-command"
);
stdOutData.Should().ContainAll(
"Usage",
"[command]",
@@ -208,7 +228,11 @@ namespace CliFx.Tests
"-h|--help", "Shows help text.",
"Commands",
"inv",
"You can run", "to show help on a specific command.");
"You can run", "to show help on a specific command."
);
_output.WriteLine(stdOutData);
_output.WriteLine(stdErrData);
}
}
}

View File

@@ -2,11 +2,16 @@
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using Xunit.Abstractions;
namespace CliFx.Tests
{
public partial class HelpTextSpecs
{
private readonly ITestOutputHelper _output;
public HelpTextSpecs(ITestOutputHelper output) => _output = output;
[Fact]
public async Task Version_information_can_be_requested_by_providing_the_version_option_without_other_arguments()
{
@@ -29,6 +34,8 @@ namespace CliFx.Tests
// Assert
exitCode.Should().Be(0);
stdOutData.Should().Be("v6.9");
_output.WriteLine(stdOutData);
}
[Fact]
@@ -68,6 +75,8 @@ namespace CliFx.Tests
"cmd", "NamedCommand description.",
"You can run", "to show help on a specific command."
);
_output.WriteLine(stdOutData);
}
[Fact]
@@ -104,6 +113,8 @@ namespace CliFx.Tests
"sub", "SubCommand description.",
"You can run", "to show help on a specific command."
);
_output.WriteLine(stdOutData);
}
[Fact]
@@ -137,6 +148,8 @@ namespace CliFx.Tests
"-e|--option-e", "OptionE description.",
"-h|--help", "Shows help text."
);
_output.WriteLine(stdOutData);
}
[Fact]
@@ -167,6 +180,8 @@ namespace CliFx.Tests
"cmd", "NamedCommand description.",
"You can run", "to show help on a specific command."
);
_output.WriteLine(stdOutData);
}
[Fact]
@@ -190,6 +205,8 @@ namespace CliFx.Tests
"Usage",
"cmd-with-params", "<first>", "<parameterb>", "<third list...>", "[options]"
);
_output.WriteLine(stdOutData);
}
[Fact]
@@ -217,6 +234,8 @@ namespace CliFx.Tests
"* -g|--option-g",
"-h|--option-h"
);
_output.WriteLine(stdOutData);
}
[Fact]
@@ -241,6 +260,8 @@ namespace CliFx.Tests
"* -a|--option-a", "Environment variable:", "ENV_OPT_A",
"-b|--option-b", "Environment variable:", "ENV_OPT_B"
);
_output.WriteLine(stdOutData);
}
}
}

View File

@@ -51,7 +51,6 @@
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="true" IncludeAssets="CliFx.Analyzers.dll" />
</ItemGroup>
<Target Name="CopyAnalyzerToPackage">
<ItemGroup>
<TfmSpecificPackageFile Include="$(OutDir)/CliFx.Analyzers.dll" PackagePath="analyzers/dotnet/cs" BuildAction="none" />

View File

@@ -39,10 +39,14 @@ namespace CliFx.Domain
!string.IsNullOrWhiteSpace(Name) &&
string.Equals(Name, name, StringComparison.OrdinalIgnoreCase);
public bool MatchesShortName(char shortName) =>
public bool MatchesShortName(char? shortName) =>
ShortName != null &&
ShortName == shortName;
public bool MatchesNameOrShortName(string? name, char? shortName) =>
MatchesName(name) ||
MatchesShortName(shortName);
public bool MatchesNameOrShortName(string alias) =>
MatchesName(alias) ||
alias.Length == 1 && MatchesShortName(alias.Single());

View File

@@ -40,6 +40,21 @@ namespace CliFx.Domain
public bool MatchesName(string? name) => string.Equals(name, Name, StringComparison.OrdinalIgnoreCase);
public IReadOnlyList<CommandOptionSchema> GetBuiltInOptions()
{
var result = new List<CommandOptionSchema>(2);
var helpOption = CommandOptionSchema.HelpOption;
var versionOption = CommandOptionSchema.VersionOption;
result.Add(helpOption);
if (IsDefault)
result.Add(versionOption);
return result;
}
private void InjectParameters(ICommand command, IReadOnlyList<CommandUnboundArgumentInput> parameterInputs)
{
// All inputs must be bound

View File

@@ -212,12 +212,8 @@ namespace CliFx.Domain
var options = command.Options
.OrderByDescending(o => o.IsRequired)
.ToList();
// Add built-in options
options.Add(CommandOptionSchema.HelpOption);
if (command.IsDefault)
options.Add(CommandOptionSchema.VersionOption);
.Concat(command.GetBuiltInOptions())
.ToArray();
foreach (var option in options)
{

View File

@@ -25,7 +25,7 @@ namespace CliFx.Exceptions
/// Whether this exception was constructed with a message.
/// </summary>
/// <remarks>
/// We cannot check against the 'Message' property because it will always return
/// We cannot check against the 'Message' property because it will always return
/// a default message if it was constructed with a null value or is currently null.
/// </remarks>
public bool HasMessage { get; }
@@ -38,7 +38,7 @@ namespace CliFx.Exceptions
/// <summary>
/// Initializes an instance of <see cref="CliFxException"/>.
/// </summary>
public CliFxException(string? message, bool showHelp = false)
public CliFxException(string? message, bool showHelp = false)
: this(message, null, showHelp: showHelp)
{
}
@@ -217,7 +217,7 @@ If that's not possible, consider converting the parameter into an option, to avo
{
var message = $@"
Command '{command.Type.FullName}' is invalid because it contains one or more options without a name:
{string.Join(Environment.NewLine, invalidOptions.Select(p => p.Property.Name))}
{string.Join(Environment.NewLine, invalidOptions.Select(o => o.Property.Name))}
Options must have either a name or a short name or both, because that's what identifies them.
@@ -232,7 +232,7 @@ To fix this, ensure all options have their names or short names set to some valu
{
var message = $@"
Command '{command.Type.FullName}' is invalid because it contains one or more options whose names are too short:
{string.Join(Environment.NewLine, invalidOptions.Select(p => $"{p.Property.Name} ('{p.DisplayName}')"))}
{string.Join(Environment.NewLine, invalidOptions.Select(o => $"{o.Property.Name} ('{o.DisplayName}')"))}
Option names must be at least 2 characters long to avoid confusion with short names.
If you intended to set the short name instead, use the corresponding attribute overload.
@@ -249,7 +249,7 @@ To fix this, ensure all option names are at least 2 characters long.";
{
var message = $@"
Command '{command.Type.FullName}' is invalid because it contains {invalidOptions.Count} options with the same name ('{name}'):
{string.Join(Environment.NewLine, invalidOptions.Select(p => p.Property.Name))}
{string.Join(Environment.NewLine, invalidOptions.Select(o => o.Property.Name))}
Options must have unique names, because that's what identifies them.
Names are not case-sensitive.
@@ -266,7 +266,7 @@ To fix this, ensure that all options have different names.";
{
var message = $@"
Command '{command.Type.FullName}' is invalid because it contains {invalidOptions.Count} options with the same short name ('{shortName}'):
{string.Join(Environment.NewLine, invalidOptions.Select(p => p.Property.Name))}
{string.Join(Environment.NewLine, invalidOptions.Select(o => o.Property.Name))}
Options must have unique short names, because that's what identifies them.
Short names are case-sensitive (i.e. 'a' and 'A' are different short names).
@@ -283,7 +283,7 @@ To fix this, ensure that all options have different short names.";
{
var message = $@"
Command '{command.Type.FullName}' is invalid because it contains {invalidOptions.Count} options with the same fallback environment variable name ('{environmentVariableName}'):
{string.Join(Environment.NewLine, invalidOptions.Select(p => p.Property.Name))}
{string.Join(Environment.NewLine, invalidOptions.Select(o => o.Property.Name))}
Options cannot share the same environment variable as a fallback.
Environment variable names are not case-sensitive.