Skip to content

Commit

Permalink
Merge pull request #2351 from KathleenDollard/powderhouse-subsystem-f…
Browse files Browse the repository at this point in the history
…ixes-and-directives

Powderhouse directives
  • Loading branch information
KathleenDollard committed Apr 4, 2024
2 parents 5ae588a + 06fa079 commit 84d40f3
Show file tree
Hide file tree
Showing 34 changed files with 1,167 additions and 458 deletions.
14 changes: 12 additions & 2 deletions src/System.CommandLine.Subsystems.Tests/AlternateSubsystems.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CommandLine.Directives;
using System.CommandLine.Subsystems;
using System.CommandLine.Subsystems.Annotations;

namespace System.CommandLine.Subsystems.Tests
{
Expand Down Expand Up @@ -45,11 +47,11 @@ internal class VersionWithInitializeAndTeardown : VersionSubsystem
internal bool ExecutionWasRun;
internal bool TeardownWasRun;

protected override CliConfiguration Initialize(CliConfiguration configuration)
protected override CliConfiguration Initialize(InitializationContext context)
{
// marker hack needed because ConsoleHack not available in initialization
InitializationWasRun = true;
return base.Initialize(configuration);
return base.Initialize(context);
}

protected override CliExit Execute(PipelineContext pipelineContext)
Expand All @@ -65,5 +67,13 @@ protected override CliExit TearDown(CliExit cliExit)
}
}

internal class StringDirectiveSubsystem(IAnnotationProvider? annotationProvider = null)
: DirectiveSubsystem("other",SubsystemKind.Other, annotationProvider)
{ }

internal class BooleanDirectiveSubsystem(IAnnotationProvider? annotationProvider = null)
: DirectiveSubsystem("diagram", SubsystemKind.Other, annotationProvider)
{ }

}
}
45 changes: 45 additions & 0 deletions src/System.CommandLine.Subsystems.Tests/DiagramSubsystemTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using FluentAssertions;
using System.CommandLine.Directives;
using System.CommandLine.Parsing;
using Xunit;

namespace System.CommandLine.Subsystems.Tests;

public class DiagramSubsystemTests
{

[Theory]
[ClassData(typeof(TestData.Diagram))]
public void Diagram_is_activated_only_when_requested(string input, bool expectedIsActive)
{
CliRootCommand rootCommand = [new CliCommand("x")];
var configuration = new CliConfiguration(rootCommand);
var subsystem = new DiagramSubsystem();
var args = CliParser.SplitCommandLine(input).ToList().AsReadOnly();

Subsystem.Initialize(subsystem, configuration, args);
var parseResult = CliParser.Parse(rootCommand, input, configuration);
var isActive = Subsystem.GetIsActivated(subsystem, parseResult);

isActive.Should().Be(expectedIsActive);
}

[Theory]
[ClassData(typeof(TestData.Diagram))]
public void String_directive_supplies_string_or_default_and_is_activated_only_when_requested(string input, bool expectedIsActive)
{
CliRootCommand rootCommand = [new CliCommand("x")];
var configuration = new CliConfiguration(rootCommand);
var subsystem = new DiagramSubsystem();
var args = CliParser.SplitCommandLine(input).ToList().AsReadOnly();

Subsystem.Initialize(subsystem, configuration, args);
var parseResult = CliParser.Parse(rootCommand, input, configuration);
var isActive = Subsystem.GetIsActivated(subsystem, parseResult);

isActive.Should().Be(expectedIsActive);
}
}
41 changes: 41 additions & 0 deletions src/System.CommandLine.Subsystems.Tests/DirectiveSubsystemTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using FluentAssertions;
using System.CommandLine.Directives;
using System.CommandLine.Parsing;
using Xunit;

namespace System.CommandLine.Subsystems.Tests;

public class DirectiveSubsystemTests
{

// For Boolean tests see DiagramSubsystemTests

[Theory]
[ClassData(typeof(TestData.Directive))]
// TODO: Not sure why these tests are passing
public void String_directive_supplies_string_or_default_and_is_activated_only_when_requested(
string input, bool expectedBoolIsActive, bool expectedStringIsActive, string? expectedValue)
{
CliRootCommand rootCommand = [new CliCommand("x")];
var configuration = new CliConfiguration(rootCommand);
var stringSubsystem = new AlternateSubsystems.StringDirectiveSubsystem();
var boolSubsystem = new AlternateSubsystems.BooleanDirectiveSubsystem();
var args = CliParser.SplitCommandLine(input).ToList().AsReadOnly();

Subsystem.Initialize(stringSubsystem, configuration, args);
Subsystem.Initialize(boolSubsystem, configuration, args);

var parseResult = CliParser.Parse(rootCommand, input, configuration);
var stringIsActive = Subsystem.GetIsActivated(stringSubsystem, parseResult);
var boolIsActive = Subsystem.GetIsActivated(boolSubsystem, parseResult);
var actualValue = stringSubsystem.Value;

boolIsActive.Should().Be(expectedBoolIsActive);
stringIsActive.Should().Be(expectedStringIsActive);
actualValue.Should().Be(expectedValue);

}
}
165 changes: 86 additions & 79 deletions src/System.CommandLine.Subsystems.Tests/PipelineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,87 +3,78 @@

using FluentAssertions;
using System.CommandLine.Parsing;
using System.Reflection;
using Xunit;

namespace System.CommandLine.Subsystems.Tests
{
public class PipelineTests
{

private static readonly string? version = (Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly())
?.GetCustomAttribute<AssemblyInformationalVersionAttribute>()
?.InformationalVersion;

private static Pipeline GetTestPipeline(VersionSubsystem versionSubsystem)
=> new()
{
Version = versionSubsystem
};
private static CliConfiguration GetNewTestConfiguration()
=> new(new CliRootCommand { new CliOption<bool>("-x") }); // Add option expected by test data

private static ConsoleHack GetNewTestConsole()
=> new ConsoleHack().RedirectToBuffer(true);

//private static (Pipeline pipeline, CliConfiguration configuration, ConsoleHack consoleHack) StandardObjects(VersionSubsystem versionSubsystem)
//{
// var configuration = new CliConfiguration(new CliRootCommand { new CliOption<bool>("-x") });
// var pipeline = new Pipeline
// {
// Version = versionSubsystem
// };
// var consoleHack = new ConsoleHack().RedirectToBuffer(true);
// return (pipeline, configuration, consoleHack);
//}

[Theory]
[InlineData("-v", true)]
[InlineData("--version", true)]
[InlineData("-x", false)]
[InlineData("", false)]
[InlineData(null, false)]
[ClassData(typeof(TestData.Version))]
public void Subsystem_runs_in_pipeline_only_when_requested(string input, bool shouldRun)
{
var configuration = new CliConfiguration(new CliRootCommand { });
var pipeline = new Pipeline
{
Version = new VersionSubsystem()
};
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
var pipeline = GetTestPipeline(new VersionSubsystem());
var console = GetNewTestConsole();

var exit = pipeline.Execute(configuration, input, consoleHack);
var exit = pipeline.Execute(GetNewTestConfiguration(), input, console);

exit.ExitCode.Should().Be(0);
exit.Handled.Should().Be(shouldRun);
if (shouldRun)
{
consoleHack.GetBuffer().Trim().Should().Be(version);
console.GetBuffer().Trim().Should().Be(TestData.AssemblyVersionString);
}
}

[Theory]
[InlineData("-v", true)]
[InlineData("--version", true)]
[InlineData("-x", false)]
[InlineData("", false)]
[InlineData(null, false)]
[ClassData(typeof(TestData.Version))]
public void Subsystem_runs_with_explicit_parse_only_when_requested(string input, bool shouldRun)
{
var configuration = new CliConfiguration(new CliRootCommand { });
var pipeline = new Pipeline
{
Version = new VersionSubsystem()
};
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
var pipeline = GetTestPipeline(new VersionSubsystem());
var console = GetNewTestConsole();

var result = pipeline.Parse(configuration, input);
var exit = pipeline.Execute(result, input, consoleHack);
var result = pipeline.Parse(GetNewTestConfiguration(), input);
var exit = pipeline.Execute(result, input, console);

exit.ExitCode.Should().Be(0);
exit.Handled.Should().Be(shouldRun);
if (shouldRun)
{
consoleHack.GetBuffer().Trim().Should().Be(version);
console.GetBuffer().Trim().Should().Be(TestData.AssemblyVersionString);
}
}

[Theory]
[InlineData("-v", true)]
[InlineData("--version", true)]
[InlineData("-x", false)]
[InlineData("", false)]
[InlineData(null, false)]
[ClassData(typeof(TestData.Version))]
public void Subsystem_runs_initialize_and_teardown_when_requested(string input, bool shouldRun)
{
var configuration = new CliConfiguration(new CliRootCommand { });
AlternateSubsystems.VersionWithInitializeAndTeardown versionSubsystem = new AlternateSubsystems.VersionWithInitializeAndTeardown();
var pipeline = new Pipeline
{
Version = versionSubsystem
};
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
var versionSubsystem = new AlternateSubsystems.VersionWithInitializeAndTeardown();
var pipeline = GetTestPipeline(versionSubsystem);
var console = GetNewTestConsole();

var exit = pipeline.Execute(configuration, input, consoleHack);
var exit = pipeline.Execute(GetNewTestConfiguration(), input, console);

exit.ExitCode.Should().Be(0);
exit.Handled.Should().Be(shouldRun);
Expand All @@ -94,56 +85,72 @@ public void Subsystem_runs_initialize_and_teardown_when_requested(string input,


[Theory]
[InlineData("-v", true)]
[InlineData("--version", true)]
[InlineData("-x", false)]
[InlineData("", false)]
[InlineData(null, false)]
public void Subsystem_can_be_used_without_runner(string input, bool shouldRun)
[ClassData(typeof(TestData.Version))]
public void Subsystem_works_without_pipeline(string input, bool shouldRun)
{
var configuration = new CliConfiguration(new CliRootCommand { });
var versionSubsystem = new VersionSubsystem();
var consoleHack = new ConsoleHack().RedirectToBuffer(true);

Subsystem.Initialize(versionSubsystem, configuration);
// TODO: I do not know why anyone would do this, but I do not see a reason to work to block it. See style2 below
var parseResult = CliParser.Parse(configuration.RootCommand, input, configuration);
// TODO: Ensure an efficient conversion as people may copy this code
var args = CliParser.SplitCommandLine(input).ToList().AsReadOnly();
var console = GetNewTestConsole();
var configuration = GetNewTestConfiguration();

Subsystem.Initialize(versionSubsystem, configuration, args);
// This approach might be taken if someone is using a subsystem just for initialization
var parseResult = CliParser.Parse(configuration.RootCommand, args, configuration);
bool value = parseResult.GetValue<bool>("--version");

parseResult.Errors.Should().BeEmpty();
value.Should().Be(shouldRun);
if (shouldRun)
if (shouldRun)
{
// TODO: Add an execute overload to avoid checking activated twice
var exit = Subsystem.Execute(versionSubsystem, parseResult, input, consoleHack);
var exit = Subsystem.Execute(versionSubsystem, parseResult, input, console);
exit.Should().NotBeNull();
exit.ExitCode.Should().Be(0);
exit.Handled.Should().BeTrue();
consoleHack.GetBuffer().Trim().Should().Be(version);
console.GetBuffer().Trim().Should().Be(TestData.AssemblyVersionString);
}
}

[Theory]
[InlineData("-v", true)]
[InlineData("--version", true)]
[InlineData("-x", false)]
[InlineData("", false)]
[InlineData(null, false)]
public void Subsystem_can_be_used_without_runner_style2(string input, bool shouldRun)
[ClassData(typeof(TestData.Version))]
public void Subsystem_works_without_pipeline_style2(string input, bool shouldRun)
{
var configuration = new CliConfiguration(new CliRootCommand { });
var versionSubsystem = new VersionSubsystem();
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
var args = CliParser.SplitCommandLine(input).ToList().AsReadOnly();
var console = GetNewTestConsole();
var configuration = GetNewTestConfiguration();
var expectedVersion = shouldRun
? version
? TestData.AssemblyVersionString
: "";

Subsystem.Initialize(versionSubsystem, configuration);
var parseResult = CliParser.Parse(configuration.RootCommand, input, configuration);
var exit = Subsystem.ExecuteIfNeeded(versionSubsystem, parseResult, input, consoleHack);
// Someone might use this approach if they wanted to do something with the ParseResult
Subsystem.Initialize(versionSubsystem, configuration, args);
var parseResult = CliParser.Parse(configuration.RootCommand, args, configuration);
var exit = Subsystem.ExecuteIfNeeded(versionSubsystem, parseResult, input, console);

exit.ExitCode.Should().Be(0);
exit.Handled.Should().Be(shouldRun);
consoleHack.GetBuffer().Trim().Should().Be(expectedVersion);
console.GetBuffer().Trim().Should().Be(expectedVersion);
}


[Theory]
[InlineData("-xy", false)]
[InlineData("--versionx", false)]
public void Subsystem_runs_when_requested_even_when_there_are_errors(string input, bool shouldRun)
{
var versionSubsystem = new VersionSubsystem();
var args = CliParser.SplitCommandLine(input).ToList().AsReadOnly();
var configuration = GetNewTestConfiguration();

Subsystem.Initialize(versionSubsystem, configuration, args);
// This approach might be taken if someone is using a subsystem just for initialization
var parseResult = CliParser.Parse(configuration.RootCommand, args, configuration);
bool value = parseResult.GetValue<bool>("--version");

parseResult.Errors.Should().NotBeEmpty();
value.Should().Be(shouldRun);
}

[Fact]
Expand Down Expand Up @@ -171,9 +178,8 @@ public void Normal_pipeline_contains_no_subsystems()
public void Subsystems_can_access_each_others_data()
{
// TODO: Explore a mechanism that doesn't require the reference to retrieve data, this shows that it is awkward
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
var symbol = new CliOption<bool>("-x");

var console = GetNewTestConsole();
var pipeline = new StandardPipeline
{
Version = new AlternateSubsystems.VersionThatUsesHelpData(symbol)
Expand All @@ -183,9 +189,10 @@ public void Subsystems_can_access_each_others_data()
{
symbol.With(pipeline.Help.Description, "Testing")
};
pipeline.Execute(new CliConfiguration(rootCommand), "-v", consoleHack);
consoleHack.GetBuffer().Trim().Should().Be($"Testing");
}

pipeline.Execute(new CliConfiguration(rootCommand), "-v", console);

console.GetBuffer().Trim().Should().Be($"Testing");
}
}
}
Loading

0 comments on commit 84d40f3

Please sign in to comment.