Skip to content

Commit 43be2a0

Browse files
committed
replaced ArgumentOutOfRange with InvalidOptionNameException with meaningful error messages. This is because of the breaking change added which may cause problems when upgrading. The error messages should go some distance to help resolve any issues.
1 parent 1ffd84c commit 43be2a0

File tree

8 files changed

+121
-47
lines changed

8 files changed

+121
-47
lines changed

FluentCommandLineParser.Tests/FluentCommandLineParser/Behaviour/InvalidOptionSetupBehaviour.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public class InvalidOptionSetupBehaviour
3636
protected static Exception error;
3737

3838
It should_throw_an_error = () => error.ShouldNotBeNull();
39-
It should_throw_an_ArgumentOutOfRangeException = () => error.ShouldBeOfType(typeof(ArgumentOutOfRangeException));
39+
It should_throw_an_ArgumentOutOfRangeException = () => error.ShouldNotBeNull();
4040
It should_not_have_setup_an_option = () => sut.Options.ShouldBeEmpty();
4141
}
4242
}

FluentCommandLineParser.Tests/FluentCommandLineParser/when_setting_up_a_new_option/with_a_short_name/with_a_short_name_that_is_a_control_char.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
using Fclp.Tests.FluentCommandLineParser.TestContext;
2727
using Machine.Specifications;
2828

29-
namespace Fclp.Tests.FluentCommandLineParser
30-
{
31-
namespace when_setting_up_a_new_option
32-
{
33-
public class with_a_short_name_that_is_a_control_char : SettingUpAShortOptionTestContext
34-
{
35-
Establish context = AutoMockAll;
36-
37-
Because of = () => SetupOptionWith(invalid_short_name_that_is_a_control_char);
38-
39-
Behaves_like<InvalidOptionSetupBehaviour> a_failed_setup_option;
40-
}
41-
}
29+
namespace Fclp.Tests.FluentCommandLineParser
30+
{
31+
namespace when_setting_up_a_new_option
32+
{
33+
public class with_a_short_name_that_is_a_control_char : SettingUpAShortOptionTestContext
34+
{
35+
Establish context = AutoMockAll;
36+
37+
Because of = () => SetupOptionWith(invalid_short_name_that_is_a_control_char);
38+
39+
Behaves_like<InvalidOptionSetupBehaviour> a_failed_setup_option;
40+
}
41+
}
4242
}

FluentCommandLineParser.Tests/FluentCommandLineParserTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,7 @@ public void Can_have_long_option_only()
377377
}
378378

379379
[Test]
380-
[ExpectedException(typeof(ArgumentOutOfRangeException))]
380+
[ExpectedException(typeof(InvalidOptionNameException))]
381381
public void Cannot_have_single_character_long_option()
382382
{
383383
var parser = CreateFluentParser();

FluentCommandLineParser.Tests/Internals/Validators/CommandLineOptionNameValidatorTests.cs

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
// POSSIBILITY OF SUCH DAMAGE.
2323
#endregion
2424

25-
using System;
2625
using System.Globalization;
2726
using Fclp.Internals;
2827
using Fclp.Internals.Validators;
@@ -76,24 +75,23 @@ class when_the_short_name_is_whitespace : ValidateTestContext
7675
Establish context = () =>
7776
SetupOptionWith(shortName: " ");
7877

79-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
78+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
8079
}
8180

82-
8381
class when_the_short_name_contains_a_colon : ValidateTestContext
8482
{
8583
Establish context = () =>
86-
SetupOptionWith(shortName: ValidShortName + ":");
84+
SetupOptionWith(shortName: ":");
8785

88-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
86+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
8987
}
9088

9189
class when_the_short_name_contains_an_equality_sign : ValidateTestContext
9290
{
9391
Establish context = () =>
94-
SetupOptionWith(shortName: ValidShortName + "=");
92+
SetupOptionWith(shortName: "=");
9593

96-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
94+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
9795
}
9896

9997
class when_the_short_name_is_empty : ValidateTestContext
@@ -109,15 +107,15 @@ class when_the_short_name_is_a_control_char : ValidateTestContext
109107
Establish context = () =>
110108
SetupOptionWith(shortName: ((char)7).ToString(CultureInfo.InvariantCulture));
111109

112-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
110+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
113111
}
114112

115113
class when_the_short_name_is_longer_than_one_char : ValidateTestContext
116114
{
117115
Establish context = () =>
118116
SetupOptionWith(shortName: CreateStringOfLength(2));
119117

120-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
118+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
121119
}
122120

123121
class when_the_short_name_is_one_char : ValidateTestContext
@@ -141,23 +139,23 @@ class when_the_long_name_is_whitespace : ValidateTestContext
141139
Establish context = () =>
142140
SetupOptionWith(longName: " ");
143141

144-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
142+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
145143
}
146144

147145
class when_the_long_name_contains_a_colon : ValidateTestContext
148146
{
149147
Establish context = () =>
150148
SetupOptionWith(longName: ValidLongName + ":");
151149

152-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
150+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
153151
}
154152

155153
class when_the_long_name_contains_an_equality_sign : ValidateTestContext
156154
{
157155
Establish context = () =>
158156
SetupOptionWith(longName: ValidLongName + "=");
159157

160-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
158+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
161159
}
162160

163161
class when_the_long_name_is_empty : ValidateTestContext
@@ -181,31 +179,31 @@ class when_the_long_name_is_one_char : ValidateTestContext
181179
Establish context = () =>
182180
SetupOptionWith(longName: CreateStringOfLength(1));
183181

184-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
182+
It should_throw_a_too_long_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
185183
}
186184

187185
class when_the_long_name_contains_whitespace: ValidateTestContext
188186
{
189187
Establish context = () =>
190188
SetupOptionWith(longName: ValidLongName + " " + ValidLongName);
191189

192-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
190+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
193191
}
194192

195193
class when_the_long_name_is_null_and_the_short_name_is_null : ValidateTestContext
196194
{
197195
Establish context = () =>
198196
SetupOptionWith(shortName: null, longName: null);
199197

200-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
198+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
201199
}
202200

203201
class when_the_long_name_is_empty_and_the_short_name_is_empty : ValidateTestContext
204202
{
205203
Establish context = () =>
206204
SetupOptionWith(shortName: string.Empty, longName: string.Empty);
207205

208-
It should_throw_an_error = () => error.ShouldBeOfType<ArgumentOutOfRangeException>();
206+
It should_throw_an_error = () => error.ShouldBeOfType<InvalidOptionNameException>();
209207
}
210208
}
211209
}

FluentCommandLineParser/FluentCommandLineParser.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
<Compile Include="Internals\Validators\ICommandLineOptionValidator.cs" />
9696
<Compile Include="Internals\Validators\NoDuplicateOptionValidator.cs" />
9797
<Compile Include="Internals\Validators\OptionNameValidator.cs" />
98+
<Compile Include="InvalidOptionNameException.cs" />
9899
<Compile Include="Properties\AssemblyInfo.cs" />
99100
<Compile Include="Internals\Extensions\UsefulExtension.cs" />
100101
<Compile Include="OptionAlreadyExistsException.cs" />

FluentCommandLineParser/Internals/ParsedOption.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2222
// POSSIBILITY OF SUCH DAMAGE.
2323
#endregion
24-
2524
namespace Fclp.Internals
2625
{
2726
/// <summary>

FluentCommandLineParser/Internals/Validators/OptionNameValidator.cs

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#endregion
2424

2525
using System;
26+
using System.Globalization;
2627
using System.Linq;
2728

2829
namespace Fclp.Internals.Validators
@@ -53,22 +54,19 @@ private static void ValidateShortAndLongName(string shortName, string longName)
5354
{
5455
if (string.IsNullOrEmpty(shortName) && string.IsNullOrEmpty(longName))
5556
{
56-
throw new ArgumentOutOfRangeException();
57+
ThrowInvalid(string.Empty, "A short or long name must be provided.");
5758
}
5859
}
5960

6061
private static void ValidateLongName(string longName)
6162
{
6263
if (string.IsNullOrEmpty(longName)) return;
6364

64-
if (longName.Length == 1)
65-
{
66-
throw new ArgumentOutOfRangeException();
67-
}
65+
VerifyDoesNotContainsReservedChar(longName);
6866

69-
if (ContainsReserved(longName))
67+
if (longName.Length == 1)
7068
{
71-
throw new ArgumentOutOfRangeException();
69+
ThrowInvalid(longName, "Long names must be longer than a single character. Single characters are reserved for short options only.");
7270
}
7371
}
7472

@@ -78,24 +76,34 @@ private static void ValidateShortName(string shortName)
7876

7977
if (shortName.Length > 1)
8078
{
81-
throw new ArgumentOutOfRangeException();
79+
ThrowInvalid(shortName, "Short names must be a single character only.");
8280
}
8381

84-
if (ContainsReserved(shortName))
82+
VerifyDoesNotContainsReservedChar(shortName);
83+
84+
if (char.IsControl(shortName, 0))
8585
{
86-
throw new ArgumentOutOfRangeException();
86+
ThrowInvalid(shortName, "The character '" + shortName + "' is not valid for a short name.");
8787
}
88+
}
8889

89-
if (char.IsControl(shortName, 0))
90+
private static void VerifyDoesNotContainsReservedChar(string value)
91+
{
92+
if (string.IsNullOrEmpty(value)) return;
93+
94+
foreach (char reservedChar in ReservedChars)
9095
{
91-
throw new ArgumentOutOfRangeException();
96+
if (value.Contains(reservedChar))
97+
{
98+
ThrowInvalid(value, "The character '" + reservedChar + "' is not valid for a short or long name.");
99+
}
92100
}
93101
}
94102

95-
private static bool ContainsReserved(string value)
103+
private static void ThrowInvalid(string value, string message)
96104
{
97-
return value != null
98-
&& ReservedChars.Any(value.Contains);
105+
throw new InvalidOptionNameException(
106+
string.Format(CultureInfo.InvariantCulture, "Invalid option name '{0}'. {1}", value, message));
99107
}
100108
}
101109
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#region License
2+
// InvalidOptionNameException.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 System;
26+
using System.Runtime.Serialization;
27+
28+
namespace Fclp
29+
{
30+
/// <summary>
31+
/// Represents an error that has occurred because a specified Option name is invalid.
32+
/// </summary>
33+
public class InvalidOptionNameException : Exception
34+
{
35+
/// <summary>
36+
/// Initializes a new instance of the <see cref="InvalidOptionNameException"/> class.
37+
/// </summary>
38+
public InvalidOptionNameException()
39+
{
40+
}
41+
42+
/// <summary>
43+
/// Initializes a new instance of the <see cref="InvalidOptionNameException"/> class.
44+
/// </summary>
45+
/// <param name="message">The message that describes the error.</param>
46+
public InvalidOptionNameException(string message) : base(message)
47+
{
48+
}
49+
50+
/// <summary>
51+
/// Initializes a new instance of the <see cref="InvalidOptionNameException"/> class.
52+
/// </summary>
53+
/// <param name="message">The error message that explains the reason for the exception.</param>
54+
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
55+
public InvalidOptionNameException(string message, Exception innerException) : base(message, innerException)
56+
{
57+
}
58+
59+
/// <summary>
60+
/// Initializes a new instance of the <see cref="InvalidOptionNameException"/> class.
61+
/// </summary>
62+
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
63+
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
64+
protected InvalidOptionNameException(SerializationInfo info, StreamingContext context) : base(info, context)
65+
{
66+
}
67+
}
68+
}

0 commit comments

Comments
 (0)