diff options
Diffstat (limited to 'cli/ops/testing.rs')
-rw-r--r-- | cli/ops/testing.rs | 100 |
1 files changed, 99 insertions, 1 deletions
diff --git a/cli/ops/testing.rs b/cli/ops/testing.rs index eb2deb90c..16544dd98 100644 --- a/cli/ops/testing.rs +++ b/cli/ops/testing.rs @@ -1,17 +1,31 @@ +use std::cell::RefCell; +use std::io::Read; +use std::rc::Rc; + use crate::tools::test::TestEvent; +use crate::tools::test::TestOutput; use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::op; use deno_core::Extension; use deno_core::ModuleSpecifier; use deno_core::OpState; +use deno_runtime::ops::io::StdFileResource; use deno_runtime::permissions::create_child_permissions; use deno_runtime::permissions::ChildPermissionsArg; use deno_runtime::permissions::Permissions; use tokio::sync::mpsc::UnboundedSender; use uuid::Uuid; -pub fn init(sender: UnboundedSender<TestEvent>) -> Extension { +pub fn init( + sender: UnboundedSender<TestEvent>, + stdout_writer: os_pipe::PipeWriter, + stderr_writer: os_pipe::PipeWriter, +) -> Extension { + // todo(dsheret): don't do this? Taking out the writers was necessary to prevent invalid handle panics + let stdout_writer = Rc::new(RefCell::new(Some(stdout_writer))); + let stderr_writer = Rc::new(RefCell::new(Some(stderr_writer))); + Extension::builder() .ops(vec![ op_pledge_test_permissions::decl(), @@ -19,13 +33,82 @@ pub fn init(sender: UnboundedSender<TestEvent>) -> Extension { op_get_test_origin::decl(), op_dispatch_test_event::decl(), ]) + .middleware(|op| match op.name { + "op_print" => op_print::decl(), + _ => op, + }) .state(move |state| { + state.resource_table.replace( + 1, + StdFileResource::stdio( + &pipe_writer_to_file(&stdout_writer.borrow_mut().take().unwrap()), + "stdout", + ), + ); + state.resource_table.replace( + 2, + StdFileResource::stdio( + &pipe_writer_to_file(&stderr_writer.borrow_mut().take().unwrap()), + "stderr", + ), + ); state.put(sender.clone()); Ok(()) }) .build() } +#[cfg(windows)] +fn pipe_writer_to_file(writer: &os_pipe::PipeWriter) -> std::fs::File { + use std::os::windows::prelude::AsRawHandle; + use std::os::windows::prelude::FromRawHandle; + unsafe { std::fs::File::from_raw_handle(writer.as_raw_handle()) } +} + +#[cfg(unix)] +fn pipe_writer_to_file(writer: &os_pipe::PipeWriter) -> std::fs::File { + use std::os::unix::io::AsRawFd; + use std::os::unix::io::FromRawFd; + unsafe { std::fs::File::from_raw_fd(writer.as_raw_fd()) } +} + +/// Creates the stdout and stderr pipes and returns the writers for stdout and stderr. +pub fn create_stdout_stderr_pipes( + sender: UnboundedSender<TestEvent>, +) -> (os_pipe::PipeWriter, os_pipe::PipeWriter) { + let (stdout_reader, stdout_writer) = os_pipe::pipe().unwrap(); + let (stderr_reader, stderr_writer) = os_pipe::pipe().unwrap(); + + start_output_redirect_thread(stdout_reader, sender.clone(), |bytes| { + TestOutput::Stdout(bytes) + }); + start_output_redirect_thread(stderr_reader, sender, |bytes| { + TestOutput::Stderr(bytes) + }); + + (stdout_writer, stderr_writer) +} + +fn start_output_redirect_thread( + mut pipe_reader: os_pipe::PipeReader, + sender: UnboundedSender<TestEvent>, + map_test_output: impl Fn(Vec<u8>) -> TestOutput + Send + 'static, +) { + tokio::task::spawn_blocking(move || loop { + let mut buffer = [0; 512]; + let size = match pipe_reader.read(&mut buffer) { + Ok(0) | Err(_) => break, + Ok(size) => size, + }; + if sender + .send(TestEvent::Output(map_test_output(buffer[0..size].to_vec()))) + .is_err() + { + break; + } + }); +} + #[derive(Clone)] struct PermissionsHolder(Uuid, Permissions); @@ -77,6 +160,21 @@ fn op_dispatch_test_event( ) -> Result<(), AnyError> { let sender = state.borrow::<UnboundedSender<TestEvent>>().clone(); sender.send(event).ok(); + Ok(()) +} +#[op] +pub fn op_print( + state: &mut OpState, + msg: String, + is_err: bool, +) -> Result<(), AnyError> { + let sender = state.borrow::<UnboundedSender<TestEvent>>().clone(); + let msg = if is_err { + TestOutput::PrintStderr(msg) + } else { + TestOutput::PrintStdout(msg) + }; + sender.send(TestEvent::Output(msg)).ok(); Ok(()) } |