From dd882a63723af6ddbed37f9778278cd4a43ed962 Mon Sep 17 00:00:00 2001 From: Alexey Golub Date: Mon, 19 Aug 2019 22:49:21 +0300 Subject: [PATCH] Refactor tests and add best-effort tests for HelpTextRenderer --- CliFx.Tests/CliApplicationTests.Commands.cs | 28 +++++ CliFx.Tests/CliApplicationTests.cs | 24 ---- CliFx.Tests/CommandFactoryTests.Commands.cs | 15 +++ CliFx.Tests/CommandFactoryTests.cs | 11 -- .../CommandInitializerTests.Commands.cs | 21 ++++ CliFx.Tests/CommandInitializerTests.cs | 21 +--- .../CommandOptionInputConverterTests.Types.cs | 49 ++++++++ .../CommandOptionInputConverterTests.cs | 45 -------- .../CommandSchemaResolverTests.Commands.cs | 24 ++++ CliFx.Tests/CommandSchemaResolverTests.cs | 24 +--- CliFx.Tests/HelpTextRendererTests.Commands.cs | 42 +++++++ CliFx.Tests/HelpTextRendererTests.cs | 105 ++++++++++++++++++ CliFx.Tests/Internal/Extensions.cs | 13 +++ CliFx/Services/Extensions.cs | 13 --- CliFx/Services/HelpTextRenderer.cs | 2 +- 15 files changed, 300 insertions(+), 137 deletions(-) create mode 100644 CliFx.Tests/CliApplicationTests.Commands.cs create mode 100644 CliFx.Tests/CommandFactoryTests.Commands.cs create mode 100644 CliFx.Tests/CommandInitializerTests.Commands.cs create mode 100644 CliFx.Tests/CommandOptionInputConverterTests.Types.cs create mode 100644 CliFx.Tests/CommandSchemaResolverTests.Commands.cs create mode 100644 CliFx.Tests/HelpTextRendererTests.Commands.cs create mode 100644 CliFx.Tests/HelpTextRendererTests.cs create mode 100644 CliFx.Tests/Internal/Extensions.cs diff --git a/CliFx.Tests/CliApplicationTests.Commands.cs b/CliFx.Tests/CliApplicationTests.Commands.cs new file mode 100644 index 0000000..3661eb5 --- /dev/null +++ b/CliFx.Tests/CliApplicationTests.Commands.cs @@ -0,0 +1,28 @@ +using System.Threading.Tasks; +using CliFx.Attributes; +using CliFx.Exceptions; +using CliFx.Services; + +namespace CliFx.Tests +{ + public partial class CliApplicationTests + { + [Command] + private class TestDefaultCommand : ICommand + { + public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + + [Command("command")] + private class TestNamedCommand : ICommand + { + public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + + [Command("faulty command")] + private class TestFaultyCommand : ICommand + { + public Task ExecuteAsync(IConsole console) => Task.FromException(new CommandException(-1337)); + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/CliApplicationTests.cs b/CliFx.Tests/CliApplicationTests.cs index f06db9b..d3ddb06 100644 --- a/CliFx.Tests/CliApplicationTests.cs +++ b/CliFx.Tests/CliApplicationTests.cs @@ -1,35 +1,11 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using CliFx.Attributes; -using CliFx.Exceptions; -using CliFx.Services; using FluentAssertions; using NUnit.Framework; namespace CliFx.Tests { - public partial class CliApplicationTests - { - [Command] - private class TestDefaultCommand : ICommand - { - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; - } - - [Command("command")] - private class TestNamedCommand : ICommand - { - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; - } - - [Command("faulty command")] - private class TestFaultyCommand : ICommand - { - public Task ExecuteAsync(IConsole console) => Task.FromException(new CommandException(-1337)); - } - } - [TestFixture] public partial class CliApplicationTests { diff --git a/CliFx.Tests/CommandFactoryTests.Commands.cs b/CliFx.Tests/CommandFactoryTests.Commands.cs new file mode 100644 index 0000000..a6fe7a4 --- /dev/null +++ b/CliFx.Tests/CommandFactoryTests.Commands.cs @@ -0,0 +1,15 @@ +using System.Threading.Tasks; +using CliFx.Attributes; +using CliFx.Services; + +namespace CliFx.Tests +{ + public partial class CommandFactoryTests + { + [Command] + private class TestCommand : ICommand + { + public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/CommandFactoryTests.cs b/CliFx.Tests/CommandFactoryTests.cs index 4b5196e..3a520d1 100644 --- a/CliFx.Tests/CommandFactoryTests.cs +++ b/CliFx.Tests/CommandFactoryTests.cs @@ -1,7 +1,5 @@ using System; using System.Collections.Generic; -using System.Threading.Tasks; -using CliFx.Attributes; using CliFx.Models; using CliFx.Services; using FluentAssertions; @@ -9,15 +7,6 @@ using NUnit.Framework; namespace CliFx.Tests { - public partial class CommandFactoryTests - { - [Command] - private class TestCommand : ICommand - { - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; - } - } - [TestFixture] public partial class CommandFactoryTests { diff --git a/CliFx.Tests/CommandInitializerTests.Commands.cs b/CliFx.Tests/CommandInitializerTests.Commands.cs new file mode 100644 index 0000000..a5fc0e4 --- /dev/null +++ b/CliFx.Tests/CommandInitializerTests.Commands.cs @@ -0,0 +1,21 @@ +using System.Threading.Tasks; +using CliFx.Attributes; +using CliFx.Services; + +namespace CliFx.Tests +{ + public partial class CommandInitializerTests + { + [Command] + private class TestCommand : ICommand + { + [CommandOption("int", 'i', IsRequired = true)] + public int IntOption { get; set; } = 24; + + [CommandOption("str", 's')] + public string StringOption { get; set; } = "foo bar"; + + public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/CommandInitializerTests.cs b/CliFx.Tests/CommandInitializerTests.cs index a1129d1..ba22c5f 100644 --- a/CliFx.Tests/CommandInitializerTests.cs +++ b/CliFx.Tests/CommandInitializerTests.cs @@ -1,32 +1,13 @@ using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; -using CliFx.Attributes; using CliFx.Exceptions; using CliFx.Models; using CliFx.Services; +using CliFx.Tests.Internal; using FluentAssertions; using NUnit.Framework; namespace CliFx.Tests { - public partial class CommandInitializerTests - { - [Command] - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] - [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] - private class TestCommand : ICommand - { - [CommandOption("int", 'i', IsRequired = true)] - public int IntOption { get; set; } = 24; - - [CommandOption("str", 's')] - public string StringOption { get; set; } = "foo bar"; - - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; - } - } - [TestFixture] public partial class CommandInitializerTests { diff --git a/CliFx.Tests/CommandOptionInputConverterTests.Types.cs b/CliFx.Tests/CommandOptionInputConverterTests.Types.cs new file mode 100644 index 0000000..66baf53 --- /dev/null +++ b/CliFx.Tests/CommandOptionInputConverterTests.Types.cs @@ -0,0 +1,49 @@ +using System; + +namespace CliFx.Tests +{ + public partial class CommandOptionInputConverterTests + { + private enum TestEnum + { + Value1, + Value2, + Value3 + } + + private class TestStringConstructable + { + public string Value { get; } + + public TestStringConstructable(string value) + { + Value = value; + } + } + + private class TestStringParseable + { + public string Value { get; } + + private TestStringParseable(string value) + { + Value = value; + } + + public static TestStringParseable Parse(string value) => new TestStringParseable(value); + } + + private class TestStringParseableWithFormatProvider + { + public string Value { get; } + + private TestStringParseableWithFormatProvider(string value) + { + Value = value; + } + + public static TestStringParseableWithFormatProvider Parse(string value, IFormatProvider formatProvider) => + new TestStringParseableWithFormatProvider(value + " " + formatProvider); + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/CommandOptionInputConverterTests.cs b/CliFx.Tests/CommandOptionInputConverterTests.cs index 3d488bb..9067806 100644 --- a/CliFx.Tests/CommandOptionInputConverterTests.cs +++ b/CliFx.Tests/CommandOptionInputConverterTests.cs @@ -9,51 +9,6 @@ using NUnit.Framework; namespace CliFx.Tests { - public partial class CommandOptionInputConverterTests - { - private enum TestEnum - { - Value1, - Value2, - Value3 - } - - private class TestStringConstructable - { - public string Value { get; } - - public TestStringConstructable(string value) - { - Value = value; - } - } - - private class TestStringParseable - { - public string Value { get; } - - private TestStringParseable(string value) - { - Value = value; - } - - public static TestStringParseable Parse(string value) => new TestStringParseable(value); - } - - private class TestStringParseableWithFormatProvider - { - public string Value { get; } - - private TestStringParseableWithFormatProvider(string value) - { - Value = value; - } - - public static TestStringParseableWithFormatProvider Parse(string value, IFormatProvider formatProvider) => - new TestStringParseableWithFormatProvider(value + " " + formatProvider); - } - } - [TestFixture] public partial class CommandOptionInputConverterTests { diff --git a/CliFx.Tests/CommandSchemaResolverTests.Commands.cs b/CliFx.Tests/CommandSchemaResolverTests.Commands.cs new file mode 100644 index 0000000..1e039ad --- /dev/null +++ b/CliFx.Tests/CommandSchemaResolverTests.Commands.cs @@ -0,0 +1,24 @@ +using System.Threading.Tasks; +using CliFx.Attributes; +using CliFx.Services; + +namespace CliFx.Tests +{ + public partial class CommandSchemaResolverTests + { + [Command("Command name", Description = "Command description")] + private class TestCommand : ICommand + { + [CommandOption("option-a", 'a')] + public int OptionA { get; set; } + + [CommandOption("option-b", IsRequired = true)] + public string OptionB { get; set; } + + [CommandOption("option-c", Description = "Option C description")] + public bool OptionC { get; set; } + + public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/CommandSchemaResolverTests.cs b/CliFx.Tests/CommandSchemaResolverTests.cs index 9bf5fd2..9153192 100644 --- a/CliFx.Tests/CommandSchemaResolverTests.cs +++ b/CliFx.Tests/CommandSchemaResolverTests.cs @@ -1,35 +1,13 @@ using System; using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; -using CliFx.Attributes; using CliFx.Models; using CliFx.Services; +using CliFx.Tests.Internal; using FluentAssertions; using NUnit.Framework; namespace CliFx.Tests { - public partial class CommandSchemaResolverTests - { - [Command("Command name", Description = "Command description")] - [SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local")] - [SuppressMessage("ReSharper", "MemberCanBePrivate.Local")] - private class TestCommand : ICommand - { - [CommandOption("option-a", 'a')] - public int OptionA { get; set; } - - [CommandOption("option-b", IsRequired = true)] - public string OptionB { get; set; } - - [CommandOption("option-c", Description = "Option C description")] - public bool OptionC { get; set; } - - public Task ExecuteAsync(IConsole console) => Task.CompletedTask; - } - } - [TestFixture] public partial class CommandSchemaResolverTests { diff --git a/CliFx.Tests/HelpTextRendererTests.Commands.cs b/CliFx.Tests/HelpTextRendererTests.Commands.cs new file mode 100644 index 0000000..7d754a6 --- /dev/null +++ b/CliFx.Tests/HelpTextRendererTests.Commands.cs @@ -0,0 +1,42 @@ +using System.Threading.Tasks; +using CliFx.Attributes; +using CliFx.Services; + +namespace CliFx.Tests +{ + public partial class HelpTextRendererTests + { + [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 Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + + [Command("cmd", Description = "NamedCommand description.")] + private class NamedCommand : ICommand + { + [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 Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + + [Command("cmd sub", Description = "NamedSubCommand description.")] + private class NamedSubCommand : ICommand + { + [CommandOption("option-e", 'e', Description = "OptionE description.")] + public string OptionE { get; set; } + + public Task ExecuteAsync(IConsole console) => Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/HelpTextRendererTests.cs b/CliFx.Tests/HelpTextRendererTests.cs new file mode 100644 index 0000000..efaff6f --- /dev/null +++ b/CliFx.Tests/HelpTextRendererTests.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using CliFx.Models; +using CliFx.Services; +using FluentAssertions; +using NUnit.Framework; + +namespace CliFx.Tests +{ + [TestFixture] + public partial class HelpTextRendererTests + { + private static HelpTextSource CreateHelpTextSource(IReadOnlyList availableCommandTypes, Type targetCommandType) + { + var commandSchemaResolver = new CommandSchemaResolver(); + + var applicationMetadata = new ApplicationMetadata("TestApp", "testapp", "1.0", null); + var availableCommandSchemas = commandSchemaResolver.GetCommandSchemas(availableCommandTypes); + var targetCommandSchema = availableCommandSchemas.Single(s => s.Type == targetCommandType); + + return new HelpTextSource(applicationMetadata, availableCommandSchemas, targetCommandSchema); + } + + private static IEnumerable GetTestCases_RenderHelpText() + { + yield return new TestCaseData( + CreateHelpTextSource( + new[] {typeof(DefaultCommand), typeof(NamedCommand), typeof(NamedSubCommand)}, + typeof(DefaultCommand)), + + new[] + { + "Usage", + "[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." + } + ); + + yield return new TestCaseData( + CreateHelpTextSource( + new[] {typeof(DefaultCommand), typeof(NamedCommand), typeof(NamedSubCommand)}, + typeof(NamedCommand)), + + new[] + { + "Description", + "NamedCommand description.", + "Usage", + "cmd", "[command]", "[options]", + "Options", + "-c|--option-c", "OptionC description.", + "-d|--option-d", "OptionD description.", + "-h|--help", "Shows help text.", + "Commands", + "sub", "NamedSubCommand description.", + "You can run", "to show help on a specific command." + } + ); + + yield return new TestCaseData( + CreateHelpTextSource( + new[] {typeof(DefaultCommand), typeof(NamedCommand), typeof(NamedSubCommand)}, + typeof(NamedSubCommand)), + + new[] + { + "Description", + "NamedSubCommand description.", + "Usage", + "cmd sub", "[options]", + "Options", + "-e|--option-e", "OptionE description.", + "-h|--help", "Shows help text." + } + ); + } + + [Test] + [TestCaseSource(nameof(GetTestCases_RenderHelpText))] + public void RenderHelpText_Test(HelpTextSource source, IReadOnlyList substrings) + { + // Arrange + using (var stdout = new StringWriter()) + { + var renderer = new HelpTextRenderer(); + var console = new VirtualConsole(stdout); + + // Act + renderer.RenderHelpText(console, source); + + // Assert + stdout.ToString().Should().ContainAll(substrings); + } + } + } +} \ No newline at end of file diff --git a/CliFx.Tests/Internal/Extensions.cs b/CliFx.Tests/Internal/Extensions.cs new file mode 100644 index 0000000..1cad8ce --- /dev/null +++ b/CliFx.Tests/Internal/Extensions.cs @@ -0,0 +1,13 @@ +using System; +using System.Linq; +using CliFx.Models; +using CliFx.Services; + +namespace CliFx.Tests.Internal +{ + internal static class Extensions + { + public static CommandSchema GetCommandSchema(this ICommandSchemaResolver resolver, Type commandType) => + resolver.GetCommandSchemas(new[] {commandType}).Single(); + } +} \ No newline at end of file diff --git a/CliFx/Services/Extensions.cs b/CliFx/Services/Extensions.cs index 5eef06d..078b538 100644 --- a/CliFx/Services/Extensions.cs +++ b/CliFx/Services/Extensions.cs @@ -1,7 +1,5 @@ using System; -using System.Linq; using CliFx.Internal; -using CliFx.Models; namespace CliFx.Services { @@ -10,17 +8,6 @@ namespace CliFx.Services /// public static class Extensions { - /// - /// Resolves command schema for specified command type. - /// - public static CommandSchema GetCommandSchema(this ICommandSchemaResolver resolver, Type commandType) - { - resolver.GuardNotNull(nameof(resolver)); - commandType.GuardNotNull(nameof(commandType)); - - return resolver.GetCommandSchemas(new[] {commandType}).Single(); - } - /// /// Sets console foreground color, executes specified action, and sets the color back to the original value. /// diff --git a/CliFx/Services/HelpTextRenderer.cs b/CliFx/Services/HelpTextRenderer.cs index da6d6c9..864a410 100644 --- a/CliFx/Services/HelpTextRenderer.cs +++ b/CliFx/Services/HelpTextRenderer.cs @@ -98,7 +98,7 @@ namespace CliFx.Services if (!source.TargetCommandSchema.IsDefault()) return; - // Title + // Title and version RenderWithColor(source.ApplicationMetadata.Title, ConsoleColor.Yellow); Render(" "); RenderWithColor(source.ApplicationMetadata.VersionText, ConsoleColor.Yellow);