Skip to content
This repository was archived by the owner on Jul 12, 2022. It is now read-only.

Commit dee2ba5

Browse files
committed
Added command line parsing unit tests
1 parent 311624b commit dee2ba5

File tree

4 files changed

+186
-26
lines changed

4 files changed

+186
-26
lines changed

src/CodeFormatter/CommandLineParser.cs

Lines changed: 82 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System;
44
using System.Collections.Generic;
55
using System.Collections.Immutable;
6+
using System.Diagnostics;
67
using System.IO;
78
using System.Linq;
89
using System.Text;
@@ -62,6 +63,56 @@ public CommandLineOptions(
6263
}
6364
}
6465

66+
public sealed class CommandLineParseResult
67+
{
68+
private readonly CommandLineOptions _options;
69+
private readonly string _error;
70+
71+
public bool IsSuccess
72+
{
73+
get { return _options != null; }
74+
}
75+
76+
public bool IsError
77+
{
78+
get { return !IsSuccess; }
79+
}
80+
81+
public CommandLineOptions Options
82+
{
83+
get
84+
{
85+
Debug.Assert(IsSuccess);
86+
return _options;
87+
}
88+
}
89+
90+
public string Error
91+
{
92+
get
93+
{
94+
Debug.Assert(IsError);
95+
return _error;
96+
}
97+
}
98+
99+
private CommandLineParseResult(CommandLineOptions options = null, string error = null)
100+
{
101+
_options = options;
102+
_error = error;
103+
}
104+
105+
public static CommandLineParseResult CreateSuccess(CommandLineOptions options)
106+
{
107+
return new CommandLineParseResult(options: options);
108+
}
109+
110+
public static CommandLineParseResult CreateError(string error)
111+
{
112+
return new CommandLineParseResult(error: error);
113+
}
114+
}
115+
65116
public static class CommandLineParser
66117
{
67118
private const string FileSwitch = "/file:";
@@ -71,10 +122,7 @@ public static class CommandLineParser
71122
private const string RuleEnabledSwitch1 = "/rule+:";
72123
private const string RuleEnabledSwitch2 = "/rule:";
73124
private const string RuleDisabledSwitch = "/rule-:";
74-
75-
public static void PrintUsage()
76-
{
77-
Console.WriteLine(
125+
private const string Usage =
78126
@"CodeFormatter [/file:<filename>] [/lang:<language>] [/c:<config>[,<config>...]>]
79127
[/copyright:<file> | /nocopyright] [/tables] [/nounicode]
80128
[/rule(+|-):rule1,rule2,...] [/verbose]
@@ -94,19 +142,24 @@ Use ConvertTests to convert MSTest tests to xUnit.
94142
/rule(+|-) - Enable (default) or disable the specified rule
95143
/rules - List the available rules
96144
/verbose - Verbose output
97-
");
145+
";
146+
147+
public static void PrintUsage()
148+
{
149+
Console.WriteLine(Usage);
98150
}
99151

100152
public static bool TryParse(string[] args, out CommandLineOptions options)
101153
{
102-
if (args.Length < 1)
103-
{
104-
PrintUsage();
105-
options = null;
106-
return false;
107-
}
154+
var result = Parse(args);
155+
options = result.IsSuccess ? result.Options : null;
156+
return result.IsSuccess;
157+
}
108158

159+
public static CommandLineParseResult Parse(string[] args)
160+
{
109161
var comparer = StringComparer.OrdinalIgnoreCase;
162+
var comparison = StringComparison.OrdinalIgnoreCase;
110163
var formatTargets = new List<string>();
111164
var fileNames = new List<string>();
112165
var configBuilder = ImmutableArray.CreateBuilder<string[]>();
@@ -116,7 +169,7 @@ public static bool TryParse(string[] args, out CommandLineOptions options)
116169
var allowTables = false;
117170
var verbose = false;
118171

119-
for (int i = 1; i < args.Length; i++)
172+
for (int i = 0; i < args.Length; i++)
120173
{
121174
string arg = args[i];
122175
if (arg.StartsWith(ConfigSwitch, StringComparison.OrdinalIgnoreCase))
@@ -134,10 +187,11 @@ public static bool TryParse(string[] args, out CommandLineOptions options)
134187
}
135188
catch (Exception ex)
136189
{
137-
Console.Error.WriteLine("Could not read {0}", fileName);
138-
Console.Error.WriteLine(ex.Message);
139-
options = null;
140-
return false;
190+
string error = string.Format("Could not read {0}{1}{2}",
191+
fileName,
192+
Environment.NewLine,
193+
ex.Message);
194+
return CommandLineParseResult.CreateError(error);
141195
}
142196
}
143197
else if (arg.StartsWith(LanguageSwitch, StringComparison.OrdinalIgnoreCase))
@@ -156,19 +210,19 @@ public static bool TryParse(string[] args, out CommandLineOptions options)
156210
{
157211
verbose = true;
158212
}
159-
else if (comparer.Equals(arg, FileSwitch))
213+
else if (arg.StartsWith(FileSwitch, comparison))
160214
{
161215
fileNames.Add(arg.Substring(FileSwitch.Length));
162216
}
163-
else if (comparer.Equals(arg, RuleEnabledSwitch1))
217+
else if (arg.StartsWith(RuleEnabledSwitch1, comparison))
164218
{
165219
UpdateRuleMap(ref ruleMap, arg.Substring(RuleEnabledSwitch1.Length), enabled: true);
166220
}
167-
else if (comparer.Equals(arg, RuleEnabledSwitch2))
221+
else if (arg.StartsWith(RuleEnabledSwitch2, comparison))
168222
{
169223
UpdateRuleMap(ref ruleMap, arg.Substring(RuleEnabledSwitch2.Length), enabled: true);
170224
}
171-
else if (comparer.Equals(arg, RuleDisabledSwitch))
225+
else if (arg.StartsWith(RuleDisabledSwitch, comparison))
172226
{
173227
UpdateRuleMap(ref ruleMap, arg.Substring(RuleDisabledSwitch.Length), enabled: false);
174228
}
@@ -178,16 +232,20 @@ public static bool TryParse(string[] args, out CommandLineOptions options)
178232
}
179233
else if (comparer.Equals(arg, "/rules"))
180234
{
181-
options = CommandLineOptions.ListRules;
182-
return true;
235+
return CommandLineParseResult.CreateSuccess(CommandLineOptions.ListRules);
183236
}
184237
else
185238
{
186239
formatTargets.Add(arg);
187240
}
188241
}
189242

190-
options = new CommandLineOptions(
243+
if (formatTargets.Count == 0)
244+
{
245+
return CommandLineParseResult.CreateError("Must specify at least one project / solution / rsp to format");
246+
}
247+
248+
var options = new CommandLineOptions(
191249
Operation.Format,
192250
configBuilder.ToImmutableArray(),
193251
copyrightHeader,
@@ -197,7 +255,7 @@ public static bool TryParse(string[] args, out CommandLineOptions options)
197255
language,
198256
allowTables,
199257
verbose);
200-
return true;
258+
return CommandLineParseResult.CreateSuccess(options);
201259
}
202260

203261
private static void UpdateRuleMap(ref ImmutableDictionary<string, bool> ruleMap, string data, bool enabled)

src/CodeFormatter/Program.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@ internal static class Program
1818
{
1919
private static int Main(string[] args)
2020
{
21-
CommandLineOptions options;
22-
if (!CommandLineParser.TryParse(args, out options))
21+
var result = CommandLineParser.Parse(args);
22+
if (result.IsError)
2323
{
24+
Console.Error.WriteLine(result.Error);
25+
CommandLineParser.PrintUsage();
2426
return -1;
2527
}
2628

29+
var options = result.Options;
2730
int exitCode;
2831
switch (options.Operation)
2932
{
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
using CodeFormatter;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
9+
namespace Microsoft.DotNet.CodeFormatting.Tests
10+
{
11+
public class CommandLineParserTests
12+
{
13+
private CommandLineOptions Parse(params string[] args)
14+
{
15+
CommandLineOptions options;
16+
Assert.True(CommandLineParser.TryParse(args, out options));
17+
return options;
18+
}
19+
20+
[Fact]
21+
public void Rules()
22+
{
23+
var options = Parse("/rules");
24+
Assert.Equal(Operation.ListRules, options.Operation);
25+
}
26+
27+
[Fact]
28+
public void Rules1()
29+
{
30+
var options = Parse("/rule:r1", "test.csproj");
31+
Assert.True(options.RuleMap["r1"]);
32+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
33+
}
34+
35+
[Fact]
36+
public void Rules2()
37+
{
38+
var options = Parse("/rule+:r1", "test.csproj");
39+
Assert.True(options.RuleMap["r1"]);
40+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
41+
}
42+
43+
[Fact]
44+
public void Rules3()
45+
{
46+
var options = Parse("/rule-:r1", "test.csproj");
47+
Assert.False(options.RuleMap["r1"]);
48+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
49+
}
50+
51+
[Fact]
52+
public void Rules4()
53+
{
54+
var options = Parse("/rule:r1,r2,r3", "test.csproj");
55+
Assert.True(options.RuleMap["r1"]);
56+
Assert.True(options.RuleMap["r2"]);
57+
Assert.True(options.RuleMap["r3"]);
58+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
59+
}
60+
61+
[Fact]
62+
public void Rules5()
63+
{
64+
var options = Parse("/rule-:r1,r2,r3", "test.csproj");
65+
Assert.False(options.RuleMap["r1"]);
66+
Assert.False(options.RuleMap["r2"]);
67+
Assert.False(options.RuleMap["r3"]);
68+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
69+
}
70+
71+
[Fact]
72+
public void NeedAtLeastOneTarget()
73+
{
74+
CommandLineOptions options;
75+
Assert.False(CommandLineParser.TryParse(new[] { "/rule:foo" }, out options));
76+
}
77+
78+
[Fact]
79+
public void NoUnicode()
80+
{
81+
var options = Parse("/nounicode", "test.csproj");
82+
Assert.False(options.RuleMap[FormattingDefaults.UnicodeLiteralsRuleName]);
83+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
84+
}
85+
86+
[Fact]
87+
public void NoCopyright()
88+
{
89+
var options = Parse("/nocopyright", "test.csproj");
90+
Assert.False(options.RuleMap[FormattingDefaults.CopyrightRuleName]);
91+
Assert.Equal(new[] { "test.csproj" }, options.FormatTargets);
92+
}
93+
}
94+
}

src/Microsoft.DotNet.CodeFormatting.Tests/Microsoft.DotNet.CodeFormatting.Tests.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
</ItemGroup>
106106
<ItemGroup>
107107
<Compile Include="CodeFormattingTestBase.cs" />
108+
<Compile Include="CommandLineParserTests.cs" />
108109
<Compile Include="Rules\BracesRuleTests.cs" />
109110
<Compile Include="Rules\CombinationTest.cs" />
110111
<Compile Include="Rules\CopyrightHeaderRuleTests.cs" />
@@ -130,6 +131,10 @@
130131
</None>
131132
</ItemGroup>
132133
<ItemGroup>
134+
<ProjectReference Include="..\CodeFormatter\CodeFormatter.csproj">
135+
<Project>{b0e1a988-f762-459d-ad0d-56a3cf4fff3f}</Project>
136+
<Name>CodeFormatter</Name>
137+
</ProjectReference>
133138
<ProjectReference Include="..\Microsoft.DotNet.CodeFormatting\Microsoft.DotNet.CodeFormatting.csproj">
134139
<Project>{d535641f-a2d7-481c-930d-96c02f052b95}</Project>
135140
<Name>Microsoft.DotNet.CodeFormatting</Name>

0 commit comments

Comments
 (0)