-
-
Notifications
You must be signed in to change notification settings - Fork 275
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Review fixes. Add unit tests for exit condition.
- Loading branch information
Showing
4 changed files
with
123 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
using CliFx; | ||
using CliFx.Attributes; | ||
using CliFx.Infrastructure; | ||
|
||
namespace CliWrap.Tests.Dummy.Commands; | ||
|
||
[Command("run process")] | ||
public class RunProcessCommand : ICommand | ||
{ | ||
[CommandOption("path")] | ||
public string FilePath { get; init; } = string.Empty; | ||
|
||
[CommandOption("arguments")] | ||
public string Arguments { get; init; } = string.Empty; | ||
|
||
public ValueTask ExecuteAsync(IConsole console) | ||
{ | ||
var startInfo = new ProcessStartInfo | ||
{ | ||
FileName = FilePath, | ||
Arguments = Arguments, | ||
RedirectStandardInput = true, | ||
RedirectStandardOutput = true, | ||
RedirectStandardError = true, | ||
UseShellExecute = false, | ||
CreateNoWindow = true | ||
}; | ||
|
||
var process = new Process(); | ||
process.StartInfo = startInfo; | ||
process.Start(); | ||
|
||
console.Output.WriteLine(process.Id); | ||
|
||
return default; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
using System; | ||
using System.Diagnostics; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using Xunit; | ||
|
||
namespace CliWrap.Tests; | ||
|
||
public class ExitConditionSpecs() | ||
{ | ||
[Fact(Timeout = 15000)] | ||
public async Task I_can_execute_a_command_that_creates_child_process_resuing_standard_output_and_finish_after_child_process_exits() | ||
{ | ||
// Arrange | ||
var cmd = this.PrepareCommand(line => { }); | ||
Check failure on line 15 in CliWrap.Tests/ExitConditionSpecs.cs
|
||
|
||
// Act | ||
var executionStart = DateTime.UtcNow; | ||
var result = await cmd.ExecuteAsync(); | ||
var executionFinish = DateTime.UtcNow; | ||
|
||
// Assert | ||
executionFinish | ||
.Subtract(executionStart) | ||
.Should() | ||
.BeGreaterThanOrEqualTo(TimeSpan.FromSeconds(3)); | ||
} | ||
|
||
[Fact(Timeout = 15000)] | ||
public async Task I_can_execute_a_command_that_creates_child_process_resuing_standard_output_and_finish_instantly_after_main_process_exits() | ||
{ | ||
// Arrange | ||
int childProcessId = -1; | ||
Check failure on line 33 in CliWrap.Tests/ExitConditionSpecs.cs
|
||
var cmd = this.PrepareCommand(line => childProcessId = Convert.ToInt32(line.Trim())) | ||
.WithExitCondition(CommandExitCondition.ProcessExited); | ||
|
||
// Act | ||
var executionStart = DateTime.UtcNow; | ||
var result = await cmd.ExecuteAsync(); | ||
var executionFinish = DateTime.UtcNow; | ||
|
||
var process = Process.GetProcessById(childProcessId); | ||
|
||
// Assert | ||
executionFinish.Subtract(executionStart).Should().BeLessThan(TimeSpan.FromSeconds(3)); | ||
|
||
process.HasExited.Should().BeFalse(); | ||
} | ||
|
||
/// <summary> | ||
/// Prepares a command that will create a sleeping child process and return its id via standard output. | ||
/// </summary> | ||
private Command PrepareCommand(Action<string> onStandardOutput) | ||
{ | ||
var cmd = Cli.Wrap(Dummy.Program.FilePath) | ||
.WithArguments( | ||
[ | ||
"run", | ||
"process", | ||
"--path", | ||
Dummy.Program.FilePath, | ||
"--arguments", | ||
"sleep 00:00:03" | ||
] | ||
) | ||
.WithStandardOutputPipe(PipeTarget.ToDelegate(line => onStandardOutput(line))) | ||
.WithStandardErrorPipe( | ||
PipeTarget.ToDelegate(line => Console.WriteLine($"Error: {line}")) | ||
); | ||
|
||
return cmd; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,21 +1,18 @@ | ||
using System; | ||
|
||
namespace CliWrap; | ||
namespace CliWrap; | ||
|
||
/// <summary> | ||
/// Strategy used for veryfing the end of command exectuion. | ||
/// Strategy used for identifying the end of command exectuion. | ||
/// </summary> | ||
[Flags] | ||
public enum CommandExitCondition | ||
{ | ||
/// <summary> | ||
/// Command is finished when process is finished and all pipes are closed. | ||
/// Command execution is considered finished when the process exits and all standard input and output streams are closed. | ||
/// </summary> | ||
PipesClosed = 0, | ||
|
||
/// <summary> | ||
/// Command is finished when the main process exits, | ||
/// even if they are child processes still running, which are reusing the same output/error streams. | ||
/// Command execution is considered finished when the process exits, even if the process's standard input and output streams are still open, | ||
/// for example after being inherited by a grandchild process. | ||
/// </summary> | ||
ProcessExited = 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters