Skip to content

Commit 1e494b8

Browse files
Merge pull request #2369 from JeanJoeris/jj/refactor-error-subsystem
Error Reporting Subsystem
2 parents 39c05a3 + 78aa0b4 commit 1e494b8

File tree

7 files changed

+122
-6
lines changed

7 files changed

+122
-6
lines changed

src/System.CommandLine.Tests/ParseErrorReportingTests.cs renamed to src/System.CommandLine.Subsystems.Tests/ErrorReportingFunctionalTests.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
/*
45
using System.CommandLine.Help;
56
using System.CommandLine.Invocation;
7+
*/
68
using System.IO;
79
using FluentAssertions;
810
using Xunit;
@@ -12,8 +14,10 @@
1214

1315
namespace System.CommandLine.Tests;
1416

15-
public class ParseErrorReportingTests
17+
public class ErrorReportingFunctionalTests
1618
{
19+
// TODO: these tests depend on help output
20+
/*
1721
[Fact] // https://github.com/dotnet/command-line-api/issues/817
1822
public void Parse_error_reporting_reports_error_when_help_is_used_and_required_subcommand_is_missing()
1923
{
@@ -126,4 +130,5 @@ public void When_no_help_option_is_present_then_help_is_not_shown_for_parse_erro
126130
127131
output.ToString().Should().NotShowHelp();
128132
}
129-
}
133+
*/
134+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using FluentAssertions;
5+
using Xunit;
6+
using System.CommandLine.Parsing;
7+
8+
namespace System.CommandLine.Subsystems.Tests;
9+
10+
public class ErrorReportingSubsystemTests
11+
{
12+
[Fact]
13+
public void Report_when_single_error_writes_to_console_hack()
14+
{
15+
var error = new ParseError("a sweet error message");
16+
var errors = new List<ParseError> { error };
17+
var errorSubsystem = new ErrorReportingSubsystem();
18+
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
19+
20+
errorSubsystem.Report(consoleHack, errors);
21+
22+
consoleHack.GetBuffer().Trim().Should().Be(error.Message);
23+
}
24+
25+
[Fact]
26+
public void Report_when_multiple_error_writes_to_console_hack()
27+
{
28+
var error = new ParseError("a sweet error message");
29+
var anotherError = new ParseError("another sweet error message");
30+
var errors = new List<ParseError> { error, anotherError };
31+
var errorSubsystem = new ErrorReportingSubsystem();
32+
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
33+
34+
errorSubsystem.Report(consoleHack, errors);
35+
36+
consoleHack.GetBuffer().Trim().Should().Be($"{error.Message}{Environment.NewLine}{anotherError.Message}");
37+
}
38+
39+
[Fact]
40+
public void Report_when_no_errors_writes_nothing_to_console_hack()
41+
{
42+
var errors = new List<ParseError> { };
43+
var errorSubsystem = new ErrorReportingSubsystem();
44+
var consoleHack = new ConsoleHack().RedirectToBuffer(true);
45+
46+
errorSubsystem.Report(consoleHack, errors);
47+
48+
consoleHack.GetBuffer().Trim().Should().Be("");
49+
}
50+
51+
[Theory]
52+
[InlineData("-x")]
53+
[InlineData("-non_existant_option")]
54+
public void GetIsActivated_GivenInvalidInput_SubsystemIsActive(string input)
55+
{
56+
var rootCommand = new CliRootCommand {new CliOption<bool>("-v")};
57+
var configuration = new CliConfiguration(rootCommand);
58+
var errorSubsystem = new ErrorReportingSubsystem();
59+
IReadOnlyList<string> args = [""];
60+
Subsystem.Initialize(errorSubsystem, configuration, args);
61+
62+
var parseResult = CliParser.Parse(rootCommand, input, configuration);
63+
var isActive = Subsystem.GetIsActivated(errorSubsystem, parseResult);
64+
65+
isActive.Should().BeTrue();
66+
}
67+
68+
[Theory]
69+
[InlineData("-v")]
70+
[InlineData("")]
71+
public void GetIsActivated_GivenValidInput_SubsystemShouldNotBeActive(string input)
72+
{
73+
var rootCommand = new CliRootCommand { new CliOption<bool>("-v") };
74+
var configuration = new CliConfiguration(rootCommand);
75+
var errorSubsystem = new ErrorReportingSubsystem();
76+
IReadOnlyList<string> args = [""];
77+
Subsystem.Initialize(errorSubsystem, configuration, args);
78+
79+
var parseResult = CliParser.Parse(rootCommand, input, configuration);
80+
var isActive = Subsystem.GetIsActivated(errorSubsystem, parseResult);
81+
82+
isActive.Should().BeFalse();
83+
}
84+
}

src/System.CommandLine.Subsystems.Tests/System.CommandLine.Subsystems.Tests.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
<Compile Include="TestData.cs" />
4040
<Compile Include="VersionFunctionalTests.cs" />
4141
<Compile Include="VersionSubsystemTests.cs" />
42+
<Compile Include="ErrorReportingFunctionalTests.cs" />
43+
<Compile Include="ErrorReportingSubsystemTests.cs" />
4244
</ItemGroup>
4345

4446
<ItemGroup>

src/System.CommandLine.Subsystems/ConsoleHack.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public class ConsoleHack
1010
private readonly StringBuilder buffer = new();
1111
private bool redirecting = false;
1212

13-
public void WriteLine(string text)
13+
public void WriteLine(string text = "")
1414
{
1515
if (redirecting)
1616
{
Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,49 @@
11
// Copyright (c) .NET Foundation and contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.CommandLine.Parsing;
45
using System.CommandLine.Subsystems;
56
using System.CommandLine.Subsystems.Annotations;
67

78
namespace System.CommandLine;
89

10+
/// <summary>
11+
/// Subsystem for reporting errors
12+
/// </summary>
13+
/// <remarks>
14+
/// This class, including interface, is likey to change as powderhouse continues
15+
/// </remarks>
916
public class ErrorReportingSubsystem : CliSubsystem
1017
{
1118
public ErrorReportingSubsystem(IAnnotationProvider? annotationProvider = null)
1219
: base(ErrorReportingAnnotations.Prefix, SubsystemKind.ErrorReporting, annotationProvider)
1320
{ }
1421

15-
// TODO: Stash option rather than using string
1622
protected internal override bool GetIsActivated(ParseResult? parseResult)
1723
=> parseResult is not null && parseResult.Errors.Any();
1824

25+
// TODO: properly test execute directly when parse result is usable in tests
1926
protected internal override CliExit Execute(PipelineContext pipelineContext)
2027
{
21-
pipelineContext.ConsoleHack.WriteLine("You have errors!");
28+
var _ = pipelineContext.ParseResult
29+
?? throw new ArgumentException("The parse result has not been set", nameof(pipelineContext));
30+
31+
Report(pipelineContext.ConsoleHack, pipelineContext.ParseResult.Errors);
32+
2233
return CliExit.SuccessfullyHandled(pipelineContext.ParseResult);
2334
}
35+
36+
public void Report(ConsoleHack consoleHack, IReadOnlyList<ParseError> errors)
37+
{
38+
ConsoleHelpers.ResetTerminalForegroundColor();
39+
ConsoleHelpers.SetTerminalForegroundRed();
40+
41+
foreach (var error in errors)
42+
{
43+
consoleHack.WriteLine(error.Message);
44+
}
45+
consoleHack.WriteLine();
46+
47+
ConsoleHelpers.ResetTerminalForegroundColor();
48+
}
2449
}

src/System.CommandLine/Parsing/ParseError.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public sealed class ParseError
1010
{
1111
// TODO: add position
1212
// TODO: reevaluate whether we should be exposing a SymbolResult here
13-
internal ParseError(
13+
public ParseError(
1414
string message,
1515
SymbolResult? symbolResult = null)
1616
{

0 commit comments

Comments
 (0)