diff --git a/crates/test-programs/build.rs b/crates/test-programs/build.rs index c953c57440..ce38998700 100644 --- a/crates/test-programs/build.rs +++ b/crates/test-programs/build.rs @@ -146,7 +146,12 @@ mod wasi_tests { }; writeln!( out, - " runtime::instantiate(&data, &bin_name, {})", + " runtime::{}(&data, &bin_name, {})", + if inherit_stdio(testsuite, stemstr) { + "instantiate_inherit_stdio" + } else { + "instantiate" + }, workspace, )?; writeln!(out, " }}")?; @@ -190,6 +195,19 @@ mod wasi_tests { "big_random_buf" => true, "clock_time_get" => true, "sched_yield" => true, + "poll_oneoff_stdio" => true, + _ => false, + } + } else { + unreachable!() + } + } + + /// Mark tests which require inheriting parent process stdio + fn inherit_stdio(testsuite: &str, name: &str) -> bool { + if testsuite == "wasi-tests" { + match name { + "poll_oneoff_stdio" => true, _ => false, } } else { diff --git a/crates/test-programs/tests/wasm_tests/runtime.rs b/crates/test-programs/tests/wasm_tests/runtime.rs index 22b063f091..63fa1825a2 100644 --- a/crates/test-programs/tests/wasm_tests/runtime.rs +++ b/crates/test-programs/tests/wasm_tests/runtime.rs @@ -62,3 +62,42 @@ pub fn instantiate(data: &[u8], bin_name: &str, workspace: Option<&Path>) -> any } } } + +pub fn instantiate_inherit_stdio( + data: &[u8], + bin_name: &str, + workspace: Option<&Path>, +) -> anyhow::Result<()> { + let r = { + let store = Store::default(); + + // Create our wasi context. + // Additionally register any preopened directories if we have them. + let mut builder = WasiCtxBuilder::new(); + + builder = builder.arg(bin_name)?.arg(".")?.inherit_stdio(); + + if let Some(workspace) = workspace { + println!("preopen: {:?}", workspace); + let preopen_dir = unsafe { cap_std::fs::Dir::open_ambient_dir(workspace) }?; + builder = builder.preopened_dir(preopen_dir, ".")?; + } + + let snapshot1 = wasi_c2_wasmtime::Wasi::new(&store, builder.build()?); + + let mut linker = Linker::new(&store); + + snapshot1.add_to_linker(&mut linker)?; + + let module = Module::new(store.engine(), &data).context("failed to create wasm module")?; + let instance = linker.instantiate(&module)?; + let start = instance.get_func("_start").unwrap(); + let with_type = start.get0::<()>()?; + with_type().map_err(anyhow::Error::from) + }; + + match r { + Ok(()) => Ok(()), + Err(trap) => Err(trap.context(format!("error while testing Wasm module '{}'", bin_name,))), + } +} diff --git a/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs b/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs index 6288a55c5f..ec6668566a 100644 --- a/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs +++ b/crates/test-programs/wasi-tests/src/bin/poll_oneoff.rs @@ -1,6 +1,6 @@ use more_asserts::assert_gt; use std::{env, mem::MaybeUninit, process}; -use wasi_tests::{open_scratch_directory, STDERR_FD, STDIN_FD, STDOUT_FD}; +use wasi_tests::open_scratch_directory; const CLOCK_ID: wasi::Userdata = 0x0123_45678; @@ -59,113 +59,6 @@ unsafe fn test_timeout() { ); } -unsafe fn test_stdin_read() { - let clock = wasi::SubscriptionClock { - id: wasi::CLOCKID_MONOTONIC, - timeout: 5_000_000u64, // 5 milliseconds - precision: 0, - flags: 0, - }; - let fd_readwrite = wasi::SubscriptionFdReadwrite { - file_descriptor: STDIN_FD, - }; - let r#in = [ - wasi::Subscription { - userdata: CLOCK_ID, - u: wasi::SubscriptionU { - tag: wasi::EVENTTYPE_CLOCK, - u: wasi::SubscriptionUU { clock }, - }, - }, - // Make sure that timeout is returned only once even if there are multiple read events - wasi::Subscription { - userdata: 1, - u: wasi::SubscriptionU { - tag: wasi::EVENTTYPE_FD_READ, - u: wasi::SubscriptionUU { - fd_read: fd_readwrite, - }, - }, - }, - ]; - let out = poll_oneoff_impl(&r#in).unwrap(); - assert_eq!(out.len(), 1, "should return 1 event"); - let event = &out[0]; - assert_eq!( - event.error, - wasi::ERRNO_SUCCESS, - "the event.error should be set to ESUCCESS" - ); - assert_eq!( - event.r#type, - wasi::EVENTTYPE_CLOCK, - "the event.type should equal clock" - ); - assert_eq!( - event.userdata, CLOCK_ID, - "the event.userdata should contain clock_id specified by the user" - ); -} - -unsafe fn test_stdout_stderr_write() { - let stdout_readwrite = wasi::SubscriptionFdReadwrite { - file_descriptor: STDOUT_FD, - }; - let stderr_readwrite = wasi::SubscriptionFdReadwrite { - file_descriptor: STDERR_FD, - }; - let r#in = [ - wasi::Subscription { - userdata: 1, - u: wasi::SubscriptionU { - tag: wasi::EVENTTYPE_FD_WRITE, - u: wasi::SubscriptionUU { - fd_write: stdout_readwrite, - }, - }, - }, - wasi::Subscription { - userdata: 2, - u: wasi::SubscriptionU { - tag: wasi::EVENTTYPE_FD_WRITE, - u: wasi::SubscriptionUU { - fd_write: stderr_readwrite, - }, - }, - }, - ]; - let out = poll_oneoff_impl(&r#in).unwrap(); - assert_eq!(out.len(), 2, "should return 2 events"); - assert_eq!( - out[0].userdata, 1, - "the event.userdata should contain fd userdata specified by the user" - ); - assert_eq!( - out[0].error, - wasi::ERRNO_SUCCESS, - "the event.error should be set to ERRNO_SUCCESS", - ); - assert_eq!( - out[0].r#type, - wasi::EVENTTYPE_FD_WRITE, - "the event.type should equal FD_WRITE" - ); - assert_eq!( - out[1].userdata, 2, - "the event.userdata should contain fd userdata specified by the user" - ); - assert_eq!( - out[1].error, - wasi::ERRNO_SUCCESS, - "the event.error should be set to ERRNO_SUCCESS", - ); - assert_eq!( - out[1].r#type, - wasi::EVENTTYPE_FD_WRITE, - "the event.type should equal FD_WRITE" - ); -} - unsafe fn test_fd_readwrite(fd: wasi::Fd, error_code: wasi::Errno) { let fd_readwrite = wasi::SubscriptionFdReadwrite { file_descriptor: fd, @@ -277,10 +170,6 @@ unsafe fn test_fd_readwrite_invalid_fd() { unsafe fn test_poll_oneoff(dir_fd: wasi::Fd) { test_timeout(); test_empty_poll(); - // NB we assume that stdin/stdout/stderr are valid and open - // for the duration of the test case - test_stdin_read(); - test_stdout_stderr_write(); test_fd_readwrite_valid_fd(dir_fd); test_fd_readwrite_invalid_fd(); } diff --git a/crates/test-programs/wasi-tests/src/bin/poll_oneoff_stdio.rs b/crates/test-programs/wasi-tests/src/bin/poll_oneoff_stdio.rs new file mode 100644 index 0000000000..44caa47593 --- /dev/null +++ b/crates/test-programs/wasi-tests/src/bin/poll_oneoff_stdio.rs @@ -0,0 +1,132 @@ +use std::mem::MaybeUninit; +use wasi_tests::{STDERR_FD, STDIN_FD, STDOUT_FD}; + +const CLOCK_ID: wasi::Userdata = 0x0123_45678; + +unsafe fn poll_oneoff_impl(r#in: &[wasi::Subscription]) -> Result, wasi::Error> { + let mut out: Vec = Vec::new(); + out.resize_with(r#in.len(), || { + MaybeUninit::::zeroed().assume_init() + }); + let size = wasi::poll_oneoff(r#in.as_ptr(), out.as_mut_ptr(), r#in.len())?; + out.truncate(size); + Ok(out) +} + +unsafe fn test_stdin_read() { + let clock = wasi::SubscriptionClock { + id: wasi::CLOCKID_MONOTONIC, + timeout: 5_000_000u64, // 5 milliseconds + precision: 0, + flags: 0, + }; + let fd_readwrite = wasi::SubscriptionFdReadwrite { + file_descriptor: STDIN_FD, + }; + let r#in = [ + wasi::Subscription { + userdata: CLOCK_ID, + u: wasi::SubscriptionU { + tag: wasi::EVENTTYPE_CLOCK, + u: wasi::SubscriptionUU { clock }, + }, + }, + // Make sure that timeout is returned only once even if there are multiple read events + wasi::Subscription { + userdata: 1, + u: wasi::SubscriptionU { + tag: wasi::EVENTTYPE_FD_READ, + u: wasi::SubscriptionUU { + fd_read: fd_readwrite, + }, + }, + }, + ]; + let out = poll_oneoff_impl(&r#in).unwrap(); + assert_eq!(out.len(), 1, "should return 1 event"); + let event = &out[0]; + assert_eq!( + event.error, + wasi::ERRNO_SUCCESS, + "the event.error should be set to ESUCCESS" + ); + assert_eq!( + event.r#type, + wasi::EVENTTYPE_CLOCK, + "the event.type should equal clock" + ); + assert_eq!( + event.userdata, CLOCK_ID, + "the event.userdata should contain clock_id specified by the user" + ); +} + +unsafe fn test_stdout_stderr_write() { + let stdout_readwrite = wasi::SubscriptionFdReadwrite { + file_descriptor: STDOUT_FD, + }; + let stderr_readwrite = wasi::SubscriptionFdReadwrite { + file_descriptor: STDERR_FD, + }; + let r#in = [ + wasi::Subscription { + userdata: 1, + u: wasi::SubscriptionU { + tag: wasi::EVENTTYPE_FD_WRITE, + u: wasi::SubscriptionUU { + fd_write: stdout_readwrite, + }, + }, + }, + wasi::Subscription { + userdata: 2, + u: wasi::SubscriptionU { + tag: wasi::EVENTTYPE_FD_WRITE, + u: wasi::SubscriptionUU { + fd_write: stderr_readwrite, + }, + }, + }, + ]; + let out = poll_oneoff_impl(&r#in).unwrap(); + assert_eq!(out.len(), 2, "should return 2 events"); + assert_eq!( + out[0].userdata, 1, + "the event.userdata should contain fd userdata specified by the user" + ); + assert_eq!( + out[0].error, + wasi::ERRNO_SUCCESS, + "the event.error should be set to ERRNO_SUCCESS", + ); + assert_eq!( + out[0].r#type, + wasi::EVENTTYPE_FD_WRITE, + "the event.type should equal FD_WRITE" + ); + assert_eq!( + out[1].userdata, 2, + "the event.userdata should contain fd userdata specified by the user" + ); + assert_eq!( + out[1].error, + wasi::ERRNO_SUCCESS, + "the event.error should be set to ERRNO_SUCCESS", + ); + assert_eq!( + out[1].r#type, + wasi::EVENTTYPE_FD_WRITE, + "the event.type should equal FD_WRITE" + ); +} + +unsafe fn test_poll_oneoff() { + // NB we assume that stdin/stdout/stderr are valid and open + // for the duration of the test case + test_stdin_read(); + test_stdout_stderr_write(); +} +fn main() { + // Run the tests. + unsafe { test_poll_oneoff() } +}