summaryrefslogtreecommitdiff
path: root/tests/testdata/assets/DenoWinRunner.cs
blob: 2f9e9f89fe355d1f904b36694eab82f7e334d8e0 (plain)
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;
        }
    }
}