-
Notifications
You must be signed in to change notification settings - Fork 147
/
Copy pathProcessHelper.cs
100 lines (83 loc) · 3.4 KB
/
ProcessHelper.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// <copyright file="ProcessHelper.cs" company="Datadog">
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache 2 License.
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2017 Datadog, Inc.
// </copyright>
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Datadog.Trace.TestHelpers
{
/// <summary>
/// Drains the standard and error output of a process
/// </summary>
public partial class ProcessHelper : IDisposable
{
private readonly TaskCompletionSource<bool> _errorTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly TaskCompletionSource<bool> _outputTask = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly TaskCompletionSource<bool> _processExit = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly StringBuilder _outputBuffer = new();
private readonly StringBuilder _errorBuffer = new();
private readonly ReadOnlyDictionary<string, string> _environmentVariables;
public ProcessHelper(Process process, Action<string> onDataReceived = null, Action<string> onErrorReceived = null)
{
try
{
_environmentVariables = new(process.StartInfo.Environment);
}
catch
{
// ...
}
Task = Task.WhenAll(_outputTask.Task, _errorTask.Task, _processExit.Task);
Task.Factory.StartNew(
() =>
{
process.WaitForExit();
_processExit.TrySetResult(true);
},
TaskCreationOptions.LongRunning);
Task.Factory.StartNew(() => DrainOutput(process.StandardOutput, _outputBuffer, _outputTask, onDataReceived), TaskCreationOptions.LongRunning);
Task.Factory.StartNew(() => DrainOutput(process.StandardError, _errorBuffer, _errorTask, onErrorReceived ?? onDataReceived), TaskCreationOptions.LongRunning);
Process = process;
}
public Process Process { get; }
public string StandardOutput => _outputBuffer.ToString();
public string ErrorOutput => _errorBuffer.ToString();
public ReadOnlyDictionary<string, string> EnvironmentVariables => _environmentVariables;
public Task Task { get; }
public bool Drain(int timeout = Timeout.Infinite)
{
if (timeout != Timeout.Infinite)
{
timeout /= 2;
}
return _outputTask.Task.Wait(timeout) && _errorTask.Task.Wait(timeout);
}
public virtual void Dispose()
{
if (!Process.HasExited)
{
Process.Kill();
}
}
private void DrainOutput(StreamReader stream, StringBuilder buffer, TaskCompletionSource<bool> tcs, Action<string> onDataReceived)
{
while (stream.ReadLine() is { } line)
{
buffer.AppendLine(line);
try
{
onDataReceived?.Invoke(line);
}
catch (Exception)
{
}
}
tcs.TrySetResult(true);
}
}
}