mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
566dd4a9a7 | ||
|
|
9beb439323 | ||
|
|
029257a915 | ||
|
|
d330fbbb63 | ||
|
|
236867f547 | ||
|
|
b41e9b4929 | ||
|
|
ff06b8896f | ||
|
|
0fe9c89fa0 | ||
|
|
8646c9de5e | ||
|
|
a33c42a163 | ||
|
|
55cea48cbd | ||
|
|
e67eda3515 | ||
|
|
4412c20e97 | ||
|
|
9eb84c6649 | ||
|
|
2ef37ab6d9 | ||
|
|
38a73772fc | ||
|
|
aed53eb090 | ||
|
|
21b601da66 | ||
|
|
a4726fcefd | ||
|
|
ab24ca8604 | ||
|
|
3533bff344 | ||
|
|
1b096b679e | ||
|
|
cb61b31e9d | ||
|
|
d8f183c404 | ||
|
|
c95b6c32d5 | ||
|
|
d2e390c691 | ||
|
|
66ef221586 | ||
|
|
2d3bb30125 | ||
|
|
5d72692aa5 | ||
|
|
3be17db784 | ||
|
|
4aef8ce8fb | ||
|
|
8c1cff3bb7 | ||
|
|
669d8bfe20 | ||
|
|
4dce7bddb4 | ||
|
|
a621e89e89 | ||
|
|
5ea11e3a23 | ||
|
|
7cb61182d2 |
67
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
67
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -1,42 +1,75 @@
|
||||
name: 🐞 Bug report
|
||||
name: 🐛 Bug report
|
||||
description: Report broken functionality.
|
||||
labels: [bug]
|
||||
|
||||
body:
|
||||
- type: markdown
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
🧐 **Guidelines:**
|
||||
- Avoid generic or vague titles such as "Something's not working" or "A couple of problems" — be as descriptive as possible.
|
||||
- Keep your issue focused on one single problem. If you have multiple bug reports, please create a separate issue for each of them.
|
||||
- Issues should represent **complete and actionable** work items. If you are unsure about something or have a question, please start a [discussion](https://github.com/Tyrrrz/CliFx/discussions/new) instead.
|
||||
- Remember that **CliFx** is an open-source project funded by the community. If you find it useful, **please consider [donating](https://tyrrrz.me/donate) to support its development**.
|
||||
|
||||
- Search through [existing issues](https://github.com/Tyrrrz/CliFx/issues?q=is%3Aissue) first to ensure that this bug has not been reported before.
|
||||
- Write a descriptive title for your issue. Avoid generic or vague titles such as "Something's not working" or "A couple of problems".
|
||||
- Keep your issue focused on one single problem. If you have multiple bug reports, please create separate issues for each of them.
|
||||
- Provide as much context as possible in the details section. Include screenshots, screen recordings, links, references, or anything else you may consider relevant.
|
||||
- If you want to ask a question instead of reporting a bug, please use [discussions](https://github.com/Tyrrrz/CliFx/discussions/new) instead.
|
||||
___
|
||||
|
||||
- type: input
|
||||
- type: input
|
||||
attributes:
|
||||
label: Version
|
||||
description: Which version of CliFx does this bug affect?
|
||||
placeholder: ver X.Y.Z
|
||||
description: Which version of the package does this bug affect? Make sure you're not using an outdated version.
|
||||
placeholder: v1.0.0
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
- type: input
|
||||
attributes:
|
||||
label: Details
|
||||
description: Clear and thorough explanation of the bug.
|
||||
placeholder: I was doing X expecting Y to happen, but Z happened instead.
|
||||
label: Platform
|
||||
description: Which platform do you experience this bug on?
|
||||
placeholder: .NET 7.0 / Windows 11
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to reproduce
|
||||
description: Minimum steps required to reproduce the bug.
|
||||
description: >
|
||||
Minimum steps required to reproduce the bug, including prerequisites, code snippets, or other relevant items.
|
||||
The information provided in this field must be readily actionable, meaning that anyone should be able to reproduce the bug by following these steps.
|
||||
If the reproduction steps are too complex to fit in this field, please provide a link to a repository instead.
|
||||
placeholder: |
|
||||
- Step 1
|
||||
- Step 2
|
||||
- Step 3
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Details
|
||||
description: Clear and thorough explanation of the bug, including any additional information you may find relevant.
|
||||
placeholder: |
|
||||
- Expected behavior: ...
|
||||
- Actual behavior: ...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Checklist
|
||||
description: Quick list of checks to ensure that everything is in order.
|
||||
options:
|
||||
- label: I have looked through existing issues to make sure that this bug has not been reported before
|
||||
required: true
|
||||
- label: I have provided a descriptive title for this issue
|
||||
required: true
|
||||
- label: I have made sure that that this bug is reproducible on the latest version of the package
|
||||
required: true
|
||||
- label: I have provided all the information needed to reproduce this bug as efficiently as possible
|
||||
required: true
|
||||
- label: I have sponsored this project
|
||||
required: false
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
If you are struggling to provide actionable reproduction steps, or if something else is preventing you from creating a complete bug report, please start a [discussion](https://github.com/Tyrrrz/CliFx/discussions/new) instead.
|
||||
|
||||
6
.github/ISSUE_TEMPLATE/config.yml
vendored
6
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -3,9 +3,9 @@ contact_links:
|
||||
- name: ⚠ Feature request
|
||||
url: https://github.com/Tyrrrz/.github/blob/master/docs/project-status.md
|
||||
about: Sorry, but this project is in maintenance mode and no longer accepts new feature requests.
|
||||
- name: 💬 Discord server
|
||||
url: https://discord.gg/2SUWKFnHSm
|
||||
about: Chat with the project community.
|
||||
- name: 🗨 Discussions
|
||||
url: https://github.com/Tyrrrz/CliFx/discussions/new
|
||||
about: Ask and answer questions.
|
||||
- name: 💬 Discord server
|
||||
url: https://discord.gg/2SUWKFnHSm
|
||||
about: Chat with the project community.
|
||||
|
||||
20
.github/workflows/main.yml
vendored
20
.github/workflows/main.yml
vendored
@@ -1,12 +1,28 @@
|
||||
name: main
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
package-version:
|
||||
type: string
|
||||
description: Package version
|
||||
required: false
|
||||
deploy:
|
||||
type: boolean
|
||||
description: Deploy package
|
||||
required: false
|
||||
default: false
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
main:
|
||||
uses: Tyrrrz/.github/.github/workflows/nuget.yml@master
|
||||
with:
|
||||
dotnet-version: 7.0.x
|
||||
dotnet-version: 8.0.x
|
||||
package-version: ${{ inputs.package-version }}
|
||||
# Deploy only on tags by default, unless deploy is explicitly requested
|
||||
deploy-on-tags-only: ${{ !(github.event_name == 'workflow_dispatch' && inputs.deploy) }}
|
||||
secrets:
|
||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
|
||||
|
||||
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,21 +1,12 @@
|
||||
# User-specific files
|
||||
.vs/
|
||||
.idea/
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
.idea/
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
[Xx]64/
|
||||
[Xx]86/
|
||||
[Bb]uild/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
bin/
|
||||
obj/
|
||||
|
||||
# Coverage
|
||||
*.opencover.xml
|
||||
# Test results
|
||||
TestResults/
|
||||
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## v2.3.5 (16-Nov-2023)
|
||||
|
||||
- Fixed an issue where calling `CliApplication.RunAsync(IReadOnlyList<string>)` could fail in very specific scenarios on Windows, if there were two global environment variables with the same name but different casing. (Thanks [@alirezanet](https://github.com/alirezanet))
|
||||
|
||||
## v2.3.4 (18-May-2023)
|
||||
|
||||
- Added an overload of `CliApplicationBuilder.UseTypeActivator(...)` that accepts a `Func<IReadOnlyList<Type>, IServiceProvider>` delegate. The first parameter in the delegate is the list of all command types registered in the application. You can use this overload to more easily add the commands to a DI container. See the readme for an [updated example](https://github.com/Tyrrrz/CliFx/tree/2.3.4#type-activation).
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -9,14 +9,15 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.2" />
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="all" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.5" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -12,9 +12,8 @@ public class CommandMustBeAnnotatedAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_a_command_is_not_annotated_with_the_command_attribute()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
@@ -29,9 +28,8 @@ public class CommandMustBeAnnotatedAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_command_is_annotated_with_the_command_attribute()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public abstract class MyCommand : ICommand
|
||||
{
|
||||
@@ -47,9 +45,8 @@ public class CommandMustBeAnnotatedAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_command_is_implemented_as_an_abstract_class()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public abstract class MyCommand : ICommand
|
||||
{
|
||||
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||
@@ -64,9 +61,8 @@ public class CommandMustBeAnnotatedAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_class_that_is_not_a_command()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class Foo
|
||||
{
|
||||
public int Bar { get; init; } = 5;
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class CommandMustImplementInterfaceAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustImplementInterfaceAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new CommandMustImplementInterfaceAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_command_does_not_implement_ICommand_interface()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand
|
||||
{
|
||||
@@ -30,9 +30,8 @@ public class CommandMustImplementInterfaceAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_command_implements_ICommand_interface()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -48,9 +47,8 @@ public class CommandMustImplementInterfaceAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_class_that_is_not_a_command()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class Foo
|
||||
{
|
||||
public int Bar { get; init; } = 5;
|
||||
|
||||
@@ -16,7 +16,7 @@ public class GeneralSpecs
|
||||
.Assembly
|
||||
.GetTypes()
|
||||
.Where(t => !t.IsAbstract && t.IsAssignableTo(typeof(DiagnosticAnalyzer)))
|
||||
.Select(t => (DiagnosticAnalyzer) Activator.CreateInstance(t)!)
|
||||
.Select(t => (DiagnosticAnalyzer)Activator.CreateInstance(t)!)
|
||||
.ToArray();
|
||||
|
||||
// Act
|
||||
|
||||
@@ -12,9 +12,8 @@ public class OptionMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_an_option_is_inside_a_class_that_is_not_a_command()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyClass
|
||||
{
|
||||
[CommandOption("foo")]
|
||||
@@ -30,9 +29,8 @@ public class OptionMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_is_inside_a_command()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -51,9 +49,8 @@ public class OptionMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_is_inside_an_abstract_class()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public abstract class MyCommand
|
||||
{
|
||||
[CommandOption("foo")]
|
||||
@@ -69,9 +66,8 @@ public class OptionMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustBeRequiredIfPropertyRequiredAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new OptionMustBeRequiredIfPropertyRequiredAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_non_required_option_is_bound_to_a_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -33,9 +33,8 @@ public class OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_required_option_is_bound_to_a_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -54,9 +53,8 @@ public class OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_required_option_is_bound_to_a_non_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -75,9 +73,8 @@ public class OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_required_option_is_bound_to_a_non_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -96,9 +93,8 @@ public class OptionMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveNameOrShortNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveNameOrShortNameAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new OptionMustHaveNameOrShortNameAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_an_option_does_not_have_a_name_or_short_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -33,9 +33,8 @@ public class OptionMustHaveNameOrShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -54,9 +53,8 @@ public class OptionMustHaveNameOrShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_short_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -75,9 +73,8 @@ public class OptionMustHaveNameOrShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -12,9 +12,8 @@ public class OptionMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_an_option_has_the_same_name_as_another_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +35,8 @@ public class OptionMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_unique_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +58,8 @@ public class OptionMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -81,9 +78,8 @@ public class OptionMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveUniqueShortNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueShortNameAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new OptionMustHaveUniqueShortNameAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_an_option_has_the_same_short_name_as_another_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +36,8 @@ public class OptionMustHaveUniqueShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_unique_short_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +59,8 @@ public class OptionMustHaveUniqueShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_short_name_which_is_unique_only_in_casing()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -84,9 +82,8 @@ public class OptionMustHaveUniqueShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_short_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -105,9 +102,8 @@ public class OptionMustHaveUniqueShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidConverterAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new OptionMustHaveValidConverterAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_converter_that_does_not_derive_from_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter
|
||||
{
|
||||
public string Convert(string? rawValue) => rawValue;
|
||||
@@ -38,9 +38,8 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_converter_that_does_not_derive_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<int>
|
||||
{
|
||||
public override int Convert(string? rawValue) => 42;
|
||||
@@ -64,9 +63,8 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<string>
|
||||
{
|
||||
public override string Convert(string? rawValue) => rawValue;
|
||||
@@ -90,9 +88,8 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_nullable_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<int>
|
||||
{
|
||||
public override int Convert(string? rawValue) => 42;
|
||||
@@ -116,9 +113,8 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_scalar_option_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<string>
|
||||
{
|
||||
public override string Convert(string? rawValue) => rawValue;
|
||||
@@ -142,9 +138,8 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_converter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -163,9 +158,8 @@ public class OptionMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -12,9 +12,8 @@ public class OptionMustHaveValidNameAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_name_which_is_too_short()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -33,9 +32,8 @@ public class OptionMustHaveValidNameAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_name_that_starts_with_a_non_letter_character()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -54,9 +52,8 @@ public class OptionMustHaveValidNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_valid_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -75,9 +72,8 @@ public class OptionMustHaveValidNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -96,9 +92,8 @@ public class OptionMustHaveValidNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidShortNameAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidShortNameAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new OptionMustHaveValidShortNameAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_short_name_which_is_not_a_letter_character()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -33,9 +33,8 @@ public class OptionMustHaveValidShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_a_valid_short_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -54,9 +53,8 @@ public class OptionMustHaveValidShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_a_short_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -75,9 +73,8 @@ public class OptionMustHaveValidShortNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidValidatorsAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new OptionMustHaveValidValidatorsAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_validator_that_does_not_derive_from_BindingValidator()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyValidator
|
||||
{
|
||||
public void Validate(string value) {}
|
||||
@@ -38,9 +38,8 @@ public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_an_option_has_a_validator_that_does_not_derive_from_a_compatible_BindingValidator()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyValidator : BindingValidator<int>
|
||||
{
|
||||
public override BindingValidationError Validate(int value) => Ok();
|
||||
@@ -64,9 +63,8 @@ public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_has_validators_that_all_derive_from_compatible_BindingValidators()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyValidator : BindingValidator<string>
|
||||
{
|
||||
public override BindingValidationError Validate(string value) => Ok();
|
||||
@@ -90,9 +88,8 @@ public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_an_option_does_not_have_validators()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -111,9 +108,8 @@ public class OptionMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_an_option()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeInsideCommandAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeInsideCommandAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustBeInsideCommandAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_parameter_is_inside_a_class_that_is_not_a_command()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyClass
|
||||
{
|
||||
[CommandParameter(0)]
|
||||
@@ -30,9 +30,8 @@ public class ParameterMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_is_inside_a_command()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -51,9 +50,8 @@ public class ParameterMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_is_inside_an_abstract_class()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public abstract class MyCommand
|
||||
{
|
||||
[CommandParameter(0)]
|
||||
@@ -69,9 +67,8 @@ public class ParameterMustBeInsideCommandAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonRequiredAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustBeLastIfNonRequiredAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_non_required_parameter_is_not_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +36,8 @@ public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_required_parameter_is_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +59,8 @@ public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_no_non_required_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -84,9 +82,8 @@ public class ParameterMustBeLastIfNonRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonScalarAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustBeLastIfNonScalarAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_non_scalar_parameter_is_not_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +36,8 @@ public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_scalar_parameter_is_the_last_in_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +59,8 @@ public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_no_non_scalar_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -84,9 +82,8 @@ public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeRequiredIfPropertyRequiredAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustBeRequiredIfPropertyRequiredAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_non_required_parameter_is_bound_to_a_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -33,9 +33,8 @@ public class ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_required_parameter_is_bound_to_a_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -54,9 +53,8 @@ public class ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_required_parameter_is_bound_to_a_non_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -75,9 +73,8 @@ public class ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_required_parameter_is_bound_to_a_non_required_property()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -96,9 +93,8 @@ public class ParameterMustBeRequiredIfPropertyRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeSingleIfNonRequiredAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonRequiredAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustBeSingleIfNonRequiredAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_more_than_one_non_required_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +36,8 @@ public class ParameterMustBeSingleIfNonRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_only_one_non_required_parameter_is_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +59,8 @@ public class ParameterMustBeSingleIfNonRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_no_non_required_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -84,9 +82,8 @@ public class ParameterMustBeSingleIfNonRequiredAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonScalarAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustBeSingleIfNonScalarAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_more_than_one_non_scalar_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +36,8 @@ public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_only_one_non_scalar_parameter_is_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +59,8 @@ public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_no_non_scalar_parameters_are_defined()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -84,9 +82,8 @@ public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -12,9 +12,8 @@ public class ParameterMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_a_parameter_has_the_same_name_as_another_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +35,8 @@ public class ParameterMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_has_unique_name()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +58,8 @@ public class ParameterMustHaveUniqueNameAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveUniqueOrderAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueOrderAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustHaveUniqueOrderAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_parameter_has_the_same_order_as_another_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -36,9 +36,8 @@ public class ParameterMustHaveUniqueOrderAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_has_unique_order()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -60,9 +59,8 @@ public class ParameterMustHaveUniqueOrderAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidConverterAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustHaveValidConverterAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_parameter_has_a_converter_that_does_not_derive_from_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter
|
||||
{
|
||||
public string Convert(string? rawValue) => rawValue;
|
||||
@@ -38,9 +38,8 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_a_parameter_has_a_converter_that_does_not_derive_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<int>
|
||||
{
|
||||
public override int Convert(string? rawValue) => 42;
|
||||
@@ -56,7 +55,6 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
}
|
||||
""";
|
||||
|
||||
|
||||
// Act & assert
|
||||
Analyzer.Should().ProduceDiagnostics(code);
|
||||
}
|
||||
@@ -65,9 +63,8 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<string>
|
||||
{
|
||||
public override string Convert(string? rawValue) => rawValue;
|
||||
@@ -91,9 +88,8 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_nullable_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<int>
|
||||
{
|
||||
public override int Convert(string? rawValue) => 42;
|
||||
@@ -117,9 +113,8 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_non_scalar_parameter_has_a_converter_that_derives_from_a_compatible_BindingConverter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyConverter : BindingConverter<string>
|
||||
{
|
||||
public override string Convert(string? rawValue) => rawValue;
|
||||
@@ -143,9 +138,8 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_does_not_have_a_converter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -164,9 +158,8 @@ public class ParameterMustHaveValidConverterAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidValidatorsAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new ParameterMustHaveValidValidatorsAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_a_parameter_has_a_validator_that_does_not_derive_from_BindingValidator()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyValidator
|
||||
{
|
||||
public void Validate(string value) {}
|
||||
@@ -38,9 +38,8 @@ public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_a_parameter_has_a_validator_that_does_not_derive_from_a_compatible_BindingValidator()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyValidator : BindingValidator<int>
|
||||
{
|
||||
public override BindingValidationError Validate(int value) => Ok();
|
||||
@@ -64,9 +63,8 @@ public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_has_validators_that_all_derive_from_compatible_BindingValidators()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
public class MyValidator : BindingValidator<string>
|
||||
{
|
||||
public override BindingValidationError Validate(string value) => Ok();
|
||||
@@ -90,9 +88,8 @@ public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_parameter_does_not_have_validators()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -111,9 +108,8 @@ public class ParameterMustHaveValidValidatorsAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_on_a_property_that_is_not_a_parameter()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -6,15 +6,15 @@ namespace CliFx.Analyzers.Tests;
|
||||
|
||||
public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
{
|
||||
private static DiagnosticAnalyzer Analyzer { get; } = new SystemConsoleShouldBeAvoidedAnalyzer();
|
||||
private static DiagnosticAnalyzer Analyzer { get; } =
|
||||
new SystemConsoleShouldBeAvoidedAnalyzer();
|
||||
|
||||
[Fact]
|
||||
public void Analyzer_reports_an_error_if_a_command_calls_a_method_on_SystemConsole()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -34,9 +34,8 @@ public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_a_command_accesses_a_property_on_SystemConsole()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -56,9 +55,8 @@ public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
public void Analyzer_reports_an_error_if_a_command_calls_a_method_on_a_property_of_SystemConsole()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -78,9 +76,8 @@ public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_command_interacts_with_the_console_through_IConsole()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -100,9 +97,8 @@ public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_IConsole_is_not_available_in_the_current_method()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
@@ -120,9 +116,8 @@ public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
|
||||
public void Analyzer_does_not_report_an_error_if_a_command_does_not_access_SystemConsole()
|
||||
{
|
||||
// Arrange
|
||||
// language=cs
|
||||
const string code =
|
||||
"""
|
||||
// lang=csharp
|
||||
const string code = """
|
||||
[Command]
|
||||
public class MyCommand : ICommand
|
||||
{
|
||||
|
||||
@@ -18,9 +18,7 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer,
|
||||
protected override string Identifier { get; } = "analyzer";
|
||||
|
||||
public AnalyzerAssertions(DiagnosticAnalyzer analyzer)
|
||||
: base(analyzer)
|
||||
{
|
||||
}
|
||||
: base(analyzer) { }
|
||||
|
||||
private Compilation Compile(string sourceCode)
|
||||
{
|
||||
@@ -43,10 +41,10 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer,
|
||||
|
||||
// Append default imports to the source code
|
||||
var sourceCodeWithUsings =
|
||||
string.Join(Environment.NewLine, defaultSystemNamespaces.Select(n => $"using {n};")) +
|
||||
string.Join(Environment.NewLine, defaultCliFxNamespaces.Select(n => $"using {n};")) +
|
||||
Environment.NewLine +
|
||||
sourceCode;
|
||||
string.Join(Environment.NewLine, defaultSystemNamespaces.Select(n => $"using {n};"))
|
||||
+ string.Join(Environment.NewLine, defaultCliFxNamespaces.Select(n => $"using {n};"))
|
||||
+ Environment.NewLine
|
||||
+ sourceCode;
|
||||
|
||||
// Parse the source code
|
||||
var ast = SyntaxFactory.ParseSyntaxTree(
|
||||
@@ -58,7 +56,9 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer,
|
||||
var compilation = CSharpCompilation.Create(
|
||||
"CliFxTests_DynamicAssembly_" + Guid.NewGuid(),
|
||||
new[] { ast },
|
||||
Net70.References.All
|
||||
Net70
|
||||
.References
|
||||
.All
|
||||
.Append(MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location)),
|
||||
// DLL to avoid having to define the Main() method
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
|
||||
@@ -103,10 +103,13 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer,
|
||||
var producedDiagnosticIds = producedDiagnostics.Select(d => d.Id).Distinct().ToArray();
|
||||
|
||||
var isSuccessfulAssertion =
|
||||
expectedDiagnosticIds.Intersect(producedDiagnosticIds).Count() ==
|
||||
expectedDiagnosticIds.Length;
|
||||
expectedDiagnosticIds.Intersect(producedDiagnosticIds).Count()
|
||||
== expectedDiagnosticIds.Length;
|
||||
|
||||
Execute.Assertion.ForCondition(isSuccessfulAssertion).FailWith(() =>
|
||||
Execute
|
||||
.Assertion
|
||||
.ForCondition(isSuccessfulAssertion)
|
||||
.FailWith(() =>
|
||||
{
|
||||
var buffer = new StringBuilder();
|
||||
|
||||
@@ -148,7 +151,10 @@ internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer,
|
||||
var producedDiagnostics = GetProducedDiagnostics(sourceCode);
|
||||
var isSuccessfulAssertion = !producedDiagnostics.Any();
|
||||
|
||||
Execute.Assertion.ForCondition(isSuccessfulAssertion).FailWith(() =>
|
||||
Execute
|
||||
.Assertion
|
||||
.ForCondition(isSuccessfulAssertion)
|
||||
.FailWith(() =>
|
||||
{
|
||||
var buffer = new StringBuilder();
|
||||
|
||||
|
||||
@@ -14,7 +14,8 @@ public abstract class AnalyzerBase : DiagnosticAnalyzer
|
||||
protected AnalyzerBase(
|
||||
string diagnosticTitle,
|
||||
string diagnosticMessage,
|
||||
DiagnosticSeverity diagnosticSeverity = DiagnosticSeverity.Error)
|
||||
DiagnosticSeverity diagnosticSeverity = DiagnosticSeverity.Error
|
||||
)
|
||||
{
|
||||
SupportedDiagnostic = new DiagnosticDescriptor(
|
||||
"CliFx_" + GetType().Name.TrimEnd("Analyzer"),
|
||||
|
||||
@@ -17,10 +17,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
|
||||
<!-- Make sure to target the lowest possible version of the compiler for wider support -->
|
||||
<PackageReference Include="Microsoft.CodeAnalysis" Version="3.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="PolyShim" Version="1.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="PolyShim" Version="1.8.0" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -13,14 +13,14 @@ public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase
|
||||
public CommandMustBeAnnotatedAnalyzer()
|
||||
: base(
|
||||
$"Commands must be annotated with `{SymbolNames.CliFxCommandAttribute}`",
|
||||
$"This type must be annotated with `{SymbolNames.CliFxCommandAttribute}` in order to be a valid command.")
|
||||
{
|
||||
}
|
||||
$"This type must be annotated with `{SymbolNames.CliFxCommandAttribute}` in order to be a valid command."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
ClassDeclarationSyntax classDeclaration,
|
||||
ITypeSymbol type)
|
||||
ITypeSymbol type
|
||||
)
|
||||
{
|
||||
// Ignore abstract classes, because they may be used to define
|
||||
// base implementations for commands, in which case the command
|
||||
@@ -28,12 +28,11 @@ public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase
|
||||
if (type.IsAbstract)
|
||||
return;
|
||||
|
||||
var implementsCommandInterface = type
|
||||
.AllInterfaces
|
||||
.Any(i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface));
|
||||
var implementsCommandInterface = type.AllInterfaces.Any(
|
||||
i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)
|
||||
);
|
||||
|
||||
var hasCommandAttribute = type
|
||||
.GetAttributes()
|
||||
var hasCommandAttribute = type.GetAttributes()
|
||||
.Select(a => a.AttributeClass)
|
||||
.Any(c => c.DisplayNameMatches(SymbolNames.CliFxCommandAttribute));
|
||||
|
||||
@@ -41,9 +40,7 @@ public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase
|
||||
// then it's very likely a user error.
|
||||
if (implementsCommandInterface && !hasCommandAttribute)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(classDeclaration.Identifier.GetLocation())
|
||||
);
|
||||
context.ReportDiagnostic(CreateDiagnostic(classDeclaration.Identifier.GetLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,31 +13,28 @@ public class CommandMustImplementInterfaceAnalyzer : AnalyzerBase
|
||||
public CommandMustImplementInterfaceAnalyzer()
|
||||
: base(
|
||||
$"Commands must implement `{SymbolNames.CliFxCommandInterface}` interface",
|
||||
$"This type must implement `{SymbolNames.CliFxCommandInterface}` interface in order to be a valid command.")
|
||||
{
|
||||
}
|
||||
$"This type must implement `{SymbolNames.CliFxCommandInterface}` interface in order to be a valid command."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
ClassDeclarationSyntax classDeclaration,
|
||||
ITypeSymbol type)
|
||||
ITypeSymbol type
|
||||
)
|
||||
{
|
||||
var hasCommandAttribute = type
|
||||
.GetAttributes()
|
||||
var hasCommandAttribute = type.GetAttributes()
|
||||
.Select(a => a.AttributeClass)
|
||||
.Any(c => c.DisplayNameMatches(SymbolNames.CliFxCommandAttribute));
|
||||
|
||||
var implementsCommandInterface = type
|
||||
.AllInterfaces
|
||||
.Any(i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface));
|
||||
var implementsCommandInterface = type.AllInterfaces.Any(
|
||||
i => i.DisplayNameMatches(SymbolNames.CliFxCommandInterface)
|
||||
);
|
||||
|
||||
// If the attribute is present, but the interface is not implemented,
|
||||
// it's very likely a user error.
|
||||
if (hasCommandAttribute && !implementsCommandInterface)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(classDeclaration.Identifier.GetLocation())
|
||||
);
|
||||
context.ReportDiagnostic(CreateDiagnostic(classDeclaration.Identifier.GetLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ internal partial class CommandOptionSymbol : ICommandMemberSymbol
|
||||
char? shortName,
|
||||
bool? isRequired,
|
||||
ITypeSymbol? converterType,
|
||||
IReadOnlyList<ITypeSymbol> validatorTypes)
|
||||
IReadOnlyList<ITypeSymbol> validatorTypes
|
||||
)
|
||||
{
|
||||
Property = property;
|
||||
Name = name;
|
||||
@@ -38,9 +39,14 @@ internal partial class CommandOptionSymbol : ICommandMemberSymbol
|
||||
|
||||
internal partial class CommandOptionSymbol
|
||||
{
|
||||
private static AttributeData? TryGetOptionAttribute(IPropertySymbol property) => property
|
||||
private static AttributeData? TryGetOptionAttribute(IPropertySymbol property) =>
|
||||
property
|
||||
.GetAttributes()
|
||||
.FirstOrDefault(a => a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandOptionAttribute) == true);
|
||||
.FirstOrDefault(
|
||||
a =>
|
||||
a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandOptionAttribute)
|
||||
== true
|
||||
);
|
||||
|
||||
public static CommandOptionSymbol? TryResolve(IPropertySymbol property)
|
||||
{
|
||||
@@ -48,19 +54,22 @@ internal partial class CommandOptionSymbol
|
||||
if (attribute is null)
|
||||
return null;
|
||||
|
||||
var name = attribute
|
||||
var name =
|
||||
attribute
|
||||
.ConstructorArguments
|
||||
.Where(a => a.Type?.SpecialType == SpecialType.System_String)
|
||||
.Select(a => a.Value)
|
||||
.FirstOrDefault() as string;
|
||||
|
||||
var shortName = attribute
|
||||
var shortName =
|
||||
attribute
|
||||
.ConstructorArguments
|
||||
.Where(a => a.Type?.SpecialType == SpecialType.System_Char)
|
||||
.Select(a => a.Value)
|
||||
.FirstOrDefault() as char?;
|
||||
|
||||
var isRequired = attribute
|
||||
var isRequired =
|
||||
attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "IsRequired")
|
||||
.Select(a => a.Value.Value)
|
||||
@@ -81,7 +90,14 @@ internal partial class CommandOptionSymbol
|
||||
.Cast<ITypeSymbol>()
|
||||
.ToArray();
|
||||
|
||||
return new CommandOptionSymbol(property, name, shortName, isRequired, converter, validators);
|
||||
return new CommandOptionSymbol(
|
||||
property,
|
||||
name,
|
||||
shortName,
|
||||
isRequired,
|
||||
converter,
|
||||
validators
|
||||
);
|
||||
}
|
||||
|
||||
public static bool IsOptionProperty(IPropertySymbol property) =>
|
||||
|
||||
@@ -25,7 +25,8 @@ internal partial class CommandParameterSymbol : ICommandMemberSymbol
|
||||
string? name,
|
||||
bool? isRequired,
|
||||
ITypeSymbol? converterType,
|
||||
IReadOnlyList<ITypeSymbol> validatorTypes)
|
||||
IReadOnlyList<ITypeSymbol> validatorTypes
|
||||
)
|
||||
{
|
||||
Property = property;
|
||||
Order = order;
|
||||
@@ -38,9 +39,14 @@ internal partial class CommandParameterSymbol : ICommandMemberSymbol
|
||||
|
||||
internal partial class CommandParameterSymbol
|
||||
{
|
||||
private static AttributeData? TryGetParameterAttribute(IPropertySymbol property) => property
|
||||
private static AttributeData? TryGetParameterAttribute(IPropertySymbol property) =>
|
||||
property
|
||||
.GetAttributes()
|
||||
.FirstOrDefault(a => a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandParameterAttribute) == true);
|
||||
.FirstOrDefault(
|
||||
a =>
|
||||
a.AttributeClass?.DisplayNameMatches(SymbolNames.CliFxCommandParameterAttribute)
|
||||
== true
|
||||
);
|
||||
|
||||
public static CommandParameterSymbol? TryResolve(IPropertySymbol property)
|
||||
{
|
||||
@@ -48,18 +54,17 @@ internal partial class CommandParameterSymbol
|
||||
if (attribute is null)
|
||||
return null;
|
||||
|
||||
var order = (int)attribute
|
||||
.ConstructorArguments
|
||||
.Select(a => a.Value)
|
||||
.First()!;
|
||||
var order = (int)attribute.ConstructorArguments.Select(a => a.Value).First()!;
|
||||
|
||||
var name = attribute
|
||||
var name =
|
||||
attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "Name")
|
||||
.Select(a => a.Value.Value)
|
||||
.FirstOrDefault() as string;
|
||||
|
||||
var isRequired = attribute
|
||||
var isRequired =
|
||||
attribute
|
||||
.NamedArguments
|
||||
.Where(a => a.Key == "IsRequired")
|
||||
.Select(a => a.Value.Value)
|
||||
|
||||
@@ -16,6 +16,6 @@ internal interface ICommandMemberSymbol
|
||||
internal static class CommandMemberSymbolExtensions
|
||||
{
|
||||
public static bool IsScalar(this ICommandMemberSymbol member) =>
|
||||
member.Property.Type.SpecialType == SpecialType.System_String ||
|
||||
member.Property.Type.TryGetEnumerableUnderlyingType() is null;
|
||||
member.Property.Type.SpecialType == SpecialType.System_String
|
||||
|| member.Property.Type.TryGetEnumerableUnderlyingType() is null;
|
||||
}
|
||||
@@ -4,7 +4,8 @@ internal static class SymbolNames
|
||||
{
|
||||
public const string CliFxCommandInterface = "CliFx.ICommand";
|
||||
public const string CliFxCommandAttribute = "CliFx.Attributes.CommandAttribute";
|
||||
public const string CliFxCommandParameterAttribute = "CliFx.Attributes.CommandParameterAttribute";
|
||||
public const string CliFxCommandParameterAttribute =
|
||||
"CliFx.Attributes.CommandParameterAttribute";
|
||||
public const string CliFxCommandOptionAttribute = "CliFx.Attributes.CommandOptionAttribute";
|
||||
public const string CliFxConsoleInterface = "CliFx.Infrastructure.IConsole";
|
||||
public const string CliFxBindingConverterClass = "CliFx.Extensibility.BindingConverter<T>";
|
||||
|
||||
@@ -13,14 +13,14 @@ public class OptionMustBeInsideCommandAnalyzer : AnalyzerBase
|
||||
public OptionMustBeInsideCommandAnalyzer()
|
||||
: base(
|
||||
"Options must be defined inside commands",
|
||||
$"This option must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`.")
|
||||
{
|
||||
}
|
||||
$"This option must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -12,14 +12,14 @@ public class OptionMustBeRequiredIfPropertyRequiredAnalyzer : AnalyzerBase
|
||||
public OptionMustBeRequiredIfPropertyRequiredAnalyzer()
|
||||
: base(
|
||||
"Options bound to required properties cannot be marked as non-required",
|
||||
"This option cannot be marked as non-required because it's bound to a required property.")
|
||||
{
|
||||
}
|
||||
"This option cannot be marked as non-required because it's bound to a required property."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
@@ -34,11 +34,7 @@ public class OptionMustBeRequiredIfPropertyRequiredAnalyzer : AnalyzerBase
|
||||
if (option.IsRequired != false)
|
||||
return;
|
||||
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(
|
||||
propertyDeclaration.Identifier.GetLocation()
|
||||
)
|
||||
);
|
||||
context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.Identifier.GetLocation()));
|
||||
}
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
|
||||
@@ -12,14 +12,14 @@ public class OptionMustHaveNameOrShortNameAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveNameOrShortNameAnalyzer()
|
||||
: base(
|
||||
"Options must have either a name or short name specified",
|
||||
"This option must have either a name or short name specified.")
|
||||
{
|
||||
}
|
||||
"This option must have either a name or short name specified."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var option = CommandOptionSymbol.TryResolve(property);
|
||||
if (option is null)
|
||||
|
||||
@@ -14,16 +14,16 @@ public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveUniqueNameAnalyzer()
|
||||
: base(
|
||||
"Options must have unique names",
|
||||
"This option's name must be unique within the command (comparison IS NOT case sensitive). " +
|
||||
"Specified name: `{0}`. " +
|
||||
"Property bound to another option with the same name: `{1}`.")
|
||||
{
|
||||
}
|
||||
"This option's name must be unique within the command (comparison IS NOT case sensitive). "
|
||||
+ "Specified name: `{0}`. "
|
||||
+ "Property bound to another option with the same name: `{1}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -13,16 +13,16 @@ public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveUniqueShortNameAnalyzer()
|
||||
: base(
|
||||
"Options must have unique short names",
|
||||
"This option's short name must be unique within the command (comparison IS case sensitive). " +
|
||||
"Specified short name: `{0}` " +
|
||||
"Property bound to another option with the same short name: `{1}`.")
|
||||
{
|
||||
}
|
||||
"This option's short name must be unique within the command (comparison IS case sensitive). "
|
||||
+ "Specified short name: `{0}` "
|
||||
+ "Property bound to another option with the same short name: `{1}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -13,14 +13,14 @@ public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveValidConverterAnalyzer()
|
||||
: base(
|
||||
$"Option converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
|
||||
$"Converter specified for this option must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`.")
|
||||
{
|
||||
}
|
||||
$"Converter specified for this option must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var option = CommandOptionSymbol.TryResolve(property);
|
||||
if (option is null)
|
||||
@@ -32,18 +32,24 @@ public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase
|
||||
var converterValueType = option
|
||||
.ConverterType
|
||||
.GetBaseTypes()
|
||||
.FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass))?
|
||||
.TypeArguments
|
||||
.FirstOrDefault(
|
||||
t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass)
|
||||
)
|
||||
?.TypeArguments
|
||||
.FirstOrDefault();
|
||||
|
||||
// Value returned by the converter must be assignable to the property type
|
||||
var isCompatible =
|
||||
converterValueType is not null && (option.IsScalar()
|
||||
converterValueType is not null
|
||||
&& (
|
||||
option.IsScalar()
|
||||
// Scalar
|
||||
? context.Compilation.IsAssignable(converterValueType, property.Type)
|
||||
// Non-scalar (assume we can handle all IEnumerable types for simplicity)
|
||||
: property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType &&
|
||||
context.Compilation.IsAssignable(converterValueType, enumerableUnderlyingType)
|
||||
: property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType
|
||||
&& context
|
||||
.Compilation
|
||||
.IsAssignable(converterValueType, enumerableUnderlyingType)
|
||||
);
|
||||
|
||||
if (!isCompatible)
|
||||
|
||||
@@ -12,15 +12,15 @@ public class OptionMustHaveValidNameAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveValidNameAnalyzer()
|
||||
: base(
|
||||
"Options must have valid names",
|
||||
"This option's name must be at least 2 characters long and must start with a letter. " +
|
||||
"Specified name: `{0}`.")
|
||||
{
|
||||
}
|
||||
"This option's name must be at least 2 characters long and must start with a letter. "
|
||||
+ "Specified name: `{0}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var option = CommandOptionSymbol.TryResolve(property);
|
||||
if (option is null)
|
||||
@@ -32,10 +32,7 @@ public class OptionMustHaveValidNameAnalyzer : AnalyzerBase
|
||||
if (option.Name.Length < 2 || !char.IsLetter(option.Name[0]))
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(
|
||||
propertyDeclaration.Identifier.GetLocation(),
|
||||
option.Name
|
||||
)
|
||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation(), option.Name)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,15 +12,15 @@ public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveValidShortNameAnalyzer()
|
||||
: base(
|
||||
"Option short names must be letter characters",
|
||||
"This option's short name must be a single letter character. " +
|
||||
"Specified short name: `{0}`.")
|
||||
{
|
||||
}
|
||||
"This option's short name must be a single letter character. "
|
||||
+ "Specified short name: `{0}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var option = CommandOptionSymbol.TryResolve(property);
|
||||
if (option is null)
|
||||
@@ -32,10 +32,7 @@ public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase
|
||||
if (!char.IsLetter(option.ShortName.Value))
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(
|
||||
propertyDeclaration.Identifier.GetLocation(),
|
||||
option.ShortName
|
||||
)
|
||||
CreateDiagnostic(propertyDeclaration.Identifier.GetLocation(), option.ShortName)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,14 +13,14 @@ public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
||||
public OptionMustHaveValidValidatorsAnalyzer()
|
||||
: base(
|
||||
$"Option validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`",
|
||||
$"Each validator specified for this option must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`.")
|
||||
{
|
||||
}
|
||||
$"Each validator specified for this option must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var option = CommandOptionSymbol.TryResolve(property);
|
||||
if (option is null)
|
||||
@@ -30,14 +30,17 @@ public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
||||
{
|
||||
var validatorValueType = validatorType
|
||||
.GetBaseTypes()
|
||||
.FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass))?
|
||||
.TypeArguments
|
||||
.FirstOrDefault(
|
||||
t =>
|
||||
t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass)
|
||||
)
|
||||
?.TypeArguments
|
||||
.FirstOrDefault();
|
||||
|
||||
// Value passed to the validator must be assignable from the property type
|
||||
var isCompatible =
|
||||
validatorValueType is not null &&
|
||||
context.Compilation.IsAssignable(property.Type, validatorValueType);
|
||||
validatorValueType is not null
|
||||
&& context.Compilation.IsAssignable(property.Type, validatorValueType);
|
||||
|
||||
if (!isCompatible)
|
||||
{
|
||||
|
||||
@@ -13,14 +13,14 @@ public class ParameterMustBeInsideCommandAnalyzer : AnalyzerBase
|
||||
public ParameterMustBeInsideCommandAnalyzer()
|
||||
: base(
|
||||
"Parameters must be defined inside commands",
|
||||
$"This parameter must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`.")
|
||||
{
|
||||
}
|
||||
$"This parameter must be defined inside a class that implements `{SymbolNames.CliFxCommandInterface}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -13,15 +13,15 @@ public class ParameterMustBeLastIfNonRequiredAnalyzer : AnalyzerBase
|
||||
public ParameterMustBeLastIfNonRequiredAnalyzer()
|
||||
: base(
|
||||
"Parameters marked as non-required must be the last in order",
|
||||
"This parameter is non-required so it must be the last in order (its order must be highest within the command). " +
|
||||
"Property bound to another non-required parameter: `{0}`.")
|
||||
{
|
||||
}
|
||||
"This parameter is non-required so it must be the last in order (its order must be highest within the command). "
|
||||
+ "Property bound to another non-required parameter: `{0}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -13,15 +13,15 @@ public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase
|
||||
public ParameterMustBeLastIfNonScalarAnalyzer()
|
||||
: base(
|
||||
"Parameters of non-scalar types must be the last in order",
|
||||
"This parameter has a non-scalar type so it must be the last in order (its order must be highest within the command). " +
|
||||
"Property bound to another non-scalar parameter: `{0}`.")
|
||||
{
|
||||
}
|
||||
"This parameter has a non-scalar type so it must be the last in order (its order must be highest within the command). "
|
||||
+ "Property bound to another non-scalar parameter: `{0}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -12,14 +12,14 @@ public class ParameterMustBeRequiredIfPropertyRequiredAnalyzer : AnalyzerBase
|
||||
public ParameterMustBeRequiredIfPropertyRequiredAnalyzer()
|
||||
: base(
|
||||
"Parameters bound to required properties cannot be marked as non-required",
|
||||
"This parameter cannot be marked as non-required because it's bound to a required property.")
|
||||
{
|
||||
}
|
||||
"This parameter cannot be marked as non-required because it's bound to a required property."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
@@ -34,11 +34,7 @@ public class ParameterMustBeRequiredIfPropertyRequiredAnalyzer : AnalyzerBase
|
||||
if (parameter.IsRequired != false)
|
||||
return;
|
||||
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(
|
||||
propertyDeclaration.Identifier.GetLocation()
|
||||
)
|
||||
);
|
||||
context.ReportDiagnostic(CreateDiagnostic(propertyDeclaration.Identifier.GetLocation()));
|
||||
}
|
||||
|
||||
public override void Initialize(AnalysisContext context)
|
||||
|
||||
@@ -13,15 +13,15 @@ public class ParameterMustBeSingleIfNonRequiredAnalyzer : AnalyzerBase
|
||||
public ParameterMustBeSingleIfNonRequiredAnalyzer()
|
||||
: base(
|
||||
"Parameters marked as non-required are limited to one per command",
|
||||
"This parameter is non-required so it must be the only such parameter in the command. " +
|
||||
"Property bound to another non-required parameter: `{0}`.")
|
||||
{
|
||||
}
|
||||
"This parameter is non-required so it must be the only such parameter in the command. "
|
||||
+ "Property bound to another non-required parameter: `{0}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -13,15 +13,15 @@ public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase
|
||||
public ParameterMustBeSingleIfNonScalarAnalyzer()
|
||||
: base(
|
||||
"Parameters of non-scalar types are limited to one per command",
|
||||
"This parameter has a non-scalar type so it must be the only such parameter in the command. " +
|
||||
"Property bound to another non-scalar parameter: `{0}`.")
|
||||
{
|
||||
}
|
||||
"This parameter has a non-scalar type so it must be the only such parameter in the command. "
|
||||
+ "Property bound to another non-scalar parameter: `{0}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -14,16 +14,16 @@ public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase
|
||||
public ParameterMustHaveUniqueNameAnalyzer()
|
||||
: base(
|
||||
"Parameters must have unique names",
|
||||
"This parameter's name must be unique within the command (comparison IS NOT case sensitive). " +
|
||||
"Specified name: `{0}`. " +
|
||||
"Property bound to another parameter with the same name: `{1}`.")
|
||||
{
|
||||
}
|
||||
"This parameter's name must be unique within the command (comparison IS NOT case sensitive). "
|
||||
+ "Specified name: `{0}`. "
|
||||
+ "Property bound to another parameter with the same name: `{1}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
@@ -51,7 +51,13 @@ public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase
|
||||
if (string.IsNullOrWhiteSpace(otherParameter.Name))
|
||||
continue;
|
||||
|
||||
if (string.Equals(parameter.Name, otherParameter.Name, StringComparison.OrdinalIgnoreCase))
|
||||
if (
|
||||
string.Equals(
|
||||
parameter.Name,
|
||||
otherParameter.Name,
|
||||
StringComparison.OrdinalIgnoreCase
|
||||
)
|
||||
)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(
|
||||
|
||||
@@ -13,16 +13,16 @@ public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase
|
||||
public ParameterMustHaveUniqueOrderAnalyzer()
|
||||
: base(
|
||||
"Parameters must have unique order",
|
||||
"This parameter's order must be unique within the command. " +
|
||||
"Specified order: {0}. " +
|
||||
"Property bound to another parameter with the same order: `{1}`.")
|
||||
{
|
||||
}
|
||||
"This parameter's order must be unique within the command. "
|
||||
+ "Specified order: {0}. "
|
||||
+ "Property bound to another parameter with the same order: `{1}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
if (property.ContainingType is null)
|
||||
return;
|
||||
|
||||
@@ -13,14 +13,14 @@ public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase
|
||||
public ParameterMustHaveValidConverterAnalyzer()
|
||||
: base(
|
||||
$"Parameter converters must derive from `{SymbolNames.CliFxBindingConverterClass}`",
|
||||
$"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`.")
|
||||
{
|
||||
}
|
||||
$"Converter specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingConverterClass}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||
if (parameter is null)
|
||||
@@ -32,18 +32,24 @@ public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase
|
||||
var converterValueType = parameter
|
||||
.ConverterType
|
||||
.GetBaseTypes()
|
||||
.FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass))?
|
||||
.TypeArguments
|
||||
.FirstOrDefault(
|
||||
t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingConverterClass)
|
||||
)
|
||||
?.TypeArguments
|
||||
.FirstOrDefault();
|
||||
|
||||
// Value returned by the converter must be assignable to the property type
|
||||
var isCompatible =
|
||||
converterValueType is not null && (parameter.IsScalar()
|
||||
converterValueType is not null
|
||||
&& (
|
||||
parameter.IsScalar()
|
||||
// Scalar
|
||||
? context.Compilation.IsAssignable(converterValueType, property.Type)
|
||||
// Non-scalar (assume we can handle all IEnumerable types for simplicity)
|
||||
: property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType &&
|
||||
context.Compilation.IsAssignable(converterValueType, enumerableUnderlyingType)
|
||||
: property.Type.TryGetEnumerableUnderlyingType() is { } enumerableUnderlyingType
|
||||
&& context
|
||||
.Compilation
|
||||
.IsAssignable(converterValueType, enumerableUnderlyingType)
|
||||
);
|
||||
|
||||
if (!isCompatible)
|
||||
|
||||
@@ -13,14 +13,14 @@ public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
||||
public ParameterMustHaveValidValidatorsAnalyzer()
|
||||
: base(
|
||||
$"Parameter validators must derive from `{SymbolNames.CliFxBindingValidatorClass}`",
|
||||
$"Each validator specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`.")
|
||||
{
|
||||
}
|
||||
$"Each validator specified for this parameter must derive from a compatible `{SymbolNames.CliFxBindingValidatorClass}`."
|
||||
) { }
|
||||
|
||||
private void Analyze(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
PropertyDeclarationSyntax propertyDeclaration,
|
||||
IPropertySymbol property)
|
||||
IPropertySymbol property
|
||||
)
|
||||
{
|
||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||
if (parameter is null)
|
||||
@@ -30,14 +30,17 @@ public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase
|
||||
{
|
||||
var validatorValueType = validatorType
|
||||
.GetBaseTypes()
|
||||
.FirstOrDefault(t => t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass))?
|
||||
.TypeArguments
|
||||
.FirstOrDefault(
|
||||
t =>
|
||||
t.ConstructedFrom.DisplayNameMatches(SymbolNames.CliFxBindingValidatorClass)
|
||||
)
|
||||
?.TypeArguments
|
||||
.FirstOrDefault();
|
||||
|
||||
// Value passed to the validator must be assignable from the property type
|
||||
var isCompatible =
|
||||
validatorValueType is not null &&
|
||||
context.Compilation.IsAssignable(property.Type, validatorValueType);
|
||||
validatorValueType is not null
|
||||
&& context.Compilation.IsAssignable(property.Type, validatorValueType);
|
||||
|
||||
if (!isCompatible)
|
||||
{
|
||||
|
||||
@@ -15,13 +15,13 @@ public class SystemConsoleShouldBeAvoidedAnalyzer : AnalyzerBase
|
||||
: base(
|
||||
$"Avoid calling `System.Console` where `{SymbolNames.CliFxConsoleInterface}` is available",
|
||||
$"Use the provided `{SymbolNames.CliFxConsoleInterface}` abstraction instead of `System.Console` to ensure that the command can be tested in isolation.",
|
||||
DiagnosticSeverity.Warning)
|
||||
{
|
||||
}
|
||||
DiagnosticSeverity.Warning
|
||||
) { }
|
||||
|
||||
private MemberAccessExpressionSyntax? TryGetSystemConsoleMemberAccess(
|
||||
SyntaxNodeAnalysisContext context,
|
||||
SyntaxNode node)
|
||||
SyntaxNode node
|
||||
)
|
||||
{
|
||||
var currentNode = node;
|
||||
|
||||
@@ -65,9 +65,7 @@ public class SystemConsoleShouldBeAvoidedAnalyzer : AnalyzerBase
|
||||
|
||||
if (isConsoleInterfaceAvailable)
|
||||
{
|
||||
context.ReportDiagnostic(
|
||||
CreateDiagnostic(systemConsoleMemberAccess.GetLocation())
|
||||
);
|
||||
context.ReportDiagnostic(CreateDiagnostic(systemConsoleMemberAccess.GetLocation()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,14 +29,19 @@ internal static class RoslynExtensions
|
||||
}
|
||||
}
|
||||
|
||||
public static ITypeSymbol? TryGetEnumerableUnderlyingType(this ITypeSymbol type) => type
|
||||
.AllInterfaces
|
||||
.FirstOrDefault(i => i.ConstructedFrom.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T)?
|
||||
.TypeArguments[0];
|
||||
public static ITypeSymbol? TryGetEnumerableUnderlyingType(this ITypeSymbol type) =>
|
||||
type.AllInterfaces
|
||||
.FirstOrDefault(
|
||||
i =>
|
||||
i.ConstructedFrom.SpecialType
|
||||
== SpecialType.System_Collections_Generic_IEnumerable_T
|
||||
)
|
||||
?.TypeArguments[0];
|
||||
|
||||
// Detect if the property is required through roundabout means so as to not have to take dependency
|
||||
// on higher versions of the C# compiler.
|
||||
public static bool IsRequired(this IPropertySymbol property) => property
|
||||
public static bool IsRequired(this IPropertySymbol property) =>
|
||||
property
|
||||
// Can't rely on the RequiredMemberAttribute because it's generated by the compiler, not added by the user,
|
||||
// so we have to check for the presence of the `required` modifier in the syntax tree instead.
|
||||
.DeclaringSyntaxReferences
|
||||
@@ -45,14 +50,19 @@ internal static class RoslynExtensions
|
||||
.SelectMany(p => p.Modifiers)
|
||||
.Any(m => m.IsKind((SyntaxKind)8447));
|
||||
|
||||
public static bool IsAssignable(this Compilation compilation, ITypeSymbol source, ITypeSymbol destination) =>
|
||||
compilation.ClassifyConversion(source, destination).Exists;
|
||||
public static bool IsAssignable(
|
||||
this Compilation compilation,
|
||||
ITypeSymbol source,
|
||||
ITypeSymbol destination
|
||||
) => compilation.ClassifyConversion(source, destination).Exists;
|
||||
|
||||
public static void HandleClassDeclaration(
|
||||
this AnalysisContext analysisContext,
|
||||
Action<SyntaxNodeAnalysisContext, ClassDeclarationSyntax, ITypeSymbol> analyze)
|
||||
Action<SyntaxNodeAnalysisContext, ClassDeclarationSyntax, ITypeSymbol> analyze
|
||||
)
|
||||
{
|
||||
analysisContext.RegisterSyntaxNodeAction(ctx =>
|
||||
analysisContext.RegisterSyntaxNodeAction(
|
||||
ctx =>
|
||||
{
|
||||
if (ctx.Node is not ClassDeclarationSyntax classDeclaration)
|
||||
return;
|
||||
@@ -62,14 +72,18 @@ internal static class RoslynExtensions
|
||||
return;
|
||||
|
||||
analyze(ctx, classDeclaration, type);
|
||||
}, SyntaxKind.ClassDeclaration);
|
||||
},
|
||||
SyntaxKind.ClassDeclaration
|
||||
);
|
||||
}
|
||||
|
||||
public static void HandlePropertyDeclaration(
|
||||
this AnalysisContext analysisContext,
|
||||
Action<SyntaxNodeAnalysisContext, PropertyDeclarationSyntax, IPropertySymbol> analyze)
|
||||
Action<SyntaxNodeAnalysisContext, PropertyDeclarationSyntax, IPropertySymbol> analyze
|
||||
)
|
||||
{
|
||||
analysisContext.RegisterSyntaxNodeAction(ctx =>
|
||||
analysisContext.RegisterSyntaxNodeAction(
|
||||
ctx =>
|
||||
{
|
||||
if (ctx.Node is not PropertyDeclarationSyntax propertyDeclaration)
|
||||
return;
|
||||
@@ -79,6 +93,8 @@ internal static class RoslynExtensions
|
||||
return;
|
||||
|
||||
analyze(ctx, propertyDeclaration, property);
|
||||
}, SyntaxKind.PropertyDeclaration);
|
||||
},
|
||||
SyntaxKind.PropertyDeclaration
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,8 @@ internal static class StringExtensions
|
||||
public static string TrimEnd(
|
||||
this string str,
|
||||
string sub,
|
||||
StringComparison comparison = StringComparison.Ordinal)
|
||||
StringComparison comparison = StringComparison.Ordinal
|
||||
)
|
||||
{
|
||||
while (str.EndsWith(sub, comparison))
|
||||
str = str[..^sub.Length];
|
||||
|
||||
@@ -16,9 +16,7 @@ public partial class Benchmarks
|
||||
[NamedArgument('b', "bool", Constraint = NumArgsConstraint.Optional, Const = true)]
|
||||
public bool BoolOption { get; set; }
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
}
|
||||
public void Execute() { }
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Clipr")]
|
||||
|
||||
@@ -8,14 +8,10 @@ public partial class Benchmarks
|
||||
public class CoconaCommand
|
||||
{
|
||||
public void Execute(
|
||||
[Option("str", new []{'s'})]
|
||||
string? strOption,
|
||||
[Option("int", new []{'i'})]
|
||||
int intOption,
|
||||
[Option("bool", new []{'b'})]
|
||||
bool boolOption)
|
||||
{
|
||||
}
|
||||
[Option("str", new[] { 's' })] string? strOption,
|
||||
[Option("int", new[] { 'i' })] int intOption,
|
||||
[Option("bool", new[] { 'b' })] bool boolOption
|
||||
) { }
|
||||
}
|
||||
|
||||
[Benchmark(Description = "Cocona")]
|
||||
|
||||
@@ -16,9 +16,7 @@ public partial class Benchmarks
|
||||
[Option('b', "bool")]
|
||||
public bool BoolOption { get; set; }
|
||||
|
||||
public void Execute()
|
||||
{
|
||||
}
|
||||
public void Execute() { }
|
||||
}
|
||||
|
||||
[Benchmark(Description = "CommandLineParser")]
|
||||
|
||||
@@ -16,9 +16,7 @@ public partial class Benchmarks
|
||||
[ArgShortcut("--bool"), ArgShortcut("-b")]
|
||||
public bool BoolOption { get; set; }
|
||||
|
||||
public void Main()
|
||||
{
|
||||
}
|
||||
public void Main() { }
|
||||
}
|
||||
|
||||
[Benchmark(Description = "PowerArgs")]
|
||||
|
||||
@@ -15,18 +15,9 @@ public partial class Benchmarks
|
||||
{
|
||||
var command = new RootCommand
|
||||
{
|
||||
new Option(new[] {"--str", "-s"})
|
||||
{
|
||||
Argument = new Argument<string?>()
|
||||
},
|
||||
new Option(new[] {"--int", "-i"})
|
||||
{
|
||||
Argument = new Argument<int>()
|
||||
},
|
||||
new Option(new[] {"--bool", "-b"})
|
||||
{
|
||||
Argument = new Argument<bool>()
|
||||
}
|
||||
new Option(new[] { "--str", "-s" }) { Argument = new Argument<string?>() },
|
||||
new Option(new[] { "--int", "-i" }) { Argument = new Argument<int>() },
|
||||
new Option(new[] { "--bool", "-b" }) { Argument = new Argument<bool>() }
|
||||
};
|
||||
|
||||
command.Handler = CommandHandler.Create(
|
||||
|
||||
@@ -9,11 +9,10 @@ namespace CliFx.Benchmarks;
|
||||
[Orderer(SummaryOrderPolicy.FastestToSlowest)]
|
||||
public partial class Benchmarks
|
||||
{
|
||||
private static readonly string[] Arguments = {"--str", "hello world", "-i", "13", "-b"};
|
||||
private static readonly string[] Arguments = { "--str", "hello world", "-i", "13", "-b" };
|
||||
|
||||
public static void Main() => BenchmarkRunner.Run<Benchmarks>(
|
||||
DefaultConfig
|
||||
.Instance
|
||||
.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
public static void Main() =>
|
||||
BenchmarkRunner.Run<Benchmarks>(
|
||||
DefaultConfig.Instance.WithOptions(ConfigOptions.DisableOptimizationsValidator)
|
||||
);
|
||||
}
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.5" />
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
|
||||
<PackageReference Include="clipr" Version="1.6.1" />
|
||||
<PackageReference Include="Cocona" Version="2.2.0" />
|
||||
<PackageReference Include="CommandLineParser" Version="2.9.1" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.0.2" />
|
||||
<PackageReference Include="PowerArgs" Version="4.0.2" />
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
|
||||
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="4.1.0" />
|
||||
<PackageReference Include="PowerArgs" Version="4.0.3" />
|
||||
<PackageReference Include="System.CommandLine" Version="2.0.0-beta1.20574.7" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ApplicationIcon>../favicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -8,7 +8,7 @@ using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book add", Description = "Add a book to the library.")]
|
||||
[Command("book add", Description = "Adds a book to the library.")]
|
||||
public partial class BookAddCommand : ICommand
|
||||
{
|
||||
private readonly LibraryProvider _libraryProvider;
|
||||
@@ -49,7 +49,8 @@ public partial class BookAddCommand
|
||||
{
|
||||
private static readonly Random Random = new();
|
||||
|
||||
private static DateTimeOffset CreateRandomDate() => new(
|
||||
private static DateTimeOffset CreateRandomDate() =>
|
||||
new(
|
||||
Random.Next(1800, 2020),
|
||||
Random.Next(1, 12),
|
||||
Random.Next(1, 28),
|
||||
@@ -59,7 +60,8 @@ public partial class BookAddCommand
|
||||
TimeSpan.Zero
|
||||
);
|
||||
|
||||
private static Isbn CreateRandomIsbn() => new(
|
||||
private static Isbn CreateRandomIsbn() =>
|
||||
new(
|
||||
Random.Next(0, 999),
|
||||
Random.Next(0, 99),
|
||||
Random.Next(0, 99999),
|
||||
|
||||
@@ -7,7 +7,7 @@ using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book", Description = "Retrieve a book from the library.")]
|
||||
[Command("book", Description = "Retrieves a book from the library.")]
|
||||
public class BookCommand : ICommand
|
||||
{
|
||||
private readonly LibraryProvider _libraryProvider;
|
||||
|
||||
@@ -6,7 +6,7 @@ using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book list", Description = "List all books in the library.")]
|
||||
[Command("book list", Description = "Lists all books in the library.")]
|
||||
public class BookListCommand : ICommand
|
||||
{
|
||||
private readonly LibraryProvider _libraryProvider;
|
||||
|
||||
@@ -6,7 +6,7 @@ using CliFx.Infrastructure;
|
||||
|
||||
namespace CliFx.Demo.Commands;
|
||||
|
||||
[Command("book remove", Description = "Remove a book from the library.")]
|
||||
[Command("book remove", Description = "Removes a book from the library.")]
|
||||
public class BookRemoveCommand : ICommand
|
||||
{
|
||||
private readonly LibraryProvider _libraryProvider;
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
|
||||
namespace CliFx.Demo.Domain;
|
||||
|
||||
public partial record Isbn(int EanPrefix, int RegistrationGroup, int Registrant, int Publication, int CheckDigit)
|
||||
public partial record Isbn(
|
||||
int EanPrefix,
|
||||
int RegistrationGroup,
|
||||
int Registrant,
|
||||
int Publication,
|
||||
int CheckDigit
|
||||
)
|
||||
{
|
||||
public override string ToString() =>
|
||||
$"{EanPrefix:000}-{RegistrationGroup:00}-{Registrant:00000}-{Publication:00}-{CheckDigit:0}";
|
||||
|
||||
@@ -6,7 +6,8 @@ namespace CliFx.Demo.Domain;
|
||||
|
||||
public class LibraryProvider
|
||||
{
|
||||
private static string StorageFilePath { get; } = Path.Combine(Directory.GetCurrentDirectory(), "Library.json");
|
||||
private static string StorageFilePath { get; } =
|
||||
Path.Combine(Directory.GetCurrentDirectory(), "Library.json");
|
||||
|
||||
private void StoreLibrary(Library library)
|
||||
{
|
||||
@@ -24,7 +25,8 @@ public class LibraryProvider
|
||||
return JsonSerializer.Deserialize<Library>(data) ?? Library.Empty;
|
||||
}
|
||||
|
||||
public Book? TryGetBook(string title) => GetLibrary().Books.FirstOrDefault(b => b.Title == title);
|
||||
public Book? TryGetBook(string title) =>
|
||||
GetLibrary().Books.FirstOrDefault(b => b.Title == title);
|
||||
|
||||
public void AddBook(Book book)
|
||||
{
|
||||
|
||||
@@ -2,9 +2,14 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ApplicationIcon>../favicon.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\CliFx\CliFx.csproj" />
|
||||
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="analyzer" />
|
||||
|
||||
@@ -14,10 +14,7 @@ public class CancellationTestCommand : ICommand
|
||||
{
|
||||
console.Output.WriteLine("Started.");
|
||||
|
||||
await Task.Delay(
|
||||
TimeSpan.FromSeconds(3),
|
||||
console.RegisterCancellationHandler()
|
||||
);
|
||||
await Task.Delay(TimeSpan.FromSeconds(3), console.RegisterCancellationHandler());
|
||||
|
||||
console.Output.WriteLine("Completed.");
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace CliFx.Tests.Dummy;
|
||||
|
||||
// This dummy application is used in tests for scenarios that require an external process to properly verify
|
||||
public static partial class Program
|
||||
public static class Program
|
||||
{
|
||||
public static Assembly Assembly { get; } = Assembly.GetExecutingAssembly();
|
||||
// Path to the apphost
|
||||
public static string FilePath { get; } =
|
||||
Path.ChangeExtension(
|
||||
Assembly.GetExecutingAssembly().Location,
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "exe" : null
|
||||
);
|
||||
|
||||
public static string Location { get; } = Assembly.Location;
|
||||
}
|
||||
|
||||
public static partial class Program
|
||||
{
|
||||
public static async Task Main()
|
||||
{
|
||||
// Make sure color codes are not produced because we rely on the output in tests
|
||||
@@ -22,9 +24,6 @@ public static partial class Program
|
||||
"false"
|
||||
);
|
||||
|
||||
await new CliApplicationBuilder()
|
||||
.AddCommandsFromThisAssembly()
|
||||
.Build()
|
||||
.RunAsync();
|
||||
await new CliApplicationBuilder().AddCommandsFromThisAssembly().Build().RunAsync();
|
||||
}
|
||||
}
|
||||
@@ -11,9 +11,7 @@ namespace CliFx.Tests;
|
||||
public class ApplicationSpecs : SpecsBase
|
||||
{
|
||||
public ApplicationSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_create_an_application_with_the_default_configuration()
|
||||
@@ -24,10 +22,7 @@ public class ApplicationSpecs : SpecsBase
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
var exitCode = await app.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
var exitCode = await app.RunAsync(Array.Empty<string>(), new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
@@ -40,8 +35,8 @@ public class ApplicationSpecs : SpecsBase
|
||||
var app = new CliApplicationBuilder()
|
||||
.AddCommand<NoOpCommand>()
|
||||
.AddCommandsFrom(typeof(NoOpCommand).Assembly)
|
||||
.AddCommands(new[] {typeof(NoOpCommand)})
|
||||
.AddCommandsFrom(new[] {typeof(NoOpCommand).Assembly})
|
||||
.AddCommands(new[] { typeof(NoOpCommand) })
|
||||
.AddCommandsFrom(new[] { typeof(NoOpCommand).Assembly })
|
||||
.AddCommandsFromThisAssembly()
|
||||
.AllowDebugMode()
|
||||
.AllowPreviewMode()
|
||||
@@ -53,17 +48,14 @@ public class ApplicationSpecs : SpecsBase
|
||||
.UseTypeActivator(Activator.CreateInstance!)
|
||||
.Build();
|
||||
|
||||
var exitCode = await app.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
var exitCode = await app.RunAsync(Array.Empty<string>(), new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_cannot_add_an_invalid_command_to_the_application()
|
||||
public async Task I_can_try_to_create_an_application_and_get_an_error_if_it_has_invalid_commands()
|
||||
{
|
||||
// Act
|
||||
var app = new CliApplicationBuilder()
|
||||
@@ -71,10 +63,7 @@ public class ApplicationSpecs : SpecsBase
|
||||
.UseConsole(FakeConsole)
|
||||
.Build();
|
||||
|
||||
var exitCode = await app.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
var exitCode = await app.RunAsync(Array.Empty<string>(), new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().NotBe(0);
|
||||
|
||||
@@ -15,9 +15,7 @@ namespace CliFx.Tests;
|
||||
public class CancellationSpecs : SpecsBase
|
||||
{
|
||||
public CancellationSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact(Timeout = 15000)]
|
||||
public async Task I_can_configure_the_command_to_listen_to_the_interrupt_signal()
|
||||
@@ -41,14 +39,11 @@ public class CancellationSpecs : SpecsBase
|
||||
PipeTarget.ToStringBuilder(stdOutBuffer)
|
||||
);
|
||||
|
||||
var command = Cli.Wrap("dotnet")
|
||||
.WithArguments(a => a
|
||||
.Add(Dummy.Program.Location)
|
||||
.Add("cancel-test")
|
||||
) | pipeTarget;
|
||||
var command = Cli.Wrap(Dummy.Program.FilePath).WithArguments("cancel-test") | pipeTarget;
|
||||
|
||||
// Act & assert
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () =>
|
||||
await Assert.ThrowsAnyAsync<OperationCanceledException>(
|
||||
async () =>
|
||||
await command.ExecuteAsync(
|
||||
// Forceful cancellation (not required because we have a timeout)
|
||||
CancellationToken.None,
|
||||
@@ -57,10 +52,7 @@ public class CancellationSpecs : SpecsBase
|
||||
)
|
||||
);
|
||||
|
||||
stdOutBuffer.ToString().Trim().Should().ConsistOfLines(
|
||||
"Started.",
|
||||
"Cancelled."
|
||||
);
|
||||
stdOutBuffer.ToString().Trim().Should().ConsistOfLines("Started.", "Cancelled.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -68,7 +60,7 @@ public class CancellationSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -113,9 +105,6 @@ public class CancellationSpecs : SpecsBase
|
||||
exitCode.Should().NotBe(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Trim().Should().ConsistOfLines(
|
||||
"Started.",
|
||||
"Cancelled."
|
||||
);
|
||||
stdOut.Trim().Should().ConsistOfLines("Started.", "Cancelled.");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -9,16 +9,18 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.2" />
|
||||
<PackageReference Include="CliWrap" Version="3.6.1" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.11.0" />
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.6.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
|
||||
<PackageReference Include="xunit" Version="2.4.2" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" PrivateAssets="all" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.2.0" PrivateAssets="all" />
|
||||
<PackageReference Include="Basic.Reference.Assemblies.Net70" Version="1.4.5" />
|
||||
<PackageReference Include="CliWrap" Version="3.6.4" />
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0" PrivateAssets="all" />
|
||||
<PackageReference Include="CSharpier.MsBuild" Version="0.26.1" PrivateAssets="all" />
|
||||
<PackageReference Include="FluentAssertions" Version="6.12.0" />
|
||||
<PackageReference Include="GitHubActionsTestLogger" Version="2.3.3" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="PolyShim" Version="1.8.0" PrivateAssets="all" />
|
||||
<PackageReference Include="xunit" Version="2.6.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" PrivateAssets="all" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -17,9 +17,7 @@ namespace CliFx.Tests;
|
||||
public class ConsoleSpecs : SpecsBase
|
||||
{
|
||||
public ConsoleSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact(Timeout = 15000)]
|
||||
public async Task I_can_run_the_application_with_the_default_console_implementation_to_interact_with_the_system_console()
|
||||
@@ -27,11 +25,8 @@ public class ConsoleSpecs : SpecsBase
|
||||
// Can't verify our own console output, so using an external process for this test
|
||||
|
||||
// Arrange
|
||||
var command = "Hello world" | Cli.Wrap("dotnet")
|
||||
.WithArguments(a => a
|
||||
.Add(Dummy.Program.Location)
|
||||
.Add("console-test")
|
||||
);
|
||||
var command =
|
||||
"Hello world" | Cli.Wrap(Dummy.Program.FilePath).WithArguments("console-test");
|
||||
|
||||
// Act
|
||||
var result = await command.ExecuteBufferedAsync();
|
||||
@@ -65,7 +60,7 @@ public class ConsoleSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -126,7 +121,7 @@ public class ConsoleSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -171,7 +166,7 @@ public class ConsoleSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -207,10 +202,6 @@ public class ConsoleSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Trim().Should().ConsistOfLines(
|
||||
"D0",
|
||||
"A",
|
||||
"Backspace"
|
||||
);
|
||||
stdOut.Trim().Should().ConsistOfLines("D0", "A", "Backspace");
|
||||
}
|
||||
}
|
||||
@@ -11,16 +11,14 @@ namespace CliFx.Tests;
|
||||
public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
public ConversionSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_or_an_option_to_a_string_property()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -44,7 +42,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "xyz"},
|
||||
new[] { "-f", "xyz" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -60,7 +58,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -84,7 +82,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "xyz"},
|
||||
new[] { "-f", "xyz" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -100,7 +98,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -133,12 +131,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"-f", "true",
|
||||
"-b", "false",
|
||||
"-c"
|
||||
},
|
||||
new[] { "-f", "true", "-b", "false", "-c" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -146,11 +139,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = True",
|
||||
"Bar = False",
|
||||
"Baz = True"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = True", "Bar = False", "Baz = True");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -158,7 +147,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -182,7 +171,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "32"},
|
||||
new[] { "-f", "32" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -198,7 +187,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -222,7 +211,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "32.14"},
|
||||
new[] { "-f", "32.14" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -238,7 +227,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -262,7 +251,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "1995-04-28Z"},
|
||||
new[] { "-f", "1995-04-28Z" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -278,7 +267,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -302,7 +291,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "12:34:56"},
|
||||
new[] { "-f", "12:34:56" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -318,7 +307,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One = 1, Two = 2, Three = 3 }
|
||||
|
||||
@@ -344,7 +333,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "two"},
|
||||
new[] { "-f", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -360,7 +349,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -389,7 +378,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-b", "123"},
|
||||
new[] { "-b", "123" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -397,10 +386,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = ",
|
||||
"Bar = 123"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = ", "Bar = 123");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -408,7 +394,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One = 1, Two = 2, Three = 3 }
|
||||
|
||||
@@ -439,7 +425,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-b", "two"},
|
||||
new[] { "-b", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -447,10 +433,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = ",
|
||||
"Bar = 2"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = ", "Bar = 2");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -458,7 +441,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class CustomType
|
||||
{
|
||||
@@ -489,7 +472,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "xyz"},
|
||||
new[] { "-f", "xyz" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -505,7 +488,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class CustomTypeA
|
||||
{
|
||||
@@ -554,7 +537,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "hello", "-b", "world"},
|
||||
new[] { "-f", "hello", "-b", "world" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -562,10 +545,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = hello",
|
||||
"Bar = world"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = hello", "Bar = world");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -573,7 +553,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class CustomConverter : BindingConverter<int>
|
||||
{
|
||||
@@ -603,7 +583,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "hello world"},
|
||||
new[] { "-f", "hello world" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -619,7 +599,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -645,7 +625,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "one", "two", "three"},
|
||||
new[] { "-f", "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -653,11 +633,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -665,7 +641,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -691,7 +667,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "one", "two", "three"},
|
||||
new[] { "-f", "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -699,11 +675,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -711,7 +683,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -737,7 +709,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "one", "two", "three"},
|
||||
new[] { "-f", "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -745,11 +717,7 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -757,7 +725,7 @@ public class ConversionSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -783,7 +751,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "1", "13", "27"},
|
||||
new[] { "-f", "1", "13", "27" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -791,19 +759,15 @@ public class ConversionSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"1",
|
||||
"13",
|
||||
"27"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("1", "13", "27");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_cannot_bind_a_parameter_or_an_option_to_a_property_of_an_unsupported_type()
|
||||
public async Task I_can_try_to_bind_a_parameter_or_an_option_to_a_property_and_get_an_error_if_it_is_of_an_unsupported_type()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class CustomType
|
||||
{
|
||||
@@ -827,7 +791,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "xyz"},
|
||||
new[] { "-f", "xyz" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -839,11 +803,11 @@ public class ConversionSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_cannot_bind_a_parameter_or_an_option_to_a_non_scalar_property_of_an_unsupported_type()
|
||||
public async Task I_can_try_to_bind_a_parameter_or_an_option_to_a_non_scalar_property_and_get_an_error_if_it_is_of_an_unsupported_type()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class CustomType : IEnumerable<object>
|
||||
{
|
||||
@@ -870,7 +834,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "one", "two"},
|
||||
new[] { "-f", "one", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -882,11 +846,11 @@ public class ConversionSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_or_an_option_to_a_property_and_get_an_error_if_the_user_provides_an_invalid_value()
|
||||
public async Task I_can_try_to_bind_a_parameter_or_an_option_to_a_property_and_get_an_error_if_the_user_provides_an_invalid_value()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -906,7 +870,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "12.34"},
|
||||
new[] { "-f", "12.34" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -918,11 +882,11 @@ public class ConversionSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_or_an_option_to_a_property_and_get_an_error_if_a_custom_validator_fails()
|
||||
public async Task I_can_try_to_bind_a_parameter_or_an_option_to_a_property_and_get_an_error_if_a_custom_validator_fails()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class ValidatorA : BindingValidator<int>
|
||||
{
|
||||
@@ -952,7 +916,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "12"},
|
||||
new[] { "-f", "12" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -964,11 +928,11 @@ public class ConversionSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_or_an_option_to_a_string_parsable_property_and_get_an_error_if_the_parsing_fails()
|
||||
public async Task I_can_try_to_bind_a_parameter_or_an_option_to_a_string_parsable_property_and_get_an_error_if_the_parsing_fails()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public class CustomType
|
||||
{
|
||||
@@ -997,7 +961,7 @@ public class ConversionSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "bar"},
|
||||
new[] { "-f", "bar" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -14,9 +14,7 @@ namespace CliFx.Tests;
|
||||
public class DirectivesSpecs : SpecsBase
|
||||
{
|
||||
public DirectivesSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact(Timeout = 15000)]
|
||||
public async Task I_can_use_the_debug_directive_to_make_the_application_wait_for_the_debugger_to_attach()
|
||||
@@ -32,11 +30,7 @@ public class DirectivesSpecs : SpecsBase
|
||||
cts.Cancel();
|
||||
}
|
||||
|
||||
var command = Cli.Wrap("dotnet")
|
||||
.WithArguments(a => a
|
||||
.Add(Dummy.Program.Location)
|
||||
.Add("[debug]")
|
||||
) | HandleStdOut;
|
||||
var command = Cli.Wrap(Dummy.Program.FilePath).WithArguments("[debug]") | HandleStdOut;
|
||||
|
||||
// Act & assert
|
||||
try
|
||||
@@ -54,7 +48,7 @@ public class DirectivesSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command("cmd")]
|
||||
public class Command : ICommand
|
||||
@@ -72,22 +66,29 @@ public class DirectivesSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"[preview]", "cmd", "param", "-abc", "--option", "foo"},
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["ENV_QOP"] = "hello",
|
||||
["ENV_KIL"] = "world"
|
||||
}
|
||||
new[] { "[preview]", "cmd", "param", "-abc", "--option", "foo" },
|
||||
new Dictionary<string, string> { ["ENV_QOP"] = "hello", ["ENV_KIL"] = "world" }
|
||||
);
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"cmd", "<param>", "[-a]", "[-b]", "[-c]", "[--option \"foo\"]",
|
||||
"ENV_QOP", "=", "\"hello\"",
|
||||
"ENV_KIL", "=", "\"world\""
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"cmd",
|
||||
"<param>",
|
||||
"[-a]",
|
||||
"[-b]",
|
||||
"[-c]",
|
||||
"[--option \"foo\"]",
|
||||
"ENV_QOP",
|
||||
"=",
|
||||
"\"hello\"",
|
||||
"ENV_KIL",
|
||||
"=",
|
||||
"\"world\""
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -15,16 +15,14 @@ namespace CliFx.Tests;
|
||||
public class EnvironmentSpecs : SpecsBase
|
||||
{
|
||||
public EnvironmentSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_configure_an_option_to_fall_back_to_an_environment_variable_if_the_user_does_not_provide_the_corresponding_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -53,22 +51,15 @@ public class EnvironmentSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo", "42"},
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["ENV_FOO"] = "100",
|
||||
["ENV_BAR"] = "200"
|
||||
}
|
||||
new[] { "--foo", "42" },
|
||||
new Dictionary<string, string> { ["ENV_FOO"] = "100", ["ENV_BAR"] = "200" }
|
||||
);
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Trim().Should().ConsistOfLines(
|
||||
"42",
|
||||
"200"
|
||||
);
|
||||
stdOut.Trim().Should().ConsistOfLines("42", "200");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -76,7 +67,7 @@ public class EnvironmentSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -103,20 +94,14 @@ public class EnvironmentSpecs : SpecsBase
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["ENV_FOO"] = $"bar{Path.PathSeparator}baz"
|
||||
}
|
||||
new Dictionary<string, string> { ["ENV_FOO"] = $"bar{Path.PathSeparator}baz" }
|
||||
);
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"bar",
|
||||
"baz"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("bar", "baz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -124,7 +109,7 @@ public class EnvironmentSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -149,10 +134,7 @@ public class EnvironmentSpecs : SpecsBase
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
Array.Empty<string>(),
|
||||
new Dictionary<string, string>
|
||||
{
|
||||
["ENV_FOO"] = $"bar{Path.PathSeparator}baz"
|
||||
}
|
||||
new Dictionary<string, string> { ["ENV_FOO"] = $"bar{Path.PathSeparator}baz" }
|
||||
);
|
||||
|
||||
// Assert
|
||||
@@ -169,14 +151,9 @@ public class EnvironmentSpecs : SpecsBase
|
||||
// System.Environment when they are not provided explicitly to CliApplication.
|
||||
|
||||
// Arrange
|
||||
var command = Cli.Wrap("dotnet")
|
||||
.WithArguments(a => a
|
||||
.Add(Dummy.Program.Location)
|
||||
.Add("env-test")
|
||||
)
|
||||
.WithEnvironmentVariables(e => e
|
||||
.Set("ENV_TARGET", "Mars")
|
||||
);
|
||||
var command = Cli.Wrap(Dummy.Program.FilePath)
|
||||
.WithArguments("env-test")
|
||||
.WithEnvironmentVariables(e => e.Set("ENV_TARGET", "Mars"));
|
||||
|
||||
// Act
|
||||
var result = await command.ExecuteBufferedAsync();
|
||||
|
||||
@@ -12,16 +12,14 @@ namespace CliFx.Tests;
|
||||
public class ErrorReportingSpecs : SpecsBase
|
||||
{
|
||||
public ErrorReportingSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_throw_an_exception_in_a_command_to_report_an_error_with_a_stacktrace()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -50,10 +48,9 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
stdOut.Should().BeEmpty();
|
||||
|
||||
var stdErr = FakeConsole.ReadErrorString();
|
||||
stdErr.Should().ContainAllInOrder(
|
||||
"System.Exception", "Something went wrong",
|
||||
"at", "CliFx."
|
||||
);
|
||||
stdErr
|
||||
.Should()
|
||||
.ContainAllInOrder("System.Exception", "Something went wrong", "at", "CliFx.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -61,7 +58,7 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -90,10 +87,15 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
stdOut.Should().BeEmpty();
|
||||
|
||||
var stdErr = FakeConsole.ReadErrorString();
|
||||
stdErr.Should().ContainAllInOrder(
|
||||
"System.Exception", "Something went wrong",
|
||||
"System.Exception", "Another exception",
|
||||
"at", "CliFx."
|
||||
stdErr
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"System.Exception",
|
||||
"Something went wrong",
|
||||
"System.Exception",
|
||||
"Another exception",
|
||||
"at",
|
||||
"CliFx."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -139,7 +141,7 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -168,10 +170,7 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
stdOut.Should().BeEmpty();
|
||||
|
||||
var stdErr = FakeConsole.ReadErrorString();
|
||||
stdErr.Should().ContainAllInOrder(
|
||||
"CliFx.Exceptions.CommandException",
|
||||
"at", "CliFx."
|
||||
);
|
||||
stdErr.Should().ContainAllInOrder("CliFx.Exceptions.CommandException", "at", "CliFx.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -179,7 +178,7 @@ public class ErrorReportingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
|
||||
@@ -12,9 +12,7 @@ namespace CliFx.Tests;
|
||||
public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
public HelpTextSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_request_the_help_text_by_running_the_application_without_arguments_if_the_default_command_is_not_defined()
|
||||
@@ -43,7 +41,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -61,7 +59,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -77,7 +75,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command("cmd")]
|
||||
public class NamedCommand : ICommand
|
||||
@@ -101,7 +99,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -117,7 +115,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -146,7 +144,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"cmd", "--help"},
|
||||
new[] { "cmd", "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -162,7 +160,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -191,7 +189,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"cmd", "sub", "--help"},
|
||||
new[] { "cmd", "sub", "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -214,7 +212,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"invalid-command", "--invalid-option"},
|
||||
new[] { "invalid-command", "--invalid-option" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -241,7 +239,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -249,11 +247,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAll(
|
||||
"App title",
|
||||
"App description",
|
||||
"App version"
|
||||
);
|
||||
stdOut.Should().ContainAll("App title", "App description", "App version");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -261,7 +255,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command(Description = "Description of the default command.")]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -278,7 +272,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -286,10 +280,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"DESCRIPTION",
|
||||
"Description of the default command."
|
||||
);
|
||||
stdOut.Should().ContainAllInOrder("DESCRIPTION", "Description of the default command.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -297,7 +288,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -320,7 +311,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -328,10 +319,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"USAGE",
|
||||
"[command]", "[...]"
|
||||
);
|
||||
stdOut.Should().ContainAllInOrder("USAGE", "[command]", "[...]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -339,7 +327,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -365,7 +353,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -373,10 +361,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"USAGE",
|
||||
"<foo>", "<bar>", "<baz...>"
|
||||
);
|
||||
stdOut.Should().ContainAllInOrder("USAGE", "<foo>", "<bar>", "<baz...>");
|
||||
}
|
||||
|
||||
// https://github.com/Tyrrrz/CliFx/issues/117
|
||||
@@ -385,7 +370,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
// Base members appear last in reflection order
|
||||
public abstract class CommandBase : ICommand
|
||||
@@ -425,10 +410,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"USAGE",
|
||||
"<foo>", "<bar>", "<baz...>"
|
||||
);
|
||||
stdOut.Should().ContainAllInOrder("USAGE", "<foo>", "<bar>", "<baz...>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -436,7 +418,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -462,7 +444,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -470,10 +452,9 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"USAGE",
|
||||
"--foo <value>", "--baz <values...>", "[options]"
|
||||
);
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder("USAGE", "--foo <value>", "--baz <values...>", "[options]");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -481,7 +462,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -504,7 +485,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -512,11 +493,15 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"PARAMETERS",
|
||||
"foo", "Description of foo.",
|
||||
"foo",
|
||||
"Description of foo.",
|
||||
"OPTIONS",
|
||||
"--bar", "Description of bar."
|
||||
"--bar",
|
||||
"Description of bar."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -525,7 +510,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -542,7 +527,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -550,10 +535,15 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"OPTIONS",
|
||||
"-h", "--help", "Shows help text",
|
||||
"--version", "Shows version information"
|
||||
"-h",
|
||||
"--help",
|
||||
"Shows help text",
|
||||
"--version",
|
||||
"Shows version information"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -562,7 +552,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command("cmd")]
|
||||
public class Command : ICommand
|
||||
@@ -579,7 +569,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"cmd", "--help"},
|
||||
new[] { "cmd", "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -588,14 +578,9 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
"OPTIONS",
|
||||
"-h", "--help", "Shows help text"
|
||||
);
|
||||
stdOut.Should().ContainAllInOrder("OPTIONS", "-h", "--help", "Shows help text");
|
||||
|
||||
stdOut.Should().NotContainAny(
|
||||
"--version", "Shows version information"
|
||||
);
|
||||
stdOut.Should().NotContainAny("--version", "Shows version information");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -603,7 +588,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One, Two, Three }
|
||||
|
||||
@@ -628,7 +613,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -636,11 +621,21 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"PARAMETERS",
|
||||
"foo", "Choices:", "One", "Two", "Three",
|
||||
"foo",
|
||||
"Choices:",
|
||||
"One",
|
||||
"Two",
|
||||
"Three",
|
||||
"OPTIONS",
|
||||
"--bar", "Choices:", "One", "Two", "Three"
|
||||
"--bar",
|
||||
"Choices:",
|
||||
"One",
|
||||
"Two",
|
||||
"Three"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -649,7 +644,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One, Two, Three }
|
||||
|
||||
@@ -674,7 +669,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -682,11 +677,21 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"PARAMETERS",
|
||||
"foo", "Choices:", "One", "Two", "Three",
|
||||
"foo",
|
||||
"Choices:",
|
||||
"One",
|
||||
"Two",
|
||||
"Three",
|
||||
"OPTIONS",
|
||||
"--bar", "Choices:", "One", "Two", "Three"
|
||||
"--bar",
|
||||
"Choices:",
|
||||
"One",
|
||||
"Two",
|
||||
"Three"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -695,7 +700,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One, Two, Three }
|
||||
|
||||
@@ -720,7 +725,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -728,11 +733,21 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"PARAMETERS",
|
||||
"foo", "Choices:", "One", "Two", "Three",
|
||||
"foo",
|
||||
"Choices:",
|
||||
"One",
|
||||
"Two",
|
||||
"Three",
|
||||
"OPTIONS",
|
||||
"--bar", "Choices:", "One", "Two", "Three"
|
||||
"--bar",
|
||||
"Choices:",
|
||||
"One",
|
||||
"Two",
|
||||
"Three"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -741,7 +756,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One, Two, Three }
|
||||
|
||||
@@ -766,7 +781,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -774,10 +789,16 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"OPTIONS",
|
||||
"--foo", "Environment variable:", "ENV_FOO",
|
||||
"--bar", "Environment variable:", "ENV_BAR"
|
||||
"--foo",
|
||||
"Environment variable:",
|
||||
"ENV_FOO",
|
||||
"--bar",
|
||||
"Environment variable:",
|
||||
"ENV_BAR"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -786,7 +807,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public enum CustomEnum { One, Two, Three }
|
||||
|
||||
@@ -829,7 +850,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -838,15 +859,33 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"OPTIONS",
|
||||
"--foo", "Default:", "42",
|
||||
"--bar", "Default:", "hello",
|
||||
"--baz", "Default:", "one", "two", "three",
|
||||
"--qwe", "Default:", "True",
|
||||
"--qop", "Default:", "1337",
|
||||
"--zor", "Default:", "02:03:00",
|
||||
"--lol", "Default:", "Two"
|
||||
"--foo",
|
||||
"Default:",
|
||||
"42",
|
||||
"--bar",
|
||||
"Default:",
|
||||
"hello",
|
||||
"--baz",
|
||||
"Default:",
|
||||
"one",
|
||||
"two",
|
||||
"three",
|
||||
"--qwe",
|
||||
"Default:",
|
||||
"True",
|
||||
"--qop",
|
||||
"Default:",
|
||||
"1337",
|
||||
"--zor",
|
||||
"Default:",
|
||||
"02:03:00",
|
||||
"--lol",
|
||||
"Default:",
|
||||
"Two"
|
||||
);
|
||||
|
||||
stdOut.Should().NotContain("not printed");
|
||||
@@ -857,7 +896,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command("cmd1", Description = "Description of one command.")]
|
||||
public class FirstCommand : ICommand
|
||||
@@ -892,7 +931,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -901,20 +940,23 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"COMMANDS",
|
||||
"cmd1", "Description of one command.",
|
||||
"cmd2", "Description of another command.",
|
||||
"cmd1",
|
||||
"Description of one command.",
|
||||
"cmd2",
|
||||
"Description of another command.",
|
||||
// `cmd2 child` will appear as an immediate command because it does not
|
||||
// have a more specific parent.
|
||||
"cmd3 child", "Description of an orphaned command."
|
||||
"cmd3 child",
|
||||
"Description of an orphaned command."
|
||||
);
|
||||
|
||||
// `cmd2 child` will still appear in the list of `cmd2` subcommands,
|
||||
// but its description will not be visible.
|
||||
stdOut.Should().NotContain(
|
||||
"Description of another command's child command."
|
||||
);
|
||||
stdOut.Should().NotContain("Description of another command's child command.");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -922,7 +964,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command("cmd1")]
|
||||
public class FirstCommand : ICommand
|
||||
@@ -963,7 +1005,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--help"},
|
||||
new[] { "--help" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -971,10 +1013,17 @@ public class HelpTextSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ContainAllInOrder(
|
||||
stdOut
|
||||
.Should()
|
||||
.ContainAllInOrder(
|
||||
"COMMANDS",
|
||||
"cmd1", "Subcommands:", "cmd1 child1",
|
||||
"cmd2", "Subcommands:", "cmd2 child1", "cmd2 child2"
|
||||
"cmd1",
|
||||
"Subcommands:",
|
||||
"cmd1 child1",
|
||||
"cmd2",
|
||||
"Subcommands:",
|
||||
"cmd2 child1",
|
||||
"cmd2 child2"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -990,7 +1039,7 @@ public class HelpTextSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--version"},
|
||||
new[] { "--version" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -12,16 +12,14 @@ namespace CliFx.Tests;
|
||||
public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
public OptionBindingSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_an_option_to_a_property_and_get_the_value_from_the_corresponding_argument_by_name()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -45,7 +43,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo"},
|
||||
new[] { "--foo" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -61,7 +59,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -84,10 +82,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f"},
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
var exitCode = await application.RunAsync(new[] { "-f" }, new Dictionary<string, string>());
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
@@ -101,7 +96,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -130,11 +125,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"--foo", "one",
|
||||
"--bar", "two"
|
||||
},
|
||||
new[] { "--foo", "one", "--bar", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -142,10 +133,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = one",
|
||||
"Bar = two"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = one", "Bar = two");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -153,7 +141,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -182,11 +170,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"-f", "one",
|
||||
"-b", "two"
|
||||
},
|
||||
new[] { "-f", "one", "-b", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -194,10 +178,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = one",
|
||||
"Bar = two"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = one", "Bar = two");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -205,7 +186,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -234,7 +215,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-fb", "value"},
|
||||
new[] { "-fb", "value" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -242,10 +223,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = ",
|
||||
"Bar = value"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = ", "Bar = value");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -253,7 +231,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -279,7 +257,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo", "one", "two", "three"},
|
||||
new[] { "--foo", "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -287,11 +265,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -299,7 +273,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -325,7 +299,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"-f", "one", "two", "three"},
|
||||
new[] { "-f", "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -333,11 +307,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -345,7 +315,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -371,12 +341,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"--foo", "one",
|
||||
"--foo", "two",
|
||||
"--foo", "three"
|
||||
},
|
||||
new[] { "--foo", "one", "--foo", "two", "--foo", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -384,11 +349,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -396,7 +357,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -422,12 +383,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"-f", "one",
|
||||
"-f", "two",
|
||||
"-f", "three"
|
||||
},
|
||||
new[] { "-f", "one", "-f", "two", "-f", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -435,11 +391,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -447,7 +399,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -473,12 +425,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"--foo", "one",
|
||||
"-f", "two",
|
||||
"--foo", "three"
|
||||
},
|
||||
new[] { "--foo", "one", "-f", "two", "--foo", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -486,11 +433,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"one",
|
||||
"two",
|
||||
"three"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("one", "two", "three");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -498,7 +441,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -527,7 +470,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo", "one"},
|
||||
new[] { "--foo", "one" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -535,10 +478,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = one",
|
||||
"Bar = hello"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = one", "Bar = hello");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -546,7 +486,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
public static class SharedContext
|
||||
{
|
||||
@@ -604,24 +544,13 @@ public class OptionBindingSpecs : SpecsBase
|
||||
.Build();
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"--foo", "42",
|
||||
"--bar",
|
||||
"--baz", "xyz"
|
||||
}
|
||||
);
|
||||
var exitCode = await application.RunAsync(new[] { "--foo", "42", "--bar", "--baz", "xyz" });
|
||||
|
||||
// Assert
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = 42",
|
||||
"Bar = True",
|
||||
"Baz = xyz"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = 42", "Bar = True", "Baz = xyz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -629,7 +558,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -653,7 +582,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo", "-13"},
|
||||
new[] { "--foo", "-13" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -665,11 +594,11 @@ public class OptionBindingSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_required_option_to_a_property_and_get_an_error_if_the_user_does_not_provide_the_corresponding_argument()
|
||||
public async Task I_can_try_to_bind_a_required_option_to_a_property_and_get_an_error_if_the_user_does_not_provide_the_corresponding_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -701,11 +630,11 @@ public class OptionBindingSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_required_option_to_a_property_and_get_an_error_if_the_user_provides_an_empty_argument()
|
||||
public async Task I_can_try_to_bind_a_required_option_to_a_property_and_get_an_error_if_the_user_provides_an_empty_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -725,7 +654,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo"},
|
||||
new[] { "--foo" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -737,11 +666,11 @@ public class OptionBindingSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_an_option_to_a_non_scalar_property_and_get_an_error_if_the_user_does_not_provide_at_least_one_corresponding_argument()
|
||||
public async Task I_can_try_to_bind_an_option_to_a_non_scalar_property_and_get_an_error_if_the_user_does_not_provide_at_least_one_corresponding_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -761,7 +690,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo"},
|
||||
new[] { "--foo" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -773,11 +702,11 @@ public class OptionBindingSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_options_and_get_an_error_if_the_user_provides_unrecognized_arguments()
|
||||
public async Task I_can_try_to_bind_options_and_get_an_error_if_the_user_provides_unrecognized_arguments()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -797,11 +726,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[]
|
||||
{
|
||||
"--foo", "one",
|
||||
"--bar", "two"
|
||||
},
|
||||
new[] { "--foo", "one", "--bar", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -813,11 +738,11 @@ public class OptionBindingSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_an_option_to_a_scalar_property_and_get_an_error_if_the_user_provides_too_many_arguments()
|
||||
public async Task I_can_try_to_bind_an_option_to_a_scalar_property_and_get_an_error_if_the_user_provides_too_many_arguments()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -837,7 +762,7 @@ public class OptionBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"--foo", "one", "two", "three"},
|
||||
new[] { "--foo", "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -11,16 +11,14 @@ namespace CliFx.Tests;
|
||||
public class ParameterBindingSpecs : SpecsBase
|
||||
{
|
||||
public ParameterBindingSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_to_a_property_and_get_the_value_from_the_corresponding_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -49,7 +47,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"one", "two"},
|
||||
new[] { "one", "two" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -57,10 +55,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = one",
|
||||
"Bar = two"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = one", "Bar = two");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -68,7 +63,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -106,7 +101,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"one", "two", "three", "four", "five", "--boo", "xxx"},
|
||||
new[] { "one", "two", "three", "four", "five", "--boo", "xxx" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -114,21 +109,17 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = one",
|
||||
"Bar = two",
|
||||
"Baz = three",
|
||||
"Baz = four",
|
||||
"Baz = five"
|
||||
);
|
||||
stdOut
|
||||
.Should()
|
||||
.ConsistOfLines("Foo = one", "Bar = two", "Baz = three", "Baz = four", "Baz = five");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_to_a_property_and_get_an_error_if_the_user_does_not_provide_the_corresponding_argument()
|
||||
public async Task I_can_try_to_bind_a_parameter_to_a_property_and_get_an_error_if_the_user_does_not_provide_the_corresponding_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -151,7 +142,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"one"},
|
||||
new[] { "one" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -163,11 +154,11 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_a_parameter_to_a_non_scalar_property_and_get_an_error_if_the_user_does_not_provide_at_least_one_corresponding_argument()
|
||||
public async Task I_can_try_to_bind_a_parameter_to_a_non_scalar_property_and_get_an_error_if_the_user_does_not_provide_at_least_one_corresponding_argument()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -190,7 +181,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"one"},
|
||||
new[] { "one" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -206,7 +197,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -235,7 +226,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"abc"},
|
||||
new[] { "abc" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -243,18 +234,15 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
exitCode.Should().Be(0);
|
||||
|
||||
var stdOut = FakeConsole.ReadOutputString();
|
||||
stdOut.Should().ConsistOfLines(
|
||||
"Foo = abc",
|
||||
"Bar = xyz"
|
||||
);
|
||||
stdOut.Should().ConsistOfLines("Foo = abc", "Bar = xyz");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_bind_parameters_and_get_an_error_if_the_user_provides_too_many_arguments()
|
||||
public async Task I_can_try_to_bind_parameters_and_get_an_error_if_the_user_provides_too_many_arguments()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -277,7 +265,7 @@ public class ParameterBindingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"one", "two", "three"},
|
||||
new[] { "one", "two", "three" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -11,16 +11,14 @@ namespace CliFx.Tests;
|
||||
public class RoutingSpecs : SpecsBase
|
||||
{
|
||||
public RoutingSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_configure_a_command_to_be_executed_by_default_when_the_user_does_not_specify_a_command_name()
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -77,7 +75,7 @@ public class RoutingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -118,7 +116,7 @@ public class RoutingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"cmd"},
|
||||
new[] { "cmd" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
@@ -134,7 +132,7 @@ public class RoutingSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandTypes = DynamicCommandBuilder.CompileMany(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class DefaultCommand : ICommand
|
||||
@@ -175,7 +173,7 @@ public class RoutingSpecs : SpecsBase
|
||||
|
||||
// Act
|
||||
var exitCode = await application.RunAsync(
|
||||
new[] {"cmd", "child"},
|
||||
new[] { "cmd", "child" },
|
||||
new Dictionary<string, string>()
|
||||
);
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@ public abstract class SpecsBase : IDisposable
|
||||
|
||||
public FakeInMemoryConsole FakeConsole { get; } = new();
|
||||
|
||||
protected SpecsBase(ITestOutputHelper testOutput) =>
|
||||
TestOutput = testOutput;
|
||||
protected SpecsBase(ITestOutputHelper testOutput) => TestOutput = testOutput;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
|
||||
@@ -13,16 +13,14 @@ namespace CliFx.Tests;
|
||||
public class TypeActivationSpecs : SpecsBase
|
||||
{
|
||||
public TypeActivationSpecs(ITestOutputHelper testOutput)
|
||||
: base(testOutput)
|
||||
{
|
||||
}
|
||||
: base(testOutput) { }
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_configure_the_application_to_use_the_default_type_activator_to_initialize_types_through_parameterless_constructors()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -56,11 +54,11 @@ public class TypeActivationSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_configure_the_application_to_use_the_default_type_activator_and_get_an_error_if_the_requested_type_does_not_have_a_parameterless_constructor()
|
||||
public async Task I_can_try_to_configure_the_application_to_use_the_default_type_activator_and_get_an_error_if_the_requested_type_does_not_have_a_parameterless_constructor()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -96,7 +94,7 @@ public class TypeActivationSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -138,7 +136,7 @@ public class TypeActivationSpecs : SpecsBase
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
@@ -189,11 +187,11 @@ public class TypeActivationSpecs : SpecsBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task I_can_configure_the_application_to_use_a_custom_type_activator_and_get_an_error_if_the_requested_type_cannot_be_initialized()
|
||||
public async Task I_can_try_to_configure_the_application_to_use_a_custom_type_activator_and_get_an_error_if_the_requested_type_cannot_be_initialized()
|
||||
{
|
||||
// Arrange
|
||||
var commandType = DynamicCommandBuilder.Compile(
|
||||
// language=cs
|
||||
// lang=csharp
|
||||
"""
|
||||
[Command]
|
||||
public class Command : ICommand
|
||||
|
||||
@@ -41,10 +41,10 @@ internal static class DynamicCommandBuilder
|
||||
|
||||
// Append default imports to the source code
|
||||
var sourceCodeWithUsings =
|
||||
string.Join(Environment.NewLine, defaultSystemNamespaces.Select(n => $"using {n};")) +
|
||||
string.Join(Environment.NewLine, defaultCliFxNamespaces.Select(n => $"using {n};")) +
|
||||
Environment.NewLine +
|
||||
sourceCode;
|
||||
string.Join(Environment.NewLine, defaultSystemNamespaces.Select(n => $"using {n};"))
|
||||
+ string.Join(Environment.NewLine, defaultCliFxNamespaces.Select(n => $"using {n};"))
|
||||
+ Environment.NewLine
|
||||
+ sourceCode;
|
||||
|
||||
// Parse the source code
|
||||
var ast = SyntaxFactory.ParseSyntaxTree(
|
||||
@@ -55,10 +55,16 @@ internal static class DynamicCommandBuilder
|
||||
// Compile the code to IL
|
||||
var compilation = CSharpCompilation.Create(
|
||||
"CliFxTests_DynamicAssembly_" + Guid.NewGuid(),
|
||||
new[] {ast},
|
||||
Net70.References.All
|
||||
new[] { ast },
|
||||
Net70
|
||||
.References
|
||||
.All
|
||||
.Append(MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location))
|
||||
.Append(MetadataReference.CreateFromFile(typeof(DynamicCommandBuilder).Assembly.Location)),
|
||||
.Append(
|
||||
MetadataReference.CreateFromFile(
|
||||
typeof(DynamicCommandBuilder).Assembly.Location
|
||||
)
|
||||
),
|
||||
// DLL to avoid having to define the Main() method
|
||||
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
|
||||
);
|
||||
@@ -82,8 +88,7 @@ internal static class DynamicCommandBuilder
|
||||
using var buffer = new MemoryStream();
|
||||
var emit = compilation.Emit(buffer);
|
||||
|
||||
var emitErrors = emit
|
||||
.Diagnostics
|
||||
var emitErrors = emit.Diagnostics
|
||||
.Where(d => d.Severity >= DiagnosticSeverity.Error)
|
||||
.ToArray();
|
||||
|
||||
|
||||
@@ -10,20 +10,21 @@ internal static class AssertionExtensions
|
||||
{
|
||||
public static void ConsistOfLines(
|
||||
this StringAssertions assertions,
|
||||
IEnumerable<string> lines)
|
||||
{
|
||||
var actualLines = assertions.Subject.Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
|
||||
actualLines.Should().Equal(lines);
|
||||
}
|
||||
IEnumerable<string> lines
|
||||
) =>
|
||||
assertions
|
||||
.Subject
|
||||
.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Should()
|
||||
.Equal(lines);
|
||||
|
||||
public static void ConsistOfLines(
|
||||
this StringAssertions assertions,
|
||||
params string[] lines) =>
|
||||
assertions.ConsistOfLines((IEnumerable<string>) lines);
|
||||
public static void ConsistOfLines(this StringAssertions assertions, params string[] lines) =>
|
||||
assertions.ConsistOfLines((IEnumerable<string>)lines);
|
||||
|
||||
public static AndConstraint<StringAssertions> ContainAllInOrder(
|
||||
this StringAssertions assertions,
|
||||
IEnumerable<string> values)
|
||||
IEnumerable<string> values
|
||||
)
|
||||
{
|
||||
var lastIndex = 0;
|
||||
|
||||
@@ -33,7 +34,9 @@ internal static class AssertionExtensions
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
Execute.Assertion.FailWith(
|
||||
Execute
|
||||
.Assertion
|
||||
.FailWith(
|
||||
$"Expected string '{assertions.Subject}' to contain '{value}' after position {lastIndex}."
|
||||
);
|
||||
}
|
||||
@@ -46,6 +49,6 @@ internal static class AssertionExtensions
|
||||
|
||||
public static AndConstraint<StringAssertions> ContainAllInOrder(
|
||||
this StringAssertions assertions,
|
||||
params string[] values) =>
|
||||
assertions.ContainAllInOrder((IEnumerable<string>) values);
|
||||
params string[] values
|
||||
) => assertions.ContainAllInOrder((IEnumerable<string>)values);
|
||||
}
|
||||
@@ -5,12 +5,15 @@ namespace CliFx.Tests.Utils.Extensions;
|
||||
|
||||
internal static class ConsoleExtensions
|
||||
{
|
||||
public static void DumpToTestOutput(this FakeInMemoryConsole console, ITestOutputHelper testOutputHelper)
|
||||
public static void DumpToTestOutput(
|
||||
this FakeInMemoryConsole console,
|
||||
ITestOutputHelper testOutput
|
||||
)
|
||||
{
|
||||
testOutputHelper.WriteLine("[*] Captured standard output:");
|
||||
testOutputHelper.WriteLine(console.ReadOutputString());
|
||||
testOutput.WriteLine("[*] Captured standard output:");
|
||||
testOutput.WriteLine(console.ReadOutputString());
|
||||
|
||||
testOutputHelper.WriteLine("[*] Captured standard error:");
|
||||
testOutputHelper.WriteLine(console.ReadErrorString());
|
||||
testOutput.WriteLine("[*] Captured standard error:");
|
||||
testOutput.WriteLine(console.ReadErrorString());
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,8 @@ public class ApplicationConfiguration
|
||||
public ApplicationConfiguration(
|
||||
IReadOnlyList<Type> commandTypes,
|
||||
bool isDebugModeAllowed,
|
||||
bool isPreviewModeAllowed)
|
||||
bool isPreviewModeAllowed
|
||||
)
|
||||
{
|
||||
CommandTypes = commandTypes;
|
||||
IsDebugModeAllowed = isDebugModeAllowed;
|
||||
|
||||
@@ -32,7 +32,8 @@ public class ApplicationMetadata
|
||||
string title,
|
||||
string executableName,
|
||||
string version,
|
||||
string? description)
|
||||
string? description
|
||||
)
|
||||
{
|
||||
Title = title;
|
||||
ExecutableName = executableName;
|
||||
|
||||
@@ -35,7 +35,5 @@ public sealed class CommandAttribute : Attribute
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandAttribute" />.
|
||||
/// </summary>
|
||||
public CommandAttribute()
|
||||
{
|
||||
}
|
||||
public CommandAttribute() { }
|
||||
}
|
||||
@@ -81,23 +81,17 @@ public sealed class CommandOptionAttribute : Attribute
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandOptionAttribute(string name, char shortName)
|
||||
: this(name, (char?)shortName)
|
||||
{
|
||||
}
|
||||
: this(name, (char?)shortName) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandOptionAttribute(string name)
|
||||
: this(name, null)
|
||||
{
|
||||
}
|
||||
: this(name, null) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes an instance of <see cref="CommandOptionAttribute" />.
|
||||
/// </summary>
|
||||
public CommandOptionAttribute(char shortName)
|
||||
: this(null, (char?)shortName)
|
||||
{
|
||||
}
|
||||
: this(null, (char?)shortName) { }
|
||||
}
|
||||
@@ -41,7 +41,8 @@ public class CliApplication
|
||||
ApplicationMetadata metadata,
|
||||
ApplicationConfiguration configuration,
|
||||
IConsole console,
|
||||
ITypeActivator typeActivator)
|
||||
ITypeActivator typeActivator
|
||||
)
|
||||
{
|
||||
Metadata = metadata;
|
||||
Configuration = configuration;
|
||||
@@ -58,9 +59,11 @@ public class CliApplication
|
||||
Configuration.IsPreviewModeAllowed && commandInput.IsPreviewDirectiveSpecified;
|
||||
|
||||
private bool ShouldShowHelpText(CommandSchema commandSchema, CommandInput commandInput) =>
|
||||
commandSchema.IsHelpOptionAvailable && commandInput.IsHelpOptionSpecified ||
|
||||
commandSchema.IsHelpOptionAvailable && commandInput.IsHelpOptionSpecified
|
||||
||
|
||||
// Show help text also if the fallback default command is executed without any arguments
|
||||
commandSchema == FallbackDefaultCommand.Schema && !commandInput.HasArguments;
|
||||
commandSchema == FallbackDefaultCommand.Schema
|
||||
&& !commandInput.HasArguments;
|
||||
|
||||
private bool ShouldShowVersionText(CommandSchema commandSchema, CommandInput commandInput) =>
|
||||
commandSchema.IsVersionOptionAvailable && commandInput.IsVersionOptionSpecified;
|
||||
@@ -69,10 +72,10 @@ public class CliApplication
|
||||
{
|
||||
using (_console.WithForegroundColor(ConsoleColor.Green))
|
||||
{
|
||||
var processId = ProcessEx.GetCurrentProcessId();
|
||||
|
||||
_console.Output.WriteLine(
|
||||
$"Attach the debugger to process with ID {processId} to continue."
|
||||
_console
|
||||
.Output
|
||||
.WriteLine(
|
||||
$"Attach the debugger to process with ID {ProcessEx.GetCurrentProcessId()} to continue."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -83,7 +86,10 @@ public class CliApplication
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
private async ValueTask<int> RunAsync(ApplicationSchema applicationSchema, CommandInput commandInput)
|
||||
private async ValueTask<int> RunAsync(
|
||||
ApplicationSchema applicationSchema,
|
||||
CommandInput commandInput
|
||||
)
|
||||
{
|
||||
// Console colors may have already been overridden by the parent process,
|
||||
// so we need to reset it to make sure that everything we write looks properly.
|
||||
@@ -104,19 +110,23 @@ public class CliApplication
|
||||
|
||||
// Try to get the command schema that matches the input
|
||||
var commandSchema =
|
||||
(!string.IsNullOrWhiteSpace(commandInput.CommandName)
|
||||
(
|
||||
!string.IsNullOrWhiteSpace(commandInput.CommandName)
|
||||
// If the command name is specified, try to find the command by name.
|
||||
// This should always succeed, because the input parsing relies on
|
||||
// the list of available command names.
|
||||
? applicationSchema.TryFindCommand(commandInput.CommandName)
|
||||
// Otherwise, try to find the default command
|
||||
: applicationSchema.TryFindDefaultCommand()) ??
|
||||
: applicationSchema.TryFindDefaultCommand()
|
||||
)
|
||||
??
|
||||
// If a valid command was not found, use the fallback default command.
|
||||
// This is only used as a stub to show the help text.
|
||||
FallbackDefaultCommand.Schema;
|
||||
|
||||
// Initialize an instance of the command type
|
||||
var commandInstance = commandSchema == FallbackDefaultCommand.Schema
|
||||
var commandInstance =
|
||||
commandSchema == FallbackDefaultCommand.Schema
|
||||
? new FallbackDefaultCommand() // bypass the activator
|
||||
: _typeActivator.CreateInstance<ICommand>(commandSchema.Type);
|
||||
|
||||
@@ -178,7 +188,8 @@ public class CliApplication
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync(
|
||||
IReadOnlyList<string> commandLineArguments,
|
||||
IReadOnlyDictionary<string, string> environmentVariables)
|
||||
IReadOnlyDictionary<string, string> environmentVariables
|
||||
)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -213,15 +224,12 @@ public class CliApplication
|
||||
/// When running WITHOUT the debugger attached (i.e. in production), this method swallows
|
||||
/// all exceptions and reports them to the console.
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync(IReadOnlyList<string> commandLineArguments) => await RunAsync(
|
||||
public async ValueTask<int> RunAsync(IReadOnlyList<string> commandLineArguments) =>
|
||||
await RunAsync(
|
||||
commandLineArguments,
|
||||
Environment
|
||||
.GetEnvironmentVariables()
|
||||
.ToDictionary<string, string>(
|
||||
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
|
||||
? StringComparer.OrdinalIgnoreCase
|
||||
: StringComparer.Ordinal
|
||||
)
|
||||
.ToDictionary<string, string>(StringComparer.Ordinal)
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
@@ -233,8 +241,10 @@ public class CliApplication
|
||||
/// When running WITHOUT the debugger attached (i.e. in production), this method swallows
|
||||
/// all exceptions and reports them to the console.
|
||||
/// </remarks>
|
||||
public async ValueTask<int> RunAsync() => await RunAsync(
|
||||
Environment.GetCommandLineArgs()
|
||||
public async ValueTask<int> RunAsync() =>
|
||||
await RunAsync(
|
||||
Environment
|
||||
.GetCommandLineArgs()
|
||||
.Skip(1) // first element is the file path
|
||||
.ToArray()
|
||||
);
|
||||
|
||||
@@ -39,8 +39,8 @@ public partial class CliApplicationBuilder
|
||||
/// <summary>
|
||||
/// Adds a command to the application.
|
||||
/// </summary>
|
||||
public CliApplicationBuilder AddCommand<TCommand>() where TCommand : ICommand =>
|
||||
AddCommand(typeof(TCommand));
|
||||
public CliApplicationBuilder AddCommand<TCommand>()
|
||||
where TCommand : ICommand => AddCommand(typeof(TCommand));
|
||||
|
||||
/// <summary>
|
||||
/// Adds multiple commands to the application.
|
||||
@@ -62,7 +62,9 @@ public partial class CliApplicationBuilder
|
||||
/// </remarks>
|
||||
public CliApplicationBuilder AddCommandsFrom(Assembly commandAssembly)
|
||||
{
|
||||
foreach (var commandType in commandAssembly.ExportedTypes.Where(CommandSchema.IsCommandType))
|
||||
foreach (
|
||||
var commandType in commandAssembly.ExportedTypes.Where(CommandSchema.IsCommandType)
|
||||
)
|
||||
AddCommand(commandType);
|
||||
|
||||
return this;
|
||||
@@ -90,7 +92,8 @@ public partial class CliApplicationBuilder
|
||||
/// This method looks for public non-abstract classes that implement <see cref="ICommand" />
|
||||
/// and are annotated by <see cref="CommandAttribute" />.
|
||||
/// </remarks>
|
||||
public CliApplicationBuilder AddCommandsFromThisAssembly() => AddCommandsFrom(Assembly.GetCallingAssembly());
|
||||
public CliApplicationBuilder AddCommandsFromThisAssembly() =>
|
||||
AddCommandsFrom(Assembly.GetCallingAssembly());
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether debug mode (enabled with the [debug] directive) is allowed in the application.
|
||||
@@ -190,8 +193,9 @@ public partial class CliApplicationBuilder
|
||||
/// This method takes a delegate that receives the list of all added command types, so that you can
|
||||
/// easily register them with the service provider.
|
||||
/// </summary>
|
||||
public CliApplicationBuilder UseTypeActivator(Func<IReadOnlyList<Type>, IServiceProvider> getServiceProvider) =>
|
||||
UseTypeActivator(getServiceProvider(_commandTypes.ToArray()));
|
||||
public CliApplicationBuilder UseTypeActivator(
|
||||
Func<IReadOnlyList<Type>, IServiceProvider> getServiceProvider
|
||||
) => UseTypeActivator(getServiceProvider(_commandTypes.ToArray()));
|
||||
|
||||
/// <summary>
|
||||
/// Creates a configured instance of <see cref="CliApplication" />.
|
||||
@@ -228,8 +232,8 @@ public partial class CliApplicationBuilder
|
||||
if (string.IsNullOrWhiteSpace(entryAssemblyName))
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to infer the default application title. " +
|
||||
$"Please specify it explicitly using {nameof(SetTitle)}()."
|
||||
"Failed to infer the default application title. "
|
||||
+ $"Please specify it explicitly using `{nameof(SetTitle)}()`."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -241,11 +245,14 @@ public partial class CliApplicationBuilder
|
||||
var entryAssemblyFilePath = EnvironmentEx.EntryAssembly?.Location;
|
||||
var processFilePath = EnvironmentEx.ProcessPath;
|
||||
|
||||
if (string.IsNullOrWhiteSpace(entryAssemblyFilePath) || string.IsNullOrWhiteSpace(processFilePath))
|
||||
if (
|
||||
string.IsNullOrWhiteSpace(entryAssemblyFilePath)
|
||||
|| string.IsNullOrWhiteSpace(processFilePath)
|
||||
)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to infer the default application executable name. " +
|
||||
$"Please specify it explicitly using {nameof(SetExecutableName)}()."
|
||||
"Failed to infer the default application executable name. "
|
||||
+ $"Please specify it explicitly using `{nameof(SetExecutableName)}()`."
|
||||
);
|
||||
}
|
||||
|
||||
@@ -258,8 +265,13 @@ public partial class CliApplicationBuilder
|
||||
|
||||
// If the process path has the same name and parent directory as the entry assembly path,
|
||||
// but different extension, it's a framework-dependent .NET Core app launched through the apphost.
|
||||
if (PathEx.AreEqual(Path.ChangeExtension(entryAssemblyFilePath, "exe"), processFilePath) ||
|
||||
PathEx.AreEqual(Path.GetFileNameWithoutExtension(entryAssemblyFilePath), processFilePath))
|
||||
if (
|
||||
PathEx.AreEqual(Path.ChangeExtension(entryAssemblyFilePath, "exe"), processFilePath)
|
||||
|| PathEx.AreEqual(
|
||||
Path.GetFileNameWithoutExtension(entryAssemblyFilePath),
|
||||
processFilePath
|
||||
)
|
||||
)
|
||||
{
|
||||
return Path.GetFileNameWithoutExtension(entryAssemblyFilePath);
|
||||
}
|
||||
@@ -274,8 +286,8 @@ public partial class CliApplicationBuilder
|
||||
if (entryAssemblyVersion is null)
|
||||
{
|
||||
throw new InvalidOperationException(
|
||||
"Failed to infer the default application version. " +
|
||||
$"Please specify it explicitly using {nameof(SetVersion)}()."
|
||||
"Failed to infer the default application version. "
|
||||
+ $"Please specify it explicitly using `{nameof(SetVersion)}()`."
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user