summaryrefslogtreecommitdiff
path: root/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs')
-rw-r--r--src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs88
1 files changed, 88 insertions, 0 deletions
diff --git a/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
new file mode 100644
index 00000000..eb07aa13
--- /dev/null
+++ b/src/internal/WixBuildTools.TestSupport/ExternalExecutable.cs
@@ -0,0 +1,88 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixBuildTools.TestSupport
4{
5 using System.Collections.Concurrent;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Text;
10
11 public abstract class ExternalExecutable
12 {
13 private readonly string exePath;
14
15 protected ExternalExecutable(string exePath)
16 {
17 this.exePath = exePath;
18 }
19
20 protected ExternalExecutableResult Run(string args, bool mergeErrorIntoOutput = false, string workingDirectory = null)
21 {
22 var startInfo = new ProcessStartInfo(this.exePath, args)
23 {
24 CreateNoWindow = true,
25 RedirectStandardError = true,
26 RedirectStandardOutput = true,
27 UseShellExecute = false,
28 WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(this.exePath),
29 };
30
31 using (var process = Process.Start(startInfo))
32 {
33 // This implementation of merging the streams does not guarantee that lines are retrieved in the same order that they were written.
34 // If the process is simultaneously writing to both streams, this is impossible to do anyway.
35 var standardOutput = new ConcurrentQueue<string>();
36 var standardError = mergeErrorIntoOutput ? standardOutput : new ConcurrentQueue<string>();
37
38 process.ErrorDataReceived += (s, e) => { if (e.Data != null) { standardError.Enqueue(e.Data); } };
39 process.OutputDataReceived += (s, e) => { if (e.Data != null) { standardOutput.Enqueue(e.Data); } };
40
41 process.BeginErrorReadLine();
42 process.BeginOutputReadLine();
43
44 process.WaitForExit();
45
46 return new ExternalExecutableResult
47 {
48 ExitCode = process.ExitCode,
49 StandardError = mergeErrorIntoOutput ? null : standardError.ToArray(),
50 StandardOutput = standardOutput.ToArray(),
51 StartInfo = startInfo,
52 };
53 }
54 }
55
56 // This is internal because it assumes backslashes aren't used as escape characters and there aren't any double quotes.
57 internal static string CombineArguments(IEnumerable<string> arguments)
58 {
59 if (arguments == null)
60 {
61 return null;
62 }
63
64 var sb = new StringBuilder();
65
66 foreach (var arg in arguments)
67 {
68 if (sb.Length > 0)
69 {
70 sb.Append(' ');
71 }
72
73 if (arg.IndexOf(' ') > -1)
74 {
75 sb.Append("\"");
76 sb.Append(arg);
77 sb.Append("\"");
78 }
79 else
80 {
81 sb.Append(arg);
82 }
83 }
84
85 return sb.ToString();
86 }
87 }
88}