Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions RunCommand.Test/RunCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
{
private static readonly string[] ExpectedALast = ["a", "last"];
private static readonly string[] ExpectedALastNext = ["a", "last", "next"];
private static readonly string[] ExpectedDone = ["done"];

private static string GetCopyCommand(string source, string destination) =>
RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
Expand Down Expand Up @@ -971,6 +972,50 @@
}
}

[TestMethod]
[DoNotParallelize]
public async Task ExecuteAsyncShouldNotSpinWhileACommandThatClosedItsOutputKeepsRunning()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.Inconclusive("Needs a shell that can close its own output streams and stay alive. The read loop this covers is in platform independent code, so the other legs cover it.");
}

Check warning on line 982 in RunCommand.Test/RunCommandTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use '[OSCondition]' attribute instead of 'RuntimeInformation.IsOSPlatform' calls with early return or 'Assert.Inconclusive'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_RunCommand&issues=AaDqKcWgCyg2j_3k87yl&open=AaDqKcWgCyg2j_3k87yl&pullRequest=97

// Standard error reaches end of stream at once while the command runs on. A reader that
// keeps reading a stream after end of stream gets 0 back straight away every time, so it
// spins on a core for the whole run instead of waiting.
const int runMilliseconds = 2000;
List<string> output = [];

using Process self = Process.GetCurrentProcess();
TimeSpan before = self.TotalProcessorTime;

int exitCode = await RunCommand.ExecuteAsync(
"sh",
["-c", $"exec 2>&-; sleep {runMilliseconds / 1000}; echo done"],
new LineOutputHandler(onStandardOutput: line =>
{
lock (output)
{
output.Add(line);
}
})).ConfigureAwait(false);

Check warning on line 1002 in RunCommand.Test/RunCommandTests.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Consider using the overload that accepts a CancellationToken and pass 'TestContext.CancellationToken'

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_RunCommand&issues=AaDqKcWgCyg2j_3k87yk&open=AaDqKcWgCyg2j_3k87yk&pullRequest=97

self.Refresh();
TimeSpan used = self.TotalProcessorTime - before;

Assert.AreEqual(0, exitCode);
Assert.AreSequenceEqual(ExpectedDone, output, "Expected standard output to be delivered in full.");

// This is process-wide CPU, which is why the test opts out of running alongside the others.
// A spinning reader costs at least a full core for the whole run, so half of the run is
// clear of the test host's own background work and still well short of a spin.
Assert.IsLessThan(
runMilliseconds / 2,
used.TotalMilliseconds,
$"Expected the reader to wait rather than spin, but the process used {used.TotalMilliseconds:F0} ms of CPU during a {runMilliseconds} ms run.");
}

/// <summary>
/// Returns a command that prints a file's contents unchanged.
/// </summary>
Expand Down
Loading