Skip to content

Commit 44639f2

Browse files
committed
refactor the parsing engine to split out the responsibilities making it more easy to test and extend. This should allow development for more edge case scenarios and create a more powerful and simplified engine
1 parent 673ee12 commit 44639f2

12 files changed

+615
-119
lines changed

FluentCommandLineParser.Tests/FluentCommandLineParser.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@
101101
<Compile Include="Integration\NCrunchExcelDataAttribute.cs" />
102102
<Compile Include="Integration\StringInlineDataAttribute.cs" />
103103
<Compile Include="Internals\AnonymousMock.cs" />
104+
<Compile Include="Internals\CommandLineOptionGrouperTests.cs" />
104105
<Compile Include="Internals\CommandLineParserEngineMark2Tests.cs" />
105106
<Compile Include="Internals\CommandLineParserEngineMark2TestsXUnit.cs" />
106107
<Compile Include="Internals\CommandLineParserEngine\TestContext\CommandLineParserEngineTestContext.cs" />
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
#region License
2+
// CommandLineOptionGrouperTests.cs
3+
// Copyright (c) 2013, Simon Williams
4+
// All rights reserved.
5+
//
6+
// Redistribution and use in source and binary forms, with or without modification, are permitted provide
7+
// d that the following conditions are met:
8+
//
9+
// Redistributions of source code must retain the above copyright notice, this list of conditions and the
10+
// following disclaimer.
11+
//
12+
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
13+
// the following disclaimer in the documentation and/or other materials provided with the distribution.
14+
//
15+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
16+
// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
17+
// PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
18+
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
19+
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20+
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
21+
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22+
// POSSIBILITY OF SUCH DAMAGE.
23+
#endregion
24+
25+
using Fclp.Internals;
26+
using Fclp.Internals.Parsing;
27+
using Machine.Specifications;
28+
29+
namespace Fclp.Tests.Internals
30+
{
31+
public class CommandLineOptionGrouperTests
32+
{
33+
[Subject(typeof(CommandLineOptionGrouper))]
34+
abstract class CommandLineOptionGrouperTestContext : TestContextBase<CommandLineOptionGrouper>
35+
{
36+
Establish context = () =>
37+
CreateSut();
38+
}
39+
40+
abstract class GroupByOptionTestContext : CommandLineOptionGrouperTestContext
41+
{
42+
protected static string[][] actualResult;
43+
protected static string[] args;
44+
45+
Because of = () =>
46+
error = Catch.Exception(() =>
47+
actualResult = sut.GroupByOption(args));
48+
}
49+
50+
class when_double_dashes_are_used_to_terminate_option_parsing : GroupByOptionTestContext
51+
{
52+
Establish context = () =>
53+
{
54+
args = new []
55+
{
56+
"-A", "1", "2", "3",
57+
"-B", "a", "b", "c",
58+
"-C", "--", "-a", "-1", "-b"
59+
};
60+
};
61+
62+
It should_return_three_sets = () =>
63+
actualResult.Length.ShouldEqual(3);
64+
65+
It should_group_the_A_elements = () =>
66+
actualResult[0].ShouldContainOnly("-A", "1", "2", "3");
67+
68+
It should_group_the_B_elements = () =>
69+
actualResult[1].ShouldContainOnly("-B", "a", "b", "c");
70+
71+
It should_group_the_C_elements = () =>
72+
actualResult[2].ShouldContainOnly("-C", "--", "-a", "-1", "-b");
73+
}
74+
75+
class when_double_dashes_are_used_immediately_to_terminate_option_parsing : GroupByOptionTestContext
76+
{
77+
Establish context = () =>
78+
args = new []
79+
{
80+
"--", "-A", "1", "2", "3",
81+
"-B", "a", "b", "c",
82+
"-C", "-a", "-1", "-b"
83+
};
84+
85+
It should_return_a_single_set = () =>
86+
actualResult.Length.ShouldEqual(1);
87+
88+
It should_group_the_A_elements = () =>
89+
actualResult[0].ShouldContainOnly("--", "-A", "1", "2", "3", "-B", "a", "b", "c", "-C", "-a", "-1", "-b");
90+
}
91+
92+
class when_there_are_only_arguments_and_no_options : GroupByOptionTestContext
93+
{
94+
Establish context = () =>
95+
args = new[] { "0", "1", "2", "3" };
96+
97+
It should_return_a_single_set = () =>
98+
actualResult.Length.ShouldEqual(1);
99+
100+
It should_group_all_the_provided_elements = () =>
101+
actualResult[0].ShouldContainOnly("0", "1", "2", "3");
102+
}
103+
104+
class when_there_is_a_double_dash_but_no_options_before_it : GroupByOptionTestContext
105+
{
106+
Establish context = () =>
107+
args = new[] { "--", "-A", "1", "2", "3" };
108+
109+
It should_return_a_single_set = () =>
110+
actualResult.Length.ShouldEqual(1);
111+
112+
It should_group_all_the_provided_elements = () =>
113+
actualResult[0].ShouldContainOnly("--", "-A", "1", "2", "3");
114+
}
115+
116+
class when_there_is_a_double_dash_at_the_end : GroupByOptionTestContext
117+
{
118+
Establish context = () =>
119+
args = new[]
120+
{
121+
"-A", "1", "2", "3",
122+
"-B", "a", "b", "c",
123+
"-C", "a1", "b1", "c1", "--"
124+
};
125+
126+
It should_return_three_sets = () =>
127+
actualResult.Length.ShouldEqual(3);
128+
129+
It should_group_the_A_elements = () =>
130+
actualResult[0].ShouldContainOnly("-A", "1", "2", "3");
131+
132+
It should_group_the_B_elements = () =>
133+
actualResult[1].ShouldContainOnly("-B", "a", "b", "c");
134+
135+
It should_group_the_C_elements = () =>
136+
actualResult[2].ShouldContainOnly("-C", "a1", "b1", "c1", "--");
137+
}
138+
139+
class when_options_are_repeated : GroupByOptionTestContext
140+
{
141+
Establish context = () =>
142+
args = new[] { "-A", "1", "2", "3", "-A", "4", "5", "6" };
143+
144+
It should_return_two_sets = () =>
145+
actualResult.Length.ShouldEqual(2);
146+
147+
It should_group_the_first_elements = () =>
148+
actualResult[0].ShouldContainOnly("-A", "1", "2", "3");
149+
150+
It should_group_the_second_repeated_elements = () =>
151+
actualResult[1].ShouldContainOnly("-A", "4", "5", "6");
152+
}
153+
154+
class when_the_args_is_empty : GroupByOptionTestContext
155+
{
156+
Establish context = () =>
157+
args = new string[0];
158+
159+
It should_not_throw_an_error = () =>
160+
error.ShouldBeNull();
161+
162+
It should_return_empty_args = () =>
163+
actualResult.ShouldBeEmpty();
164+
}
165+
}
166+
}

FluentCommandLineParser.Tests/Internals/CommandLineParserEngine/when_specified_args_contain_no_keys.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ public class when_specified_args_contain_no_keys : CommandLineParserEngineTestCo
4343
};
4444
};
4545

46-
Because of = () => RunParserWith(args);
46+
Because of = () =>
47+
RunParserWith(args);
4748

4849
Behaves_like<NoResultsBehaviour> there_are_no_keys_found;
4950
}

FluentCommandLineParser.Tests/Internals/CommandLineParserEngineMark2Tests.cs

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace Fclp.Tests.Internals
3131
{
3232
sealed class CommandLineParserEnginerMark2Tests
3333
{
34+
[Subject(typeof(CommandLineParserEngineMark2))]
3435
abstract class CommandLineParserEngineMark2TestContext : TestContextBase<CommandLineParserEngineMark2>
3536
{
3637
Establish context = () => CreateSut();
@@ -63,9 +64,62 @@ class when_args_is_null : ParseTestContext
6364
result.AdditionalValues.ShouldBeEmpty();
6465
}
6566

66-
class when_ : ParseTestContext
67+
class when_args_is_empty : ParseTestContext
6768
{
68-
Establish context = () => SetupArgs("");
69+
Establish context = () => args = CreateEmptyList<string>().ToArray();
70+
71+
It should_return_a_result_with_no_parsed_options = () =>
72+
result.ParsedOptions.ShouldBeEmpty();
73+
74+
It should_return_a_result_with_no_additional_values = () =>
75+
result.AdditionalValues.ShouldBeEmpty();
76+
}
77+
78+
class when_args_contains_negative_argument_seperated_with_a_colon : ParseTestContext
79+
{
80+
Establish context = () => SetupArgs("--int:-1");
81+
82+
It should_return_a_single_option = () =>
83+
result.ParsedOptions.Count().ShouldEqual(1);
84+
85+
It should_set_the_parsed_option_value_to_the_negative_number = () =>
86+
result.ParsedOptions.First().Value.ShouldEqual("-1");
87+
}
88+
89+
class when_args_contains_negative_argument_seperated_with_a_equals : ParseTestContext
90+
{
91+
Establish context = () => SetupArgs("--int=-123");
92+
93+
It should_return_a_single_option = () =>
94+
result.ParsedOptions.Count().ShouldEqual(1);
95+
96+
It should_set_the_parsed_option_value_to_the_negative_number = () =>
97+
result.ParsedOptions.First().Value.ShouldEqual("-123");
98+
}
99+
100+
class when_args_contains_negative_arguments_seperated_with_double_dash : ParseTestContext
101+
{
102+
Establish context = () => SetupArgs("--int -- -4321");
103+
104+
It should_return_a_single_option = () =>
105+
result.ParsedOptions.Count().ShouldEqual(1);
106+
107+
It should_set_the_parsed_option_value_to_the_negative_number = () =>
108+
result.ParsedOptions.First().Value.ShouldEqual("-4321");
109+
}
110+
111+
class when_args_contains_single_switch : ParseTestContext
112+
{
113+
Establish context = () => SetupArgs("-b");
114+
115+
It should_return_a_single_option = () =>
116+
result.ParsedOptions.Count().ShouldEqual(1);
117+
118+
It should_set_the_parsed_key_to_the_correct_value = () =>
119+
result.ParsedOptions.First().Key.ShouldEqual("b");
120+
121+
It should_set_the_parsed_raw_key_to_the_correct_value = () =>
122+
result.ParsedOptions.First().RawKey.ShouldEqual("-b");
69123
}
70124
}
71125
}

FluentCommandLineParser/FluentCommandLineParser.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
<Compile Include="Internals\IHelpCommandLineOption.cs" />
7676
<Compile Include="Internals\IHelpCommandLineOptionResult.cs" />
7777
<Compile Include="Internals\ParsedOption.cs" />
78+
<Compile Include="Internals\ParsedOptionFactory.cs" />
7879
<Compile Include="Internals\ParserEngineResult.cs" />
7980
<Compile Include="Internals\Parsers\BoolCommandLineOptionParser.cs" />
8081
<Compile Include="Internals\CommandLineOption.cs" />
@@ -94,6 +95,9 @@
9495
<Compile Include="Internals\Parsers\Int32CommandLineOptionParser.cs" />
9596
<Compile Include="Internals\Parsers\ListCommandLineOptionParser.cs" />
9697
<Compile Include="Internals\Parsers\StringCommandLineOptionParser.cs" />
98+
<Compile Include="Internals\Parsing\CommandLineOptionGrouper.cs" />
99+
<Compile Include="Internals\Parsing\OptionArgumentParser.cs" />
100+
<Compile Include="Internals\Parsing\Something.cs" />
97101
<Compile Include="Internals\SpecialCharacters.cs" />
98102
<Compile Include="Internals\Validators\CommandLineOptionValidator.cs" />
99103
<Compile Include="Internals\Validators\ICommandLineOptionValidator.cs" />
@@ -109,6 +113,7 @@
109113
<ItemGroup>
110114
<None Include="FluentCommandLineParser.snk" />
111115
</ItemGroup>
116+
<ItemGroup />
112117
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
113118
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
114119
Other similar extension points exist, see Microsoft.Common.targets.

0 commit comments

Comments
 (0)