Skip to content

Commit 69ed5c2

Browse files
authored
Proc.StartLongRunning allows you to yield after waiting for started. (#16)
Without necessarily killing the process (unless disposing the long running subscription). Its now also easier to stop buffering process output after started handler (optional)
1 parent 9668d9f commit 69ed5c2

File tree

5 files changed

+71
-44
lines changed

5 files changed

+71
-44
lines changed

src/Proc/ObservableProcess.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,11 +143,12 @@ public virtual IDisposable SubscribeLines(Action<LineOut> onNext, Action<Excepti
143143
public virtual IDisposable SubscribeLinesAndCharacters(
144144
Action<LineOut> onNext, Action<Exception> onError,
145145
Action<CharactersOut> onNextCharacters,
146-
Action<Exception> onExceptionCharacters
147-
) =>
146+
Action<Exception> onExceptionCharacters,
147+
Action? onCompleted = null
148+
) =>
148149
Subscribe(
149-
Observer.Create(onNext, onError, delegate { }),
150-
Observer.Create(onNextCharacters, onExceptionCharacters, delegate { })
150+
Observer.Create(onNext, onError, onCompleted ?? delegate { }),
151+
Observer.Create(onNextCharacters, onExceptionCharacters, onCompleted ?? delegate { })
151152
);
152153

153154
public virtual IDisposable SubscribeLines(Action<LineOut> onNext) =>

src/Proc/Proc.StartLongRunning.cs

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,16 @@ internal LongRunningApplicationSubscription(ObservableProcess process, Composite
1717

1818
private IDisposable Subscription { get; }
1919

20-
public ObservableProcess Process { get; }
20+
private ObservableProcess Process { get; }
2121

22+
public bool Running { get; internal set; }
23+
24+
internal ManualResetEvent WaitHandle { get; } = new(false);
25+
26+
/// <inheritdoc cref="ObservableProcessBase{TConsoleOut}.SendControlC(int)"/>>
2227
public bool SendControlC(int processId) => Process.SendControlC(processId);
28+
29+
/// <inheritdoc cref="ObservableProcessBase{TConsoleOut}.SendControlC()"/>>
2330
public void SendControlC() => Process.SendControlC();
2431

2532
public void Dispose()
@@ -52,48 +59,50 @@ public static partial class Proc
5259
/// <returns>The exit code and whether the process completed</returns>
5360
public static LongRunningApplicationSubscription StartLongRunning(LongRunningArguments arguments, TimeSpan waitForStartedConfirmation, IConsoleOutWriter consoleOutWriter = null)
5461
{
55-
var started = false;
56-
var confirmWaitHandle = new ManualResetEvent(false);
5762
var composite = new CompositeDisposable();
5863
var process = new ObservableProcess(arguments);
64+
var subscription = new LongRunningApplicationSubscription(process, composite);
5965
consoleOutWriter ??= new ConsoleOutColorWriter();
6066

6167
var startedConfirmation = arguments.StartedConfirmationHandler ?? (_ => true);
6268

6369
if (arguments.StartedConfirmationHandler != null && arguments.StopBufferingAfterStarted)
64-
arguments.KeepBufferingLines = _ => !started;
70+
arguments.KeepBufferingLines = _ => !subscription.Running;
6571

6672
Exception seenException = null;
6773
composite.Add(process);
6874
composite.Add(process.SubscribeLinesAndCharacters(
6975
l =>
7076
{
71-
if (startedConfirmation(l))
72-
{
73-
confirmWaitHandle.Set();
74-
started = true;
75-
}
77+
if (!startedConfirmation(l)) return;
78+
subscription.Running = true;
79+
subscription.WaitHandle.Set();
7680
},
7781
e =>
7882
{
7983
seenException = e;
80-
confirmWaitHandle.Set();
84+
subscription.Running = false;
85+
subscription.WaitHandle.Set();
8186
},
8287
l => consoleOutWriter.Write(l),
83-
l => consoleOutWriter.Write(l)
84-
)
88+
l => consoleOutWriter.Write(l),
89+
onCompleted: () =>
90+
{
91+
subscription.Running = false;
92+
subscription.WaitHandle.Set();
93+
})
8594
);
8695

8796
if (seenException != null) ExceptionDispatchInfo.Capture(seenException).Throw();
8897
if (arguments.StartedConfirmationHandler == null)
8998
{
90-
confirmWaitHandle.Set();
91-
started = true;
99+
subscription.Running = true;
100+
subscription.WaitHandle.Set();
92101
}
93102
else
94103
{
95-
var completed = confirmWaitHandle.WaitOne(waitForStartedConfirmation);
96-
if (completed) return new(process, composite);
104+
var completed = subscription.WaitHandle.WaitOne(waitForStartedConfirmation);
105+
if (completed) return subscription;
97106
var pwd = arguments.WorkingDirectory;
98107
var args = arguments.Args.NaivelyQuoteArguments();
99108
var printBinary = arguments.OnlyPrintBinaryInExceptionMessage
@@ -102,7 +111,7 @@ public static LongRunningApplicationSubscription StartLongRunning(LongRunningArg
102111
throw new ProcExecException($"Could not yield started confirmation after {waitForStartedConfirmation} while running {printBinary}");
103112
}
104113

105-
return new(process, composite);
114+
return subscription;
106115
}
107116
}
108117
}

tests/Proc.Tests/LineOutputTests.cs

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4-
using System.Text;
54
using FluentAssertions;
65
using ProcNet.Std;
76
using Xunit;
7+
using Xunit.Abstractions;
88

99
namespace ProcNet.Tests
1010
{
@@ -19,7 +19,7 @@ public void OverwriteLines()
1919
}
2020

2121

22-
public class LineOutputTestCases : TestsBase
22+
public class LineOutputTestCases(ITestOutputHelper output) : TestsBase
2323
{
2424
private static readonly string _expected = @"
2525
Windows IP Configuration
@@ -99,7 +99,7 @@ public void SubscribeLinesSeesAllLines()
9999
[Fact]
100100
public void ConsoleWriterSeesAllLines()
101101
{
102-
var writer = new TestConsoleOutWriter();
102+
var writer = new TestConsoleOutWriter(output);
103103
var args = TestCaseArguments("MoreText");
104104
var result = Proc.Start(args, WaitTimeout, writer);
105105
result.ExitCode.Should().HaveValue();
@@ -110,15 +110,5 @@ public void ConsoleWriterSeesAllLines()
110110
lines[i].Should().Be(_expectedLines[i], i.ToString());
111111
}
112112

113-
public class TestConsoleOutWriter : IConsoleOutWriter
114-
{
115-
private readonly StringBuilder _sb = new StringBuilder();
116-
public string[] Lines => _sb.ToString().Replace("\r\n", "\n").Split(new [] {"\n"}, StringSplitOptions.None);
117-
public string Text => _sb.ToString();
118-
119-
public void Write(Exception e) => throw e;
120-
121-
public void Write(ConsoleOut consoleOut) => consoleOut.CharsOrString(c=>_sb.Append(new string(c)), s=>_sb.AppendLine(s));
122-
}
123113
}
124114
}

tests/Proc.Tests/LongRunningTests.cs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
using System;
22
using System.Diagnostics;
3-
using System.Text;
43
using System.Threading.Tasks;
54
using FluentAssertions;
6-
using ProcNet.Std;
7-
using FluentAssertions;
85
using Xunit;
6+
using Xunit.Abstractions;
97

108
namespace ProcNet.Tests
119
{
12-
public class LongRunningTests : TestsBase
10+
public class LongRunningTests(ITestOutputHelper output) : TestsBase
1311
{
1412
[Fact]
1513
public async Task LongRunningShouldSeeAllOutput()
1614
{
1715
var args = LongRunningTestCaseArguments("LongRunning");
1816
args.StartedConfirmationHandler = l => l.Line == "Started!";
1917

20-
var outputWriter = new LineOutputTestCases.TestConsoleOutWriter();
21-
using (var result = Proc.StartLongRunning(args, WaitTimeout, outputWriter))
18+
var outputWriter = new TestConsoleOutWriter(output);
19+
20+
using (var process = Proc.StartLongRunning(args, WaitTimeout, outputWriter))
21+
{
22+
process.Running.Should().BeTrue();
2223
await Task.Delay(TimeSpan.FromSeconds(2));
24+
process.Running.Should().BeFalse();
25+
}
2326

2427
var lines = outputWriter.Lines;
2528
lines.Length.Should().BeGreaterThan(0);
@@ -35,11 +38,12 @@ public async Task LongRunningShouldStopBufferingOutputWhenAsked()
3538
args.StartedConfirmationHandler = l => l.Line == "Started!";
3639
args.StopBufferingAfterStarted = true;
3740

38-
var outputWriter = new LineOutputTestCases.TestConsoleOutWriter();
41+
var outputWriter = new TestConsoleOutWriter(output);
3942
var sw = Stopwatch.StartNew();
4043

41-
using (var result = Proc.StartLongRunning(args, WaitTimeout, outputWriter))
44+
using (var process = Proc.StartLongRunning(args, WaitTimeout, outputWriter))
4245
{
46+
process.Running.Should().BeTrue();
4347
sw.Elapsed.Should().BeGreaterThan(TimeSpan.FromSeconds(1));
4448
var lines = outputWriter.Lines;
4549
lines.Length.Should().BeGreaterThan(0);
@@ -49,6 +53,7 @@ public async Task LongRunningShouldStopBufferingOutputWhenAsked()
4953
await Task.Delay(TimeSpan.FromSeconds(2));
5054
lines.Should().NotContain(s => s.StartsWith("Data after startup:"));
5155
}
56+
5257
// we dispose before the program's completion
5358
sw.Elapsed.Should().BeLessThan(TimeSpan.FromSeconds(20));
5459

@@ -58,10 +63,13 @@ public async Task LongRunningShouldStopBufferingOutputWhenAsked()
5863
public async Task LongRunningWithoutConfirmationHandler()
5964
{
6065
var args = LongRunningTestCaseArguments("LongRunning");
61-
var outputWriter = new LineOutputTestCases.TestConsoleOutWriter();
66+
var outputWriter = new TestConsoleOutWriter(output);
6267

63-
using (var result = Proc.StartLongRunning(args, WaitTimeout, outputWriter))
68+
using (var process = Proc.StartLongRunning(args, WaitTimeout, outputWriter))
69+
{
70+
process.Running.Should().BeTrue();
6471
await Task.Delay(TimeSpan.FromSeconds(2));
72+
}
6573

6674
var lines = outputWriter.Lines;
6775
lines.Should().Contain(s => s.StartsWith("Starting up:"));
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using System;
2+
using System.Text;
3+
using ProcNet.Std;
4+
using Xunit.Abstractions;
5+
6+
public class TestConsoleOutWriter(ITestOutputHelper output) : IConsoleOutWriter
7+
{
8+
private readonly StringBuilder _sb = new();
9+
public string[] Lines => _sb.ToString().Replace("\r\n", "\n").Split(new [] {"\n"}, StringSplitOptions.None);
10+
public string Text => _sb.ToString();
11+
12+
public void Write(Exception e) => throw e;
13+
14+
public void Write(ConsoleOut consoleOut)
15+
{
16+
consoleOut.CharsOrString(c => _sb.Append(new string(c)), s => _sb.AppendLine(s));
17+
consoleOut.CharsOrString(c => output.WriteLine(new string(c)), output.WriteLine);
18+
}
19+
}

0 commit comments

Comments
 (0)