Skip to content

Commit 65a92f5

Browse files
committed
added tests to SuggestionStore::GetCompletions
Fix dotnet#2147
1 parent 1c23816 commit 65a92f5

File tree

6 files changed

+229
-80
lines changed

6 files changed

+229
-80
lines changed

src/System.CommandLine.Suggest.Tests/DotnetSuggestEndToEndTests.cs

Lines changed: 62 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -3,84 +3,31 @@
33

44
using FluentAssertions;
55
using System.CommandLine.Tests.Utility;
6-
using System.IO;
7-
using System.Linq;
86
using System.Text;
97
using Xunit.Abstractions;
108
using static System.Environment;
119
using Process = System.CommandLine.Tests.Utility.Process;
1210

1311
namespace System.CommandLine.Suggest.Tests
1412
{
15-
public class DotnetSuggestEndToEndTests : IDisposable
13+
public class DotnetSuggestEndToEndTests : TestsWithTestApps
1614
{
17-
private readonly ITestOutputHelper _output;
18-
private readonly FileInfo _endToEndTestApp;
19-
private readonly FileInfo _dotnetSuggest;
20-
private readonly (string, string)[] _environmentVariables;
21-
private readonly DirectoryInfo _dotnetHostDir = DotnetMuxer.Path.Directory;
22-
private static string _testRoot;
23-
24-
public DotnetSuggestEndToEndTests(ITestOutputHelper output)
15+
public DotnetSuggestEndToEndTests(ITestOutputHelper output) : base(output)
2516
{
26-
_output = output;
27-
28-
// delete sentinel files for EndToEndTestApp in order to trigger registration when it's run
29-
var sentinelsDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "system-commandline-sentinel-files"));
30-
31-
if (sentinelsDir.Exists)
32-
{
33-
var sentinels = sentinelsDir.GetFiles("*EndToEndTestApp*");
34-
35-
foreach (var sentinel in sentinels)
36-
{
37-
sentinel.Delete();
38-
}
39-
}
40-
41-
var currentDirectory = Path.Combine(
42-
Directory.GetCurrentDirectory(),
43-
"TestAssets");
44-
45-
_endToEndTestApp = new DirectoryInfo(currentDirectory)
46-
.GetFiles("EndToEndTestApp".ExecutableName())
47-
.SingleOrDefault();
48-
49-
_dotnetSuggest = new DirectoryInfo(currentDirectory)
50-
.GetFiles("dotnet-suggest".ExecutableName())
51-
.SingleOrDefault();
52-
53-
PrepareTestHomeDirectoryToAvoidPolluteBuildMachineHome();
54-
55-
_environmentVariables = new[] {
56-
("DOTNET_ROOT", _dotnetHostDir.FullName),
57-
("INTERNAL_TEST_DOTNET_SUGGEST_HOME", _testRoot)};
58-
}
59-
60-
public void Dispose()
61-
{
62-
if (_testRoot != null && Directory.Exists(_testRoot))
63-
{
64-
Directory.Delete(_testRoot, recursive: true);
65-
}
66-
}
67-
68-
private static void PrepareTestHomeDirectoryToAvoidPolluteBuildMachineHome()
69-
{
70-
_testRoot = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
71-
Directory.CreateDirectory(_testRoot);
7217
}
7318

7419
[ReleaseBuildOnlyFact]
7520
public void Test_app_supplies_suggestions()
7621
{
7722
var stdOut = new StringBuilder();
78-
23+
24+
Output.WriteLine($"_endToEndTestApp.FullName: {EndToEndTestApp.FullName}");
25+
7926
Process.RunToCompletion(
80-
_endToEndTestApp.FullName,
27+
EndToEndTestApp.FullName,
8128
"[suggest:1] \"a\"",
8229
stdOut: value => stdOut.AppendLine(value),
83-
environmentVariables: _environmentVariables);
30+
environmentVariables: EnvironmentVariables);
8431

8532
stdOut.ToString()
8633
.Should()
@@ -92,26 +39,26 @@ public void Dotnet_suggest_provides_suggestions_for_app()
9239
{
9340
// run "dotnet-suggest register" in explicit way
9441
Process.RunToCompletion(
95-
_dotnetSuggest.FullName,
96-
$"register --command-path \"{_endToEndTestApp.FullName}\"",
97-
stdOut: s => _output.WriteLine(s),
98-
stdErr: s => _output.WriteLine(s),
99-
environmentVariables: _environmentVariables).Should().Be(0);
42+
DotnetSuggest.FullName,
43+
$"register --command-path \"{EndToEndTestApp.FullName}\"",
44+
stdOut: s => Output.WriteLine(s),
45+
stdErr: s => Output.WriteLine(s),
46+
environmentVariables: EnvironmentVariables).Should().Be(0);
10047

10148
var stdOut = new StringBuilder();
10249
var stdErr = new StringBuilder();
10350

10451
var commandLineToComplete = "a";
10552

10653
Process.RunToCompletion(
107-
_dotnetSuggest.FullName,
108-
$"get -e \"{_endToEndTestApp.FullName}\" --position {commandLineToComplete.Length} -- \"{commandLineToComplete}\"",
54+
DotnetSuggest.FullName,
55+
$"get -e \"{EndToEndTestApp.FullName}\" --position {commandLineToComplete.Length} -- \"{commandLineToComplete}\"",
10956
stdOut: value => stdOut.AppendLine(value),
11057
stdErr: value => stdErr.AppendLine(value),
111-
environmentVariables: _environmentVariables);
58+
environmentVariables: EnvironmentVariables);
11259

113-
_output.WriteLine($"stdOut:{NewLine}{stdOut}{NewLine}");
114-
_output.WriteLine($"stdErr:{NewLine}{stdErr}{NewLine}");
60+
Output.WriteLine($"stdOut:{NewLine}{stdOut}{NewLine}");
61+
Output.WriteLine($"stdErr:{NewLine}{stdErr}{NewLine}");
11562

11663
stdErr.ToString()
11764
.Should()
@@ -127,26 +74,26 @@ public void Dotnet_suggest_provides_suggestions_for_app_with_only_commandname()
12774
{
12875
// run "dotnet-suggest register" in explicit way
12976
Process.RunToCompletion(
130-
_dotnetSuggest.FullName,
131-
$"register --command-path \"{_endToEndTestApp.FullName}\"",
132-
stdOut: s => _output.WriteLine(s),
133-
stdErr: s => _output.WriteLine(s),
134-
environmentVariables: _environmentVariables).Should().Be(0);
77+
DotnetSuggest.FullName,
78+
$"register --command-path \"{EndToEndTestApp.FullName}\"",
79+
stdOut: s => Output.WriteLine(s),
80+
stdErr: s => Output.WriteLine(s),
81+
environmentVariables: EnvironmentVariables).Should().Be(0);
13582

13683
var stdOut = new StringBuilder();
13784
var stdErr = new StringBuilder();
13885

13986
var commandLineToComplete = "a ";
14087

14188
Process.RunToCompletion(
142-
_dotnetSuggest.FullName,
143-
$"get -e \"{_endToEndTestApp.FullName}\" --position {commandLineToComplete.Length} -- \"{commandLineToComplete}\"",
89+
DotnetSuggest.FullName,
90+
$"get -e \"{EndToEndTestApp.FullName}\" --position {commandLineToComplete.Length} -- \"{commandLineToComplete}\"",
14491
stdOut: value => stdOut.AppendLine(value),
14592
stdErr: value => stdErr.AppendLine(value),
146-
environmentVariables: _environmentVariables);
93+
environmentVariables: EnvironmentVariables);
14794

148-
_output.WriteLine($"stdOut:{NewLine}{stdOut}{NewLine}");
149-
_output.WriteLine($"stdErr:{NewLine}{stdErr}{NewLine}");
95+
Output.WriteLine($"stdOut:{NewLine}{stdOut}{NewLine}");
96+
Output.WriteLine($"stdErr:{NewLine}{stdErr}{NewLine}");
15097

15198
stdErr.ToString()
15299
.Should()
@@ -156,5 +103,40 @@ public void Dotnet_suggest_provides_suggestions_for_app_with_only_commandname()
156103
.Should()
157104
.Be($"--apple{NewLine}--banana{NewLine}--cherry{NewLine}--durian{NewLine}--help{NewLine}--version{NewLine}-?{NewLine}-h{NewLine}/?{NewLine}/h{NewLine}");
158105
}
106+
107+
[ReleaseBuildOnlyFact]
108+
public void Dotnet_suggest_fails_to_provide_suggestions_because_app_faulted()
109+
{
110+
// run "dotnet-suggest register" in explicit way
111+
Process.RunToCompletion(
112+
DotnetSuggest.FullName,
113+
$"register --command-path \"{WaitAndFailTestApp.FullName}\"",
114+
stdOut: s => Output.WriteLine(s),
115+
stdErr: s => Output.WriteLine(s),
116+
environmentVariables: EnvironmentVariables).Should().Be(0);
117+
118+
var stdOut = new StringBuilder();
119+
var stdErr = new StringBuilder();
120+
121+
var commandLineToComplete = "a";
122+
123+
Process.RunToCompletion(
124+
DotnetSuggest.FullName,
125+
$"get -e \"{WaitAndFailTestApp.FullName}\" --position {commandLineToComplete.Length} -- \"{commandLineToComplete}\"",
126+
stdOut: value => stdOut.AppendLine(value),
127+
stdErr: value => stdErr.AppendLine(value),
128+
environmentVariables: EnvironmentVariables);
129+
130+
Output.WriteLine($"stdOut:{NewLine}{stdOut}{NewLine}");
131+
Output.WriteLine($"stdErr:{NewLine}{stdErr}{NewLine}");
132+
133+
stdErr.ToString()
134+
.Should()
135+
.BeEmpty();
136+
137+
stdOut.ToString()
138+
.Should()
139+
.BeEmpty();
140+
}
159141
}
160142
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) .NET Foundation and contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.CommandLine.Tests.Utility;
5+
using FluentAssertions;
6+
using Xunit.Abstractions;
7+
using static System.Environment;
8+
9+
namespace System.CommandLine.Suggest.Tests
10+
{
11+
public class SuggestionStoreTests : TestsWithTestApps
12+
{
13+
public SuggestionStoreTests(ITestOutputHelper output) : base(output)
14+
{
15+
}
16+
17+
[ReleaseBuildOnlyFact]
18+
public void GetCompletions_obtains_suggestions_successfully()
19+
{
20+
var store = new SuggestionStore();
21+
var completions = store.GetCompletions(EndToEndTestApp.FullName, "[suggest:1] \"a\"", TimeSpan.FromSeconds(1));
22+
completions.Should().Be($"--apple{NewLine}--banana{NewLine}--durian{NewLine}");
23+
}
24+
25+
[ReleaseBuildOnlyFact]
26+
public void GetCompletions_fails_to_obtain_suggestions_because_app_takes_too_long()
27+
{
28+
var store = new SuggestionStore();
29+
var appHangingTimeSpanArgument = TimeSpan.FromMilliseconds(2000).ToString();
30+
var completions = store
31+
.GetCompletions(WaitAndFailTestApp.FullName, appHangingTimeSpanArgument, TimeSpan.FromMilliseconds(1000));
32+
completions.Should().BeEmpty();
33+
}
34+
35+
[ReleaseBuildOnlyFact]
36+
public void GetCompletions_fails_to_obtain_suggestions_because_app_exited_with_nonzero_code()
37+
{
38+
var store = new SuggestionStore();
39+
var appHangingTimeSpanArgument = TimeSpan.FromMilliseconds(0).ToString();
40+
var completions = store
41+
.GetCompletions(WaitAndFailTestApp.FullName, appHangingTimeSpanArgument, TimeSpan.FromMilliseconds(1000));
42+
completions.Should().BeEmpty();
43+
}
44+
}
45+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
using System.IO;
2+
using System.Linq;
3+
using Xunit;
4+
using Xunit.Abstractions;
5+
6+
namespace System.CommandLine.Suggest.Tests;
7+
8+
[Collection("TestsWithTestApps")]
9+
public class TestsWithTestApps : IDisposable
10+
{
11+
protected readonly ITestOutputHelper Output;
12+
protected readonly FileInfo EndToEndTestApp;
13+
protected readonly FileInfo WaitAndFailTestApp;
14+
protected readonly FileInfo DotnetSuggest;
15+
protected readonly (string, string)[] EnvironmentVariables;
16+
private readonly DirectoryInfo _dotnetHostDir = DotnetMuxer.Path.Directory;
17+
private static string _testRoot;
18+
19+
protected TestsWithTestApps(ITestOutputHelper output)
20+
{
21+
Output = output;
22+
23+
// delete sentinel files for TestApps in order to trigger registration when it's run
24+
var sentinelsDir = new DirectoryInfo(Path.Combine(Path.GetTempPath(), "system-commandline-sentinel-files"));
25+
26+
if (sentinelsDir.Exists)
27+
{
28+
var sentinels = sentinelsDir
29+
.EnumerateFiles()
30+
.Where(f => f.Name.Contains("EndToEndTestApp") || f.Name.Contains("WaitAndFailTestApp"));
31+
32+
foreach (var sentinel in sentinels)
33+
{
34+
sentinel.Delete();
35+
}
36+
}
37+
38+
var currentDirectory = Path.Combine(
39+
Directory.GetCurrentDirectory(),
40+
"TestAssets");
41+
42+
EndToEndTestApp = new DirectoryInfo(currentDirectory)
43+
.GetFiles("EndToEndTestApp".ExecutableName())
44+
.SingleOrDefault();
45+
46+
WaitAndFailTestApp = new DirectoryInfo(currentDirectory)
47+
.GetFiles("WaitAndFailTestApp".ExecutableName())
48+
.SingleOrDefault();
49+
50+
DotnetSuggest = new DirectoryInfo(currentDirectory)
51+
.GetFiles("dotnet-suggest".ExecutableName())
52+
.SingleOrDefault();
53+
54+
PrepareTestHomeDirectoryToAvoidPolluteBuildMachineHome();
55+
56+
EnvironmentVariables = new[] {
57+
("DOTNET_ROOT", _dotnetHostDir.FullName),
58+
("INTERNAL_TEST_DOTNET_SUGGEST_HOME", _testRoot)};
59+
}
60+
61+
public void Dispose()
62+
{
63+
if (_testRoot != null && Directory.Exists(_testRoot))
64+
{
65+
Directory.Delete(_testRoot, recursive: true);
66+
}
67+
}
68+
69+
private static void PrepareTestHomeDirectoryToAvoidPolluteBuildMachineHome()
70+
{
71+
_testRoot = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
72+
Directory.CreateDirectory(_testRoot);
73+
}
74+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
4+
namespace WaitAndFailTestApp;
5+
6+
public class Program
7+
{
8+
private static TimeSpan defaultWait = TimeSpan.FromMilliseconds(3000);
9+
10+
//we should not be able to receive any suggestion from this test app,
11+
//so we are not constructing it using CliConfiguration
12+
13+
static async Task Main(string[] args)
14+
{
15+
var waitPeriod = args.Length > 0 && int.TryParse(args[0], out var millisecondsToWaitParsed)
16+
? TimeSpan.FromMilliseconds(millisecondsToWaitParsed)
17+
: defaultWait;
18+
19+
await Task.Delay(waitPeriod);
20+
Environment.ExitCode = 1;
21+
22+
Console.WriteLine("this 'suggestion' is provided too late and/or with invalid app exit code");
23+
}
24+
}
25+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<ItemGroup>
4+
<ProjectReference Include="..\..\System.CommandLine\System.CommandLine.csproj" />
5+
</ItemGroup>
6+
7+
<PropertyGroup>
8+
<OutputType>Exe</OutputType>
9+
<TargetFramework>net7.0</TargetFramework>
10+
</PropertyGroup>
11+
12+
</Project>

src/System.CommandLine.Suggest.Tests/dotnet-suggest.Tests.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
<Content Remove="EndToEndTestApp/**" />
1414
<EmbeddedResource Remove="EndToEndTestApp/**" />
1515
<None Remove="EndToEndTestApp/**" />
16+
17+
<Compile Remove="WaitAndFailTestApp/**" />
18+
<Content Remove="WaitAndFailTestApp/**" />
19+
<EmbeddedResource Remove="WaitAndFailTestApp/**" />
20+
<None Remove="WaitAndFailTestApp/**" />
1621
</ItemGroup>
1722

1823
<ItemGroup>
@@ -67,6 +72,12 @@
6772

6873
<MSBuild BuildInParallel="False" Projects="EndToEndTestApp/EndToEndTestApp.csproj" Targets="Build;Publish" Properties="UseAppHost=true;SelfContained=false;RuntimeIdentifier=$(Rid);PublishDir=$(TestAssetsPath);Configuration=Release">
6974
</MSBuild>
75+
76+
<MSBuild BuildInParallel="False" Projects="WaitAndFailTestApp/WaitAndFailTestApp.csproj" Targets="Restore" Properties="UseAppHost=true;SelfContained=false;RuntimeIdentifier=$(Rid);ForceRestoreToEvaluateSeparately=1;Configuration=Release">
77+
</MSBuild>
78+
79+
<MSBuild BuildInParallel="False" Projects="WaitAndFailTestApp/WaitAndFailTestApp.csproj" Targets="Build;Publish" Properties="UseAppHost=true;SelfContained=false;RuntimeIdentifier=$(Rid);PublishDir=$(TestAssetsPath);Configuration=Release">
80+
</MSBuild>
7081

7182
</Target>
7283

0 commit comments

Comments
 (0)