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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
[Flags]
public enum DenoConstraints : int
{
None = 0,
NoStdin = 1,
NoStdout = 2,
NoStderr = 4
}
public class DenoWinRunner
{
private const int STD_INPUT_HANDLE = -10;
private const int STD_OUTPUT_HANDLE = -11;
private const int STD_ERROR_HANDLE = -12;
private const int FILE_NOT_FOUND = 2;
private const int WAIT_TIMEOUT = 258;
[DllImport("kernel32.dll")]
private static extern void SetStdHandle(int nStdHandle, IntPtr handle);
/// <summary>
/// Runs Deno.exe under the specified constraints
/// </summary>
/// <param name="pathToDenoExe">Path to the Deno.exe file. Can be absolute or relative</param>
/// <param name="pathToTestScript">Path to the script file Deno should run.</param>
/// <param name="constraints">The constraints to apply to the Deno process</param>
/// <param name="timeoutMilliseconds">How long to wait for the Deno process to exit</param>
/// <returns>The deno.exe exit code, or an exit code provided by the test runner</returns>
public static int RunDenoScript(string pathToDenoExe, string pathToTestScript, DenoConstraints constraints, uint timeoutMilliseconds = 1000)
{
try
{
if (!File.Exists(pathToDenoExe))
{
Console.Error.WriteLine("Cannot find Deno.exe at " + pathToDenoExe);
return FILE_NOT_FOUND;
}
if (!File.Exists(pathToTestScript))
{
Console.Error.WriteLine("Cannot find test script at " + pathToTestScript);
return FILE_NOT_FOUND;
}
ProcessStartInfo startInfo = new ProcessStartInfo(pathToDenoExe)
{
ErrorDialog = false,
UseShellExecute = false,
Arguments = @"run -A " + pathToTestScript,
RedirectStandardInput = !constraints.HasFlag(DenoConstraints.NoStdin),
RedirectStandardOutput = !constraints.HasFlag(DenoConstraints.NoStdout),
RedirectStandardError = !constraints.HasFlag(DenoConstraints.NoStderr)
};
startInfo.Environment.Add("RUST_BACKTRACE", "1");
if (constraints.HasFlag(DenoConstraints.NoStdin))
{
SetStdHandle(STD_INPUT_HANDLE, (IntPtr)null);
}
if (constraints.HasFlag(DenoConstraints.NoStdout))
{
SetStdHandle(STD_OUTPUT_HANDLE, (IntPtr)null);
}
if (constraints.HasFlag(DenoConstraints.NoStderr))
{
SetStdHandle(STD_ERROR_HANDLE, (IntPtr)null);
}
Process process = new Process { StartInfo = startInfo };
process.Start();
Task<string> stdErrTask = startInfo.RedirectStandardError ?
process.StandardError.ReadToEndAsync() : Task.FromResult<string>(null);
Task<string> stdOutTask = startInfo.RedirectStandardOutput ?
process.StandardOutput.ReadToEndAsync() : Task.FromResult<string>(null);
if (!process.WaitForExit((int)timeoutMilliseconds))
{
Console.Error.WriteLine("Timed out waiting for Deno process to exit");
try
{
process.Kill();
}
catch
{
// Kill might fail, either because the process already exited or due to some other error
Console.Error.WriteLine("Failure killing the Deno process - possible Zombie Deno.exe process");
}
return WAIT_TIMEOUT;
}
// If the Deno process wrote to STDERR - append it to our STDERR
if (!constraints.HasFlag(DenoConstraints.NoStderr))
{
string error = stdErrTask.Result;
if (!string.IsNullOrWhiteSpace(error))
{
Console.Error.WriteLine(error);
}
}
return process.ExitCode;
}
catch (Win32Exception ex)
{
Console.Error.WriteLine("Win32Exception: code = " + ex.ErrorCode + ", message: " + ex.Message);
return ex.NativeErrorCode;
}
catch (Exception ex)
{
Console.Error.WriteLine("Exception: message: " + ex.Message);
return -1;
}
}
}
|