Skip to content

Commit 2cbd6cd

Browse files
authored
Added Unit tests for CommandLine (#305)
* State of the testing is rather limited due to bugs in the underlying API and design [This library should minimize exposure to the underlying System.CommandLine so that it is easier to replace it with a different parsing technology as there are a LOT of issues/assumptions]
1 parent 0e09cac commit 2cbd6cd

File tree

10 files changed

+331
-2
lines changed

10 files changed

+331
-2
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
// In SDK-style projects such as this one, several assembly attributes that were historically
8+
// defined in this file are now automatically added during build and populated with
9+
// values defined in project properties. For details of which attributes are included
10+
// and how to customize this process see: https://aka.ms/assembly-info-properties
11+
12+
// Setting ComVisible to false makes the types in this assembly not visible to COM
13+
// components. If you need to access a type in this assembly from COM, set the ComVisible
14+
// attribute to true on that type.
15+
[assembly: ComVisible( false )]
16+
17+
// The following GUID is for the ID of the typelib if this project is exposed to COM.
18+
[assembly: Guid( "994905c9-5cea-480a-b9fa-1458c5fc04d5" )]
19+
20+
[assembly: CLSCompliant( false )]
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
using System.CommandLine;
5+
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
8+
namespace Ubiquity.NET.CommandLine.UT
9+
{
10+
[TestClass]
11+
public class CommandLineTests
12+
{
13+
[TestMethod]
14+
public void CommandLine_parse_with_version_option_only_succeeds( )
15+
{
16+
var settings = CreateTestSettings();
17+
var result = ArgsParsing.Parse<TestOptions>(["--version"], settings);
18+
19+
// due to bug in underlying library this will fail, see: https://github.com/dotnet/command-line-api/issues/2659
20+
// bug is fixed in PR https://github.com/dotnet/command-line-api/pull/2644 but not available
21+
// yet. (as of version 2.0.0-beta7.25380.108)
22+
//Assert.HasCount( 0, result.Errors );
23+
Assert.HasCount( 1, result.Errors );
24+
25+
var versionOption = result.GetVersionOption();
26+
Assert.IsNotNull(versionOption);
27+
Assert.AreEqual( versionOption.Action, result.Action);
28+
}
29+
30+
[TestMethod]
31+
public void CommandLine_with_help_option_only_succeeds( )
32+
{
33+
var settings = CreateTestSettings();
34+
35+
var result = ArgsParsing.Parse<TestOptions>(["--help"], settings);
36+
Assert.HasCount( 0, result.Errors );
37+
}
38+
39+
[TestMethod]
40+
public void CommandLine_with_unknown_option_has_errors( )
41+
{
42+
var settings = CreateTestSettings();
43+
ParseResult result = ArgsParsing.Parse<TestOptions>(["--FooBar"], settings );
44+
Assert.HasCount( 2, result.Errors, "Errors should include missing Required, and invalid param" );
45+
}
46+
47+
[TestMethod]
48+
[Ignore("https://github.com/dotnet/command-line-api/issues/2664")]
49+
public void CommandLine_with_known_option_and_version_has_errors( )
50+
{
51+
var settings = CreateTestSettings();
52+
ParseResult result = ArgsParsing.Parse<TestOptions>(["--version", "--option1"], settings );
53+
54+
// until https://github.com/dotnet/command-line-api/issues/2664 is resolved this will fail
55+
Assert.HasCount( 2, result.Errors, "Should be one error (--version must be set alone, missing arg for --option1)" );
56+
}
57+
58+
[TestMethod]
59+
[Ignore("https://github.com/dotnet/command-line-api/issues/2664")]
60+
public void CommandLine_with_known_option_requiring_arg_and_version_has_errors( )
61+
{
62+
var settings = CreateTestSettings();
63+
ParseResult result = ArgsParsing.Parse<TestOptions>(["--option1", "--version"], settings );
64+
65+
// until https://github.com/dotnet/command-line-api/issues/2664 is resolved this will fail
66+
Assert.HasCount( 2, result.Errors, "Should be one error (--version must be set alone, missing arg for --option1)" );
67+
}
68+
69+
internal static CmdLineSettings CreateTestSettings( DefaultOption defaultOptions = DefaultOption.Help | DefaultOption.Version )
70+
{
71+
return new CmdLineSettings()
72+
{
73+
DefaultOptions = defaultOptions,
74+
};
75+
}
76+
}
77+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
// This file is used by Code Analysis to maintain SuppressMessage
5+
// attributes that are applied to this project.
6+
// Project-level suppressions either have no target or are given
7+
// a specific target and scoped to a namespace, type, member, etc.
8+
9+
using System.Diagnostics.CodeAnalysis;
10+
11+
[assembly: SuppressMessage( "StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Unit Tests" )]
12+
[assembly: SuppressMessage( "StyleCop.CSharp.DocumentationRules", "SA1652:Enable XML documentation output", Justification = "Unit Tests" )]
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
#if USE_MODULE_FIXTURES
5+
using System;
6+
7+
using Microsoft.VisualStudio.TestTools.UnitTesting;
8+
9+
namespace Ubiquity.NET.CommandLine.UT
10+
{
11+
// Provides common location for one time initialization for all tests in this assembly
12+
[TestClass]
13+
public static class ModuleFixtures
14+
{
15+
[AssemblyInitialize]
16+
public static void AssemblyInitialize( TestContext ctx )
17+
{
18+
ArgumentNullException.ThrowIfNull( ctx );
19+
}
20+
21+
[AssemblyCleanup]
22+
public static void AssemblyCleanup( )
23+
{
24+
}
25+
}
26+
}
27+
#endif
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
using System.CommandLine;
5+
6+
using Microsoft.VisualStudio.TestTools.UnitTesting;
7+
8+
namespace Ubiquity.NET.CommandLine.UT
9+
{
10+
/// <summary>This is mostly for validation.understanding of the underlying RAW API as well as a good place to put "samples" for bug reports</summary>
11+
[TestClass]
12+
public class RawApiTests
13+
{
14+
[TestMethod]
15+
[Ignore( "https://github.com/dotnet/command-line-api/issues/2664" )]
16+
public void RawApi_Version_Error_tests( )
17+
{
18+
var rootCommand = new RootCommand("Test Root")
19+
{
20+
new Option<string>("--option1")
21+
{
22+
Description = "Test option `",
23+
Required = true,
24+
},
25+
};
26+
27+
var result = rootCommand.Parse(["--FooBar", "--version"]);
28+
Assert.HasCount( 3, result.Errors, "Errors should account for, bogus arg (`--FooBar`), missing required arg (`--option1`), AND that `--version` should be solo" );
29+
}
30+
31+
[TestMethod]
32+
[Ignore( "https://github.com/dotnet/command-line-api/issues/2664" )]
33+
public void RawApi_Help_Error_tests( )
34+
{
35+
var rootCommand = new RootCommand("Test Root")
36+
{
37+
new Option<string>("--option1")
38+
{
39+
Description = "Test option `",
40+
Required = true,
41+
},
42+
};
43+
44+
var result = rootCommand.Parse(["--FooBar", "--help"]);
45+
Assert.HasCount( 3, result.Errors, "Errors should account for bogus arg (`--FooBar`), missing required arg (`--option1`), AND that `--version` should be solo" );
46+
}
47+
48+
[TestMethod]
49+
[Ignore( "https://github.com/dotnet/command-line-api/issues/2659" )]
50+
public void RawApi_Version_Only_with_required_has_no_errors( )
51+
{
52+
var rootCommand = new RootCommand("Test Root")
53+
{
54+
new Option<string>("--option1")
55+
{
56+
Description = "Test option `",
57+
Required = true,
58+
},
59+
};
60+
61+
var result = rootCommand.Parse(["--version"]);
62+
Assert.HasCount( 0, result.Errors, "Should not be any errors" );
63+
}
64+
65+
[TestMethod]
66+
[Ignore("https://github.com/dotnet/command-line-api/issues/2664")]
67+
public void RawApi_Version_with_required_option_has_errors( )
68+
{
69+
var rootCommand = new RootCommand("Test Root")
70+
{
71+
new Option<string>("--option1")
72+
{
73+
Description = "Test option `",
74+
Required = true,
75+
},
76+
};
77+
78+
var result = rootCommand.Parse(["--version", "--option1"]);
79+
80+
// This assert will fail - result.Errors.Count == 3!
81+
// result.Errors:
82+
// [0]{--version option cannot be combined with other arguments.}
83+
// [1]{Required argument missing for option: '--option1'.}
84+
// [2]{Required argument missing for option: '--option1'.} // Why is this occurring twice?
85+
//Assert.HasCount( 2, result.Errors, "Should be two errors (version not used solo, missing arg)" );
86+
87+
// try with arguments in reversed order (--version is later)
88+
result = rootCommand.Parse(["--option1", "--version"]);
89+
90+
// result.Action == null! [BUG]
91+
// result.Errors.Count == 0! [BUG]
92+
Assert.HasCount( 2, result.Errors, "Should be two errors (version not used solo, missing arg)" );
93+
94+
result = rootCommand.Parse("--option1 --version");
95+
96+
// result.Action == null! [BUG]
97+
// result.Errors.Count == 0! [BUG]
98+
Assert.HasCount( 2, result.Errors, "Should be two errors (version not used solo, missing arg)" );
99+
}
100+
}
101+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
using System;
5+
using System.Collections.Immutable;
6+
using System.IO;
7+
8+
namespace Ubiquity.NET.CommandLine.UT
9+
{
10+
internal static class SettingsTestExtensions
11+
{
12+
public static ImmutableArray<string> GetOutput( this StringWriter self )
13+
{
14+
ArgumentNullException.ThrowIfNull( self );
15+
string underlyingString = self.ToString();
16+
return string.IsNullOrWhiteSpace( underlyingString )
17+
? []
18+
: [ .. underlyingString.Split( self.NewLine, StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries ) ];
19+
}
20+
}
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
2+
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
3+
4+
namespace Ubiquity.NET.CommandLine.UT
5+
{
6+
internal partial class TestOptions
7+
{
8+
public string Option1 { get; init; } = string.Empty;
9+
}
10+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.CommandLine;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace Ubiquity.NET.CommandLine.UT
9+
{
10+
// FUTURE: Generate this with source generator from attributes in other partial declaration
11+
internal partial class TestOptions
12+
: ICommandLineOptions<TestOptions>
13+
{
14+
public static TestOptions Bind( ParseResult parseResult )
15+
{
16+
return new()
17+
{
18+
Option1 = parseResult.GetValue(Descriptors.Option1),
19+
};
20+
}
21+
22+
public static AppControlledDefaultsRootCommand BuildRootCommand( CmdLineSettings settings )
23+
{
24+
return new( settings, "Test option root command")
25+
{
26+
Descriptors.Option1,
27+
};
28+
}
29+
30+
internal static class Descriptors
31+
{
32+
internal static Option<string> Option1
33+
= new("--option1")
34+
{
35+
Description = "Test Option",
36+
Required = true, // Should have no impact on Help/Version
37+
};
38+
}
39+
}
40+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<IsPackable>false</IsPackable>
6+
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" />
11+
<PackageReference Include="MSTest.TestFramework" />
12+
<PackageReference Include="MSTest.TestAdapter" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\Ubiquity.NET.CommandLine\Ubiquity.NET.CommandLine.csproj" />
17+
</ItemGroup>
18+
</Project>

src/Ubiquity.NET.Llvm.slnx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,17 @@
6464
<Project Path="Samples/Kaleidoscope/Kaleidoscope.Runtime/Kaleidoscope.Runtime.csproj" />
6565
<Project Path="Samples/Kaleidoscope/Kaleidoscope.Tests/Kaleidoscope.Tests.csproj" />
6666
</Folder>
67+
<Folder Name="/Tests/">
68+
<Project Path="Ubiquity.NET.CommandLine.UT/Ubiquity.NET.CommandLine.UT.csproj" />
69+
<Project Path="Ubiquity.NET.Llvm.JIT.Tests/Ubiquity.NET.Llvm.JIT.Tests.csproj" />
70+
<Project Path="Ubiquity.NET.Llvm.Tests/Ubiquity.NET.Llvm.UT.csproj" />
71+
</Folder>
6772
<Project Path="../docfx/documentation.msbuildproj" Type="13b669be-bb05-4ddf-9536-439f39a36129" />
6873
<Project Path="../PsModules/CommonBuild/CommonBuild.pssproj" Type="f5034706-568f-408a-b7b3-4d38c6db8a32" Id="6cafc0c6-a428-4d30-a9f9-700e829fea51" />
6974
<Project Path="../PsModules/RepoBuild/RepoBuild.pssproj" Type="f5034706-568f-408a-b7b3-4d38c6db8a32" Id="118e4d2b-5adb-4f7c-9395-b4ceeee3d2e1" />
7075
<Project Path="Ubiquity.NET.ANTLR.Utils/Ubiquity.NET.ANTLR.Utils.csproj" />
7176
<Project Path="Ubiquity.NET.CommandLine/Ubiquity.NET.CommandLine.csproj" Id="3dae7afd-0d33-4805-855b-c1283237b58a" />
7277
<Project Path="Ubiquity.NET.Extensions/Ubiquity.NET.Extensions.csproj" />
73-
<Project Path="Ubiquity.NET.Llvm.JIT.Tests/Ubiquity.NET.Llvm.JIT.Tests.csproj" />
74-
<Project Path="Ubiquity.NET.Llvm.Tests/Ubiquity.NET.Llvm.UT.csproj" />
7578
<Project Path="Ubiquity.NET.Llvm/Ubiquity.NET.Llvm.csproj" />
7679
<Project Path="Ubiquity.NET.Runtime.Utils/Ubiquity.NET.Runtime.Utils.csproj" />
7780
<Project Path="Ubiquity.NET.TextUX/Ubiquity.NET.TextUX.csproj" Id="0c58feb7-2b42-431a-9a37-43a8b263add9" />

0 commit comments

Comments
 (0)