mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 15:24:46 -05:00
parent
202e7fa6ad
commit
edb7a0eead
7 changed files with 289 additions and 33 deletions
|
@ -37,36 +37,48 @@ lazy_static! {
|
||||||
/// resource table is dropped storing reference to that handle, the handle
|
/// resource table is dropped storing reference to that handle, the handle
|
||||||
/// itself won't be closed (so Deno.core.print) will still work.
|
/// itself won't be closed (so Deno.core.print) will still work.
|
||||||
// TODO(ry) It should be possible to close stdout.
|
// TODO(ry) It should be possible to close stdout.
|
||||||
static ref STDIN_HANDLE: std::fs::File = {
|
static ref STDIN_HANDLE: Option<std::fs::File> = {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let stdin = unsafe { std::fs::File::from_raw_fd(0) };
|
let stdin = unsafe { Some(std::fs::File::from_raw_fd(0)) };
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let stdin = unsafe {
|
let stdin = unsafe {
|
||||||
std::fs::File::from_raw_handle(winapi::um::processenv::GetStdHandle(
|
let handle = winapi::um::processenv::GetStdHandle(
|
||||||
winapi::um::winbase::STD_INPUT_HANDLE,
|
winapi::um::winbase::STD_INPUT_HANDLE,
|
||||||
))
|
);
|
||||||
|
if handle.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(std::fs::File::from_raw_handle(handle))
|
||||||
};
|
};
|
||||||
stdin
|
stdin
|
||||||
};
|
};
|
||||||
static ref STDOUT_HANDLE: std::fs::File = {
|
static ref STDOUT_HANDLE: Option<std::fs::File> = {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let stdout = unsafe { std::fs::File::from_raw_fd(1) };
|
let stdout = unsafe { Some(std::fs::File::from_raw_fd(1)) };
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let stdout = unsafe {
|
let stdout = unsafe {
|
||||||
std::fs::File::from_raw_handle(winapi::um::processenv::GetStdHandle(
|
let handle = winapi::um::processenv::GetStdHandle(
|
||||||
winapi::um::winbase::STD_OUTPUT_HANDLE,
|
winapi::um::winbase::STD_OUTPUT_HANDLE,
|
||||||
))
|
);
|
||||||
|
if handle.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(std::fs::File::from_raw_handle(handle))
|
||||||
};
|
};
|
||||||
stdout
|
stdout
|
||||||
};
|
};
|
||||||
static ref STDERR_HANDLE: std::fs::File = {
|
static ref STDERR_HANDLE: Option<std::fs::File> = {
|
||||||
#[cfg(not(windows))]
|
#[cfg(not(windows))]
|
||||||
let stderr = unsafe { std::fs::File::from_raw_fd(2) };
|
let stderr = unsafe { Some(std::fs::File::from_raw_fd(2)) };
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let stderr = unsafe {
|
let stderr = unsafe {
|
||||||
std::fs::File::from_raw_handle(winapi::um::processenv::GetStdHandle(
|
let handle = winapi::um::processenv::GetStdHandle(
|
||||||
winapi::um::winbase::STD_ERROR_HANDLE,
|
winapi::um::winbase::STD_ERROR_HANDLE,
|
||||||
))
|
);
|
||||||
|
if handle.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(std::fs::File::from_raw_handle(handle))
|
||||||
};
|
};
|
||||||
stderr
|
stderr
|
||||||
};
|
};
|
||||||
|
@ -78,26 +90,31 @@ pub fn init(i: &mut CoreIsolate, s: &State) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_stdio() -> (
|
pub fn get_stdio() -> (
|
||||||
StreamResourceHolder,
|
Option<StreamResourceHolder>,
|
||||||
StreamResourceHolder,
|
Option<StreamResourceHolder>,
|
||||||
StreamResourceHolder,
|
Option<StreamResourceHolder>,
|
||||||
) {
|
) {
|
||||||
let stdin = StreamResourceHolder::new(StreamResource::FsFile(Some({
|
let stdin = get_stdio_stream(&STDIN_HANDLE);
|
||||||
let stdin = STDIN_HANDLE.try_clone().unwrap();
|
let stdout = get_stdio_stream(&STDOUT_HANDLE);
|
||||||
(tokio::fs::File::from_std(stdin), FileMetadata::default())
|
let stderr = get_stdio_stream(&STDERR_HANDLE);
|
||||||
})));
|
|
||||||
let stdout = StreamResourceHolder::new(StreamResource::FsFile(Some({
|
|
||||||
let stdout = STDOUT_HANDLE.try_clone().unwrap();
|
|
||||||
(tokio::fs::File::from_std(stdout), FileMetadata::default())
|
|
||||||
})));
|
|
||||||
let stderr = StreamResourceHolder::new(StreamResource::FsFile(Some({
|
|
||||||
let stderr = STDERR_HANDLE.try_clone().unwrap();
|
|
||||||
(tokio::fs::File::from_std(stderr), FileMetadata::default())
|
|
||||||
})));
|
|
||||||
|
|
||||||
(stdin, stdout, stderr)
|
(stdin, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_stdio_stream(
|
||||||
|
handle: &Option<std::fs::File>,
|
||||||
|
) -> Option<StreamResourceHolder> {
|
||||||
|
match handle {
|
||||||
|
None => None,
|
||||||
|
Some(file_handle) => match file_handle.try_clone() {
|
||||||
|
Ok(clone) => Some(StreamResourceHolder::new(StreamResource::FsFile(
|
||||||
|
Some((tokio::fs::File::from_std(clone), FileMetadata::default())),
|
||||||
|
))),
|
||||||
|
Err(_e) => None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn no_buffer_specified() -> OpError {
|
fn no_buffer_specified() -> OpError {
|
||||||
OpError::type_error("no buffer specified".to_string())
|
OpError::type_error("no buffer specified".to_string())
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,9 +58,15 @@ fn create_web_worker(
|
||||||
let state = state_rc.borrow();
|
let state = state_rc.borrow();
|
||||||
let mut resource_table = state.resource_table.borrow_mut();
|
let mut resource_table = state.resource_table.borrow_mut();
|
||||||
let (stdin, stdout, stderr) = get_stdio();
|
let (stdin, stdout, stderr) = get_stdio();
|
||||||
resource_table.add("stdin", Box::new(stdin));
|
if let Some(stream) = stdin {
|
||||||
resource_table.add("stdout", Box::new(stdout));
|
resource_table.add("stdin", Box::new(stream));
|
||||||
resource_table.add("stderr", Box::new(stderr));
|
}
|
||||||
|
if let Some(stream) = stdout {
|
||||||
|
resource_table.add("stdout", Box::new(stream));
|
||||||
|
}
|
||||||
|
if let Some(stream) = stderr {
|
||||||
|
resource_table.add("stderr", Box::new(stream));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instead of using name for log we use `worker-${id}` because
|
// Instead of using name for log we use `worker-${id}` because
|
||||||
|
|
127
cli/tests/DenoWinRunner.cs
Normal file
127
cli/tests/DenoWinRunner.cs
Normal file
|
@ -0,0 +1,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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
10
cli/tests/DenoWinRunner.ps1
Normal file
10
cli/tests/DenoWinRunner.ps1
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
$Source = [IO.File]::ReadAllText("$PSScriptRoot\DenoWinRunner.cs")
|
||||||
|
$denoExePath = $args[0]
|
||||||
|
$scriptPath = $args[1]
|
||||||
|
$constraints = $args[2]
|
||||||
|
$timeout = 5000;
|
||||||
|
Add-Type -TypeDefinition $Source -Language CSharp
|
||||||
|
Write-Output("Running Deno script: " + $args[1])
|
||||||
|
$code = [DenoWinRunner]::RunDenoScript($denoExePath, $scriptPath, $constraints, $timeout)
|
||||||
|
Write-Output("Deno.exe or the test wrapper has exited with code: $code")
|
||||||
|
exit $code
|
|
@ -3116,3 +3116,62 @@ fn set_raw_should_not_panic_on_no_tty() {
|
||||||
let stderr = std::str::from_utf8(&output.stderr).unwrap().trim();
|
let stderr = std::str::from_utf8(&output.stderr).unwrap().trim();
|
||||||
assert!(stderr.contains("BadResource"));
|
assert!(stderr.contains("BadResource"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
enum WinProcConstraints {
|
||||||
|
NoStdIn,
|
||||||
|
NoStdOut,
|
||||||
|
NoStdErr,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
fn run_deno_script_constrained(
|
||||||
|
script_path: std::path::PathBuf,
|
||||||
|
constraints: WinProcConstraints,
|
||||||
|
) -> Result<(), i64> {
|
||||||
|
let file_path = "cli/tests/DenoWinRunner.ps1";
|
||||||
|
let constraints = match constraints {
|
||||||
|
WinProcConstraints::NoStdIn => "1",
|
||||||
|
WinProcConstraints::NoStdOut => "2",
|
||||||
|
WinProcConstraints::NoStdErr => "4",
|
||||||
|
};
|
||||||
|
let deno_exe_path = util::deno_exe_path()
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let deno_script_path = script_path.into_os_string().into_string().unwrap();
|
||||||
|
|
||||||
|
let args = vec![&deno_exe_path[..], &deno_script_path[..], constraints];
|
||||||
|
util::run_powershell_script_file(file_path, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn should_not_panic_on_no_stdin() {
|
||||||
|
let output = run_deno_script_constrained(
|
||||||
|
util::tests_path().join("echo.ts"),
|
||||||
|
WinProcConstraints::NoStdIn,
|
||||||
|
);
|
||||||
|
output.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn should_not_panic_on_no_stdout() {
|
||||||
|
let output = run_deno_script_constrained(
|
||||||
|
util::tests_path().join("echo.ts"),
|
||||||
|
WinProcConstraints::NoStdOut,
|
||||||
|
);
|
||||||
|
output.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
#[test]
|
||||||
|
fn should_not_panic_on_no_stderr() {
|
||||||
|
let output = run_deno_script_constrained(
|
||||||
|
util::tests_path().join("echo.ts"),
|
||||||
|
WinProcConstraints::NoStdErr,
|
||||||
|
);
|
||||||
|
output.unwrap();
|
||||||
|
}
|
||||||
|
|
|
@ -300,9 +300,15 @@ impl MainWorker {
|
||||||
let state_rc = CoreIsolate::state(&worker.isolate);
|
let state_rc = CoreIsolate::state(&worker.isolate);
|
||||||
let state = state_rc.borrow();
|
let state = state_rc.borrow();
|
||||||
let mut t = state.resource_table.borrow_mut();
|
let mut t = state.resource_table.borrow_mut();
|
||||||
t.add("stdin", Box::new(stdin));
|
if let Some(stream) = stdin {
|
||||||
t.add("stdout", Box::new(stdout));
|
t.add("stdin", Box::new(stream));
|
||||||
t.add("stderr", Box::new(stderr));
|
}
|
||||||
|
if let Some(stream) = stdout {
|
||||||
|
t.add("stdout", Box::new(stream));
|
||||||
|
}
|
||||||
|
if let Some(stream) = stderr {
|
||||||
|
t.add("stderr", Box::new(stream));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
worker.execute("bootstrap.mainRuntime()")?;
|
worker.execute("bootstrap.mainRuntime()")?;
|
||||||
Ok(worker)
|
Ok(worker)
|
||||||
|
|
|
@ -591,6 +591,37 @@ pub fn run_python_script(script: &str) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn run_powershell_script_file(
|
||||||
|
script_file_path: &str,
|
||||||
|
args: Vec<&str>,
|
||||||
|
) -> Result<(), i64> {
|
||||||
|
let deno_dir = new_deno_dir();
|
||||||
|
let mut command = Command::new("powershell.exe");
|
||||||
|
|
||||||
|
command
|
||||||
|
.env("DENO_DIR", deno_dir.path())
|
||||||
|
.current_dir(root_path())
|
||||||
|
.arg("-file")
|
||||||
|
.arg(script_file_path);
|
||||||
|
|
||||||
|
for arg in args {
|
||||||
|
command.arg(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
let output = command.output().expect("failed to spawn script");
|
||||||
|
let stdout = String::from_utf8(output.stdout).unwrap();
|
||||||
|
let stderr = String::from_utf8(output.stderr).unwrap();
|
||||||
|
println!("{}", stdout);
|
||||||
|
if !output.status.success() {
|
||||||
|
panic!(
|
||||||
|
"{} executed with failing error code\n{}{}",
|
||||||
|
script_file_path, stdout, stderr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct CheckOutputIntegrationTest {
|
pub struct CheckOutputIntegrationTest {
|
||||||
pub args: &'static str,
|
pub args: &'static str,
|
||||||
|
|
Loading…
Reference in a new issue