mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Refactor a bit
This commit is contained in:
@@ -3,11 +3,16 @@ using System.IO;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class ErrorReportingSpecs
|
public partial class ErrorReportingSpecs
|
||||||
{
|
{
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
public ErrorReportingSpecs(ITestOutputHelper output) => _output = output;
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
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()
|
||||||
{
|
{
|
||||||
@@ -33,6 +38,8 @@ namespace CliFx.Tests
|
|||||||
"System.Exception:",
|
"System.Exception:",
|
||||||
"Kaput", "at",
|
"Kaput", "at",
|
||||||
"CliFx.Tests");
|
"CliFx.Tests");
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -57,6 +64,8 @@ namespace CliFx.Tests
|
|||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().Be(69);
|
exitCode.Should().Be(69);
|
||||||
stdErrData.Should().Be("Kaput");
|
stdErrData.Should().Be("Kaput");
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -81,6 +90,8 @@ namespace CliFx.Tests
|
|||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
stdErrData.Should().NotBeEmpty();
|
stdErrData.Should().NotBeEmpty();
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -99,7 +110,7 @@ namespace CliFx.Tests
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] { "exc" });
|
await application.RunAsync(new[] {"exc"});
|
||||||
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
var stdOutData = console.Output.Encoding.GetString(stdOut.ToArray()).TrimEnd();
|
||||||
var stdErrData = console.Output.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
var stdErrData = console.Output.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
|
|
||||||
@@ -114,6 +125,9 @@ namespace CliFx.Tests
|
|||||||
"sub",
|
"sub",
|
||||||
"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);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -131,7 +145,7 @@ namespace CliFx.Tests
|
|||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
await application.RunAsync(new[] { "exc" });
|
await application.RunAsync(new[] {"exc"});
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
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();
|
||||||
|
|
||||||
@@ -146,6 +160,9 @@ namespace CliFx.Tests
|
|||||||
"sub",
|
"sub",
|
||||||
"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);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -162,7 +179,7 @@ namespace CliFx.Tests
|
|||||||
|
|
||||||
// Act
|
// Act
|
||||||
var exitCode = await application.RunAsync(
|
var exitCode = await application.RunAsync(
|
||||||
new[] { "exc", "-m", "Kaput" },
|
new[] {"exc", "-m", "Kaput"},
|
||||||
new Dictionary<string, string>());
|
new Dictionary<string, string>());
|
||||||
|
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
@@ -173,6 +190,8 @@ namespace CliFx.Tests
|
|||||||
"System.Exception:",
|
"System.Exception:",
|
||||||
"Kaput", "at",
|
"Kaput", "at",
|
||||||
"CliFx.Tests");
|
"CliFx.Tests");
|
||||||
|
|
||||||
|
_output.WriteLine(stdErrData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -190,7 +209,7 @@ namespace CliFx.Tests
|
|||||||
|
|
||||||
// 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>());
|
new Dictionary<string, string>());
|
||||||
|
|
||||||
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
var stdErrData = console.Error.Encoding.GetString(stdErr.ToArray()).TrimEnd();
|
||||||
@@ -200,7 +219,8 @@ namespace CliFx.Tests
|
|||||||
exitCode.Should().NotBe(0);
|
exitCode.Should().NotBe(0);
|
||||||
stdErrData.Should().ContainAll(
|
stdErrData.Should().ContainAll(
|
||||||
"Can't find a command that matches the following arguments:",
|
"Can't find a command that matches the following arguments:",
|
||||||
"not-a-valid-command");
|
"not-a-valid-command"
|
||||||
|
);
|
||||||
stdOutData.Should().ContainAll(
|
stdOutData.Should().ContainAll(
|
||||||
"Usage",
|
"Usage",
|
||||||
"[command]",
|
"[command]",
|
||||||
@@ -208,7 +228,11 @@ namespace CliFx.Tests
|
|||||||
"-h|--help", "Shows help text.",
|
"-h|--help", "Shows help text.",
|
||||||
"Commands",
|
"Commands",
|
||||||
"inv",
|
"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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,11 +2,16 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
using Xunit.Abstractions;
|
||||||
|
|
||||||
namespace CliFx.Tests
|
namespace CliFx.Tests
|
||||||
{
|
{
|
||||||
public partial class HelpTextSpecs
|
public partial class HelpTextSpecs
|
||||||
{
|
{
|
||||||
|
private readonly ITestOutputHelper _output;
|
||||||
|
|
||||||
|
public HelpTextSpecs(ITestOutputHelper output) => _output = output;
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Version_information_can_be_requested_by_providing_the_version_option_without_other_arguments()
|
public async Task Version_information_can_be_requested_by_providing_the_version_option_without_other_arguments()
|
||||||
{
|
{
|
||||||
@@ -29,6 +34,8 @@ namespace CliFx.Tests
|
|||||||
// Assert
|
// Assert
|
||||||
exitCode.Should().Be(0);
|
exitCode.Should().Be(0);
|
||||||
stdOutData.Should().Be("v6.9");
|
stdOutData.Should().Be("v6.9");
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -68,6 +75,8 @@ namespace CliFx.Tests
|
|||||||
"cmd", "NamedCommand description.",
|
"cmd", "NamedCommand description.",
|
||||||
"You can run", "to show help on a specific command."
|
"You can run", "to show help on a specific command."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -104,6 +113,8 @@ namespace CliFx.Tests
|
|||||||
"sub", "SubCommand description.",
|
"sub", "SubCommand description.",
|
||||||
"You can run", "to show help on a specific command."
|
"You can run", "to show help on a specific command."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -137,6 +148,8 @@ namespace CliFx.Tests
|
|||||||
"-e|--option-e", "OptionE description.",
|
"-e|--option-e", "OptionE description.",
|
||||||
"-h|--help", "Shows help text."
|
"-h|--help", "Shows help text."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -167,6 +180,8 @@ namespace CliFx.Tests
|
|||||||
"cmd", "NamedCommand description.",
|
"cmd", "NamedCommand description.",
|
||||||
"You can run", "to show help on a specific command."
|
"You can run", "to show help on a specific command."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -190,6 +205,8 @@ namespace CliFx.Tests
|
|||||||
"Usage",
|
"Usage",
|
||||||
"cmd-with-params", "<first>", "<parameterb>", "<third list...>", "[options]"
|
"cmd-with-params", "<first>", "<parameterb>", "<third list...>", "[options]"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -217,6 +234,8 @@ namespace CliFx.Tests
|
|||||||
"* -g|--option-g",
|
"* -g|--option-g",
|
||||||
"-h|--option-h"
|
"-h|--option-h"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
@@ -241,6 +260,8 @@ namespace CliFx.Tests
|
|||||||
"* -a|--option-a", "Environment variable:", "ENV_OPT_A",
|
"* -a|--option-a", "Environment variable:", "ENV_OPT_A",
|
||||||
"-b|--option-b", "Environment variable:", "ENV_OPT_B"
|
"-b|--option-b", "Environment variable:", "ENV_OPT_B"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
_output.WriteLine(stdOutData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -51,7 +51,6 @@
|
|||||||
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="true" IncludeAssets="CliFx.Analyzers.dll" />
|
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="true" IncludeAssets="CliFx.Analyzers.dll" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|
||||||
<Target Name="CopyAnalyzerToPackage">
|
<Target Name="CopyAnalyzerToPackage">
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<TfmSpecificPackageFile Include="$(OutDir)/CliFx.Analyzers.dll" PackagePath="analyzers/dotnet/cs" BuildAction="none" />
|
<TfmSpecificPackageFile Include="$(OutDir)/CliFx.Analyzers.dll" PackagePath="analyzers/dotnet/cs" BuildAction="none" />
|
||||||
|
|||||||
@@ -39,10 +39,14 @@ namespace CliFx.Domain
|
|||||||
!string.IsNullOrWhiteSpace(Name) &&
|
!string.IsNullOrWhiteSpace(Name) &&
|
||||||
string.Equals(Name, name, StringComparison.OrdinalIgnoreCase);
|
string.Equals(Name, name, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
public bool MatchesShortName(char shortName) =>
|
public bool MatchesShortName(char? shortName) =>
|
||||||
ShortName != null &&
|
ShortName != null &&
|
||||||
ShortName == shortName;
|
ShortName == shortName;
|
||||||
|
|
||||||
|
public bool MatchesNameOrShortName(string? name, char? shortName) =>
|
||||||
|
MatchesName(name) ||
|
||||||
|
MatchesShortName(shortName);
|
||||||
|
|
||||||
public bool MatchesNameOrShortName(string alias) =>
|
public bool MatchesNameOrShortName(string alias) =>
|
||||||
MatchesName(alias) ||
|
MatchesName(alias) ||
|
||||||
alias.Length == 1 && MatchesShortName(alias.Single());
|
alias.Length == 1 && MatchesShortName(alias.Single());
|
||||||
|
|||||||
@@ -40,6 +40,21 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
public bool MatchesName(string? name) => string.Equals(name, Name, StringComparison.OrdinalIgnoreCase);
|
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)
|
private void InjectParameters(ICommand command, IReadOnlyList<CommandUnboundArgumentInput> parameterInputs)
|
||||||
{
|
{
|
||||||
// All inputs must be bound
|
// All inputs must be bound
|
||||||
|
|||||||
@@ -212,12 +212,8 @@ namespace CliFx.Domain
|
|||||||
|
|
||||||
var options = command.Options
|
var options = command.Options
|
||||||
.OrderByDescending(o => o.IsRequired)
|
.OrderByDescending(o => o.IsRequired)
|
||||||
.ToList();
|
.Concat(command.GetBuiltInOptions())
|
||||||
|
.ToArray();
|
||||||
// Add built-in options
|
|
||||||
options.Add(CommandOptionSchema.HelpOption);
|
|
||||||
if (command.IsDefault)
|
|
||||||
options.Add(CommandOptionSchema.VersionOption);
|
|
||||||
|
|
||||||
foreach (var option in options)
|
foreach (var option in options)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ If that's not possible, consider converting the parameter into an option, to avo
|
|||||||
{
|
{
|
||||||
var message = $@"
|
var message = $@"
|
||||||
Command '{command.Type.FullName}' is invalid because it contains one or more options without a name:
|
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.
|
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 = $@"
|
var message = $@"
|
||||||
Command '{command.Type.FullName}' is invalid because it contains one or more options whose names are too short:
|
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.
|
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.
|
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 = $@"
|
var message = $@"
|
||||||
Command '{command.Type.FullName}' is invalid because it contains {invalidOptions.Count} options with the same name ('{name}'):
|
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.
|
Options must have unique names, because that's what identifies them.
|
||||||
Names are not case-sensitive.
|
Names are not case-sensitive.
|
||||||
@@ -266,7 +266,7 @@ To fix this, ensure that all options have different names.";
|
|||||||
{
|
{
|
||||||
var message = $@"
|
var message = $@"
|
||||||
Command '{command.Type.FullName}' is invalid because it contains {invalidOptions.Count} options with the same short name ('{shortName}'):
|
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.
|
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).
|
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 = $@"
|
var message = $@"
|
||||||
Command '{command.Type.FullName}' is invalid because it contains {invalidOptions.Count} options with the same fallback environment variable name ('{environmentVariableName}'):
|
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.
|
Options cannot share the same environment variable as a fallback.
|
||||||
Environment variable names are not case-sensitive.
|
Environment variable names are not case-sensitive.
|
||||||
|
|||||||
Reference in New Issue
Block a user