Skip to content

Commit 1e381ed

Browse files
authored
Merge pull request #1952 from tyrielv/tyrielv/cli-tests
Add CLI argument parsing tests for GVFS, GVFS.Mount, and FastFetch
2 parents 96352b2 + 413ea79 commit 1e381ed

13 files changed

Lines changed: 957 additions & 4 deletions

GVFS.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Payload", "GVFS\GVFS.P
4545
EndProject
4646
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GVFS.Installers", "GVFS\GVFS.Installers\GVFS.Installers.csproj", "{258FEAC0-5E2D-408A-9652-9E9653219F3B}"
4747
EndProject
48+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GVFS.CommandLine.Tests", "GVFS\GVFS.CommandLine.Tests\GVFS.CommandLine.Tests.csproj", "{4D201963-957A-436A-8E43-79A63FB84B94}"
49+
EndProject
4850
Global
4951
GlobalSection(SolutionConfigurationPlatforms) = preSolution
5052
Debug|x64 = Debug|x64
@@ -135,6 +137,10 @@ Global
135137
{258FEAC0-5E2D-408A-9652-9E9653219F3B}.Debug|x64.Build.0 = Debug|Any CPU
136138
{258FEAC0-5E2D-408A-9652-9E9653219F3B}.Release|x64.ActiveCfg = Release|Any CPU
137139
{258FEAC0-5E2D-408A-9652-9E9653219F3B}.Release|x64.Build.0 = Release|Any CPU
140+
{4D201963-957A-436A-8E43-79A63FB84B94}.Debug|x64.ActiveCfg = Debug|Any CPU
141+
{4D201963-957A-436A-8E43-79A63FB84B94}.Debug|x64.Build.0 = Debug|Any CPU
142+
{4D201963-957A-436A-8E43-79A63FB84B94}.Release|x64.ActiveCfg = Release|Any CPU
143+
{4D201963-957A-436A-8E43-79A63FB84B94}.Release|x64.Build.0 = Release|Any CPU
138144
EndGlobalSection
139145
GlobalSection(SolutionProperties) = preSolution
140146
HideSolutionNode = FALSE

GVFS/FastFetch/FastFetchVerb.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ public static RootCommand BuildRootCommand()
122122
};
123123
rootCommand.Add(foldersListOption);
124124

125-
Option<bool> allowIndexMetadataOption = new Option<bool>("--Allow-index-metadata-update-from-working-tree") { Description = "When specified, index metadata is updated from disk if not already in the index." };
125+
Option<bool> allowIndexMetadataOption = new Option<bool>("--allow-index-metadata-update-from-working-tree") { Description = "When specified, index metadata is updated from disk if not already in the index." };
126126
rootCommand.Add(allowIndexMetadataOption);
127127

128128
Option<bool> verboseOption = new Option<bool>("--verbose") { Description = "Show all outputs on the console in addition to writing them to a log file" };
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
using System.Runtime.CompilerServices;
2+
3+
[assembly: InternalsVisibleTo("GVFS.CommandLine.Tests")]

GVFS/FastFetch/Program.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,20 @@
11
using System.CommandLine;
2+
using System.Runtime.CompilerServices;
23
using GVFS.PlatformLoader;
34

5+
[assembly: InternalsVisibleTo("GVFS.CommandLine.Tests")]
6+
47
namespace FastFetch
58
{
69
public class Program
710
{
811
public static void Main(string[] args)
912
{
1013
GVFSPlatformLoader.Initialize();
11-
RootCommand rootCommand = FastFetchVerb.BuildRootCommand();
14+
RootCommand rootCommand = BuildRootCommand();
1215
rootCommand.Parse(args).Invoke();
1316
}
17+
18+
internal static RootCommand BuildRootCommand() => FastFetchVerb.BuildRootCommand();
1419
}
1520
}
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
using System.CommandLine;
2+
using System.CommandLine.Parsing;
3+
using System.Linq;
4+
using NUnit.Framework;
5+
6+
namespace GVFS.CommandLine.Tests
7+
{
8+
/// <summary>
9+
/// Tests that FastFetch CLI parsing matches the original CommandLineParser behavior.
10+
/// Verifies short aliases, defaults, and option names are backward-compatible.
11+
/// </summary>
12+
[TestFixture]
13+
public class FastFetchCliTests
14+
{
15+
private RootCommand rootCommand;
16+
17+
[SetUp]
18+
public void SetUp()
19+
{
20+
rootCommand = FastFetch.Program.BuildRootCommand();
21+
}
22+
23+
#region Short Aliases
24+
25+
[Test]
26+
public void CommitOption_HasShortAlias_C()
27+
{
28+
var opt = FindOption("--commit");
29+
Assert.That(opt, Is.Not.Null, "Expected --commit option to exist");
30+
Assert.That(opt.Aliases, Does.Contain("-c"), "Expected -c short alias for --commit");
31+
}
32+
33+
[Test]
34+
public void BranchOption_HasShortAlias_B()
35+
{
36+
var opt = FindOption("--branch");
37+
Assert.That(opt, Is.Not.Null, "Expected --branch option to exist");
38+
Assert.That(opt.Aliases, Does.Contain("-b"), "Expected -b short alias for --branch");
39+
}
40+
41+
[Test]
42+
public void MaxRetriesOption_HasShortAlias_R()
43+
{
44+
var opt = FindOption("--max-retries");
45+
Assert.That(opt, Is.Not.Null, "Expected --max-retries option to exist");
46+
Assert.That(opt.Aliases, Does.Contain("-r"), "Expected -r short alias for --max-retries");
47+
}
48+
49+
[TestCase("-c", "abc123")]
50+
[TestCase("-b", "main")]
51+
[TestCase("-r", "5")]
52+
public void ShortAliases_ParseCorrectly(string alias, string value)
53+
{
54+
var parseResult = rootCommand.Parse(new[] { alias, value });
55+
Assert.That(parseResult.Errors, Is.Empty, $"Parsing '{alias} {value}' should produce no errors");
56+
}
57+
58+
#endregion
59+
60+
#region Default Values
61+
62+
[Test]
63+
public void ChunkSize_DefaultsTo4000()
64+
{
65+
var parseResult = rootCommand.Parse(System.Array.Empty<string>());
66+
var opt = FindOption<int>("--chunk-size");
67+
Assert.That(opt, Is.Not.Null);
68+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(4000),
69+
"ChunkSize should default to 4000 when not specified");
70+
}
71+
72+
[Test]
73+
public void MaxRetries_DefaultsTo10()
74+
{
75+
var parseResult = rootCommand.Parse(System.Array.Empty<string>());
76+
var opt = FindOption<int>("--max-retries");
77+
Assert.That(opt, Is.Not.Null);
78+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(10),
79+
"MaxRetries should default to 10 when not specified");
80+
}
81+
82+
[Test]
83+
public void Folders_DefaultsToEmptyString()
84+
{
85+
var parseResult = rootCommand.Parse(System.Array.Empty<string>());
86+
var opt = FindOption<string>("--folders");
87+
Assert.That(opt, Is.Not.Null);
88+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(""),
89+
"Folders should default to empty string when not specified");
90+
}
91+
92+
[Test]
93+
public void FoldersList_DefaultsToEmptyString()
94+
{
95+
var parseResult = rootCommand.Parse(System.Array.Empty<string>());
96+
var opt = FindOption<string>("--folders-list");
97+
Assert.That(opt, Is.Not.Null);
98+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(""),
99+
"FoldersList should default to empty string when not specified");
100+
}
101+
102+
[Test]
103+
public void BooleanOptions_DefaultToFalse()
104+
{
105+
var parseResult = rootCommand.Parse(System.Array.Empty<string>());
106+
107+
var checkout = FindOption<bool>("--checkout");
108+
var forceCheckout = FindOption<bool>("--force-checkout");
109+
var verbose = FindOption<bool>("--verbose");
110+
var allowIndexMetadata = FindOption<bool>("--allow-index-metadata-update-from-working-tree");
111+
112+
Assert.Multiple(() =>
113+
{
114+
Assert.That(parseResult.GetValue(checkout), Is.False, "--checkout should default to false");
115+
Assert.That(parseResult.GetValue(forceCheckout), Is.False, "--force-checkout should default to false");
116+
Assert.That(parseResult.GetValue(verbose), Is.False, "--verbose should default to false");
117+
Assert.That(parseResult.GetValue(allowIndexMetadata), Is.False, "--allow-index-metadata-update-from-working-tree should default to false");
118+
});
119+
}
120+
121+
[Test]
122+
public void IntThreadOptions_DefaultToZero()
123+
{
124+
var parseResult = rootCommand.Parse(System.Array.Empty<string>());
125+
126+
var search = FindOption<int>("--search-thread-count");
127+
var download = FindOption<int>("--download-thread-count");
128+
var index = FindOption<int>("--index-thread-count");
129+
var checkoutThread = FindOption<int>("--checkout-thread-count");
130+
131+
Assert.Multiple(() =>
132+
{
133+
Assert.That(parseResult.GetValue(search), Is.EqualTo(0));
134+
Assert.That(parseResult.GetValue(download), Is.EqualTo(0));
135+
Assert.That(parseResult.GetValue(index), Is.EqualTo(0));
136+
Assert.That(parseResult.GetValue(checkoutThread), Is.EqualTo(0));
137+
});
138+
}
139+
140+
#endregion
141+
142+
#region Explicit Value Parsing
143+
144+
[Test]
145+
public void ChunkSize_ExplicitValue_Overrides()
146+
{
147+
var parseResult = rootCommand.Parse(new[] { "--chunk-size", "8000" });
148+
var opt = FindOption<int>("--chunk-size");
149+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(8000));
150+
}
151+
152+
[Test]
153+
public void MaxRetries_ExplicitValue_Overrides()
154+
{
155+
var parseResult = rootCommand.Parse(new[] { "--max-retries", "3" });
156+
var opt = FindOption<int>("--max-retries");
157+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(3));
158+
}
159+
160+
[Test]
161+
public void CommitAndBranch_ParseWithShortAliases()
162+
{
163+
var parseResult = rootCommand.Parse(new[] { "-c", "abc123", "-b", "feature/test" });
164+
var commitOpt = FindOption<string>("--commit");
165+
var branchOpt = FindOption<string>("--branch");
166+
Assert.Multiple(() =>
167+
{
168+
Assert.That(parseResult.GetValue(commitOpt), Is.EqualTo("abc123"));
169+
Assert.That(parseResult.GetValue(branchOpt), Is.EqualTo("feature/test"));
170+
});
171+
}
172+
173+
[Test]
174+
public void AllStringOptions_ParseCorrectly()
175+
{
176+
var parseResult = rootCommand.Parse(new[]
177+
{
178+
"--commit", "abc123",
179+
"--branch", "main",
180+
"--cache-server-url", "https://cache.example.com",
181+
"--git-path", @"C:\Program Files\Git\bin\git.exe",
182+
"--folders", "src;lib",
183+
"--folders-list", @"C:\folders.txt",
184+
"--parent-activity-id", "12345678-1234-1234-1234-123456789012"
185+
});
186+
187+
Assert.That(parseResult.Errors, Is.Empty, "All string options should parse without errors");
188+
}
189+
190+
[Test]
191+
public void MaxRetries_ShortAlias_R_ParsesCorrectly()
192+
{
193+
var parseResult = rootCommand.Parse(new[] { "-r", "5" });
194+
var opt = FindOption<int>("--max-retries");
195+
Assert.That(parseResult.GetValue(opt), Is.EqualTo(5));
196+
}
197+
198+
#endregion
199+
200+
#region All Expected Options Exist
201+
202+
[Test]
203+
public void AllExpectedOptions_Exist()
204+
{
205+
var expectedOptions = new[]
206+
{
207+
"--commit", "--branch", "--cache-server-url", "--chunk-size",
208+
"--checkout", "--force-checkout", "--search-thread-count",
209+
"--download-thread-count", "--index-thread-count", "--checkout-thread-count",
210+
"--max-retries", "--git-path", "--folders", "--folders-list",
211+
"--allow-index-metadata-update-from-working-tree", "--verbose",
212+
"--parent-activity-id"
213+
};
214+
215+
foreach (var optName in expectedOptions)
216+
{
217+
Assert.That(FindOption(optName), Is.Not.Null, $"Expected option {optName} to exist");
218+
}
219+
}
220+
221+
#endregion
222+
223+
#region Helpers
224+
225+
private Option FindOption(string name)
226+
{
227+
return rootCommand.Options.FirstOrDefault(o => o.Name == name || o.Aliases.Contains(name));
228+
}
229+
230+
private Option<T> FindOption<T>(string name)
231+
{
232+
return rootCommand.Options.FirstOrDefault(o => o.Name == name || o.Aliases.Contains(name)) as Option<T>;
233+
}
234+
235+
#endregion
236+
}
237+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net471</TargetFramework>
5+
<IsTestProject>true</IsTestProject>
6+
<OutputType>Exe</OutputType>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="NUnitLite" />
11+
<PackageReference Include="NUnit3TestAdapter" />
12+
<PackageReference Include="System.CommandLine" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<ProjectReference Include="..\FastFetch\FastFetch.csproj" />
17+
<ProjectReference Include="..\GVFS.Mount\GVFS.Mount.csproj" />
18+
<ProjectReference Include="..\GVFS\GVFS.csproj" />
19+
</ItemGroup>
20+
21+
</Project>

0 commit comments

Comments
 (0)