deno/cli/tests/DenoWinRunner.cs
2020-07-09 21:06:51 +02:00

127 lines
4.5 KiB
C#

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 contrainsts 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;
}
}
}