From 4afb73d9ffb3cb52f1144205205b1f6274e0832f Mon Sep 17 00:00:00 2001 From: John Sharratt's Shared Account Date: Fri, 10 Jun 2022 21:45:28 +0200 Subject: [PATCH] - Converted the WASI js test into a generic stdio test that works for both sys and js versions of wasmer (also fixed the tests) - Added stdin, stdout and stderr methods to WasiState for wasmer consumers --- lib/vfs/src/host_fs.rs | 4 +- lib/vfs/src/lib.rs | 6 +- lib/vfs/src/mem_fs/file_opener.rs | 2 +- lib/wasi/src/state/builder.rs | 14 +- lib/wasi/src/state/guard.rs | 292 +++++++++++++++++++++++++++++ lib/wasi/src/state/mod.rs | 114 ++++++----- lib/wasi/src/state/types.rs | 4 +- lib/wasi/tests/{js.rs => stdio.rs} | 92 ++++++--- 8 files changed, 426 insertions(+), 102 deletions(-) create mode 100644 lib/wasi/src/state/guard.rs rename lib/wasi/tests/{js.rs => stdio.rs} (73%) diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index 37f715ea288..788d21b3b77 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -181,7 +181,7 @@ impl crate::FileOpener for FileOpener { &mut self, path: &Path, conf: &OpenOptionsConfig, - ) -> Result> { + ) -> Result> { // TODO: handle create implying write, etc. let read = conf.read(); let write = conf.write(); @@ -197,7 +197,7 @@ impl crate::FileOpener for FileOpener { .map_err(Into::into) .map(|file| { Box::new(File::new(file, path.to_owned(), read, write, append)) - as Box + as Box }) } } diff --git a/lib/vfs/src/lib.rs b/lib/vfs/src/lib.rs index 0b8456bfda3..927440cd212 100644 --- a/lib/vfs/src/lib.rs +++ b/lib/vfs/src/lib.rs @@ -55,7 +55,7 @@ pub trait FileOpener { &mut self, path: &Path, conf: &OpenOptionsConfig, - ) -> Result>; + ) -> Result>; } #[derive(Debug, Clone)] @@ -150,14 +150,14 @@ impl OpenOptions { self } - pub fn open>(&mut self, path: P) -> Result> { + pub fn open>(&mut self, path: P) -> Result> { self.opener.open(path.as_ref(), &self.conf) } } /// This trait relies on your file closing when it goes out of scope via `Drop` #[cfg_attr(feature = "enable-serde", typetag::serde)] -pub trait VirtualFile: fmt::Debug + Send + Write + Read + Seek + 'static + Upcastable { +pub trait VirtualFile: fmt::Debug + Write + Read + Seek + Upcastable { /// the last time the file was accessed in nanoseconds as a UNIX timestamp fn last_accessed(&self) -> u64; diff --git a/lib/vfs/src/mem_fs/file_opener.rs b/lib/vfs/src/mem_fs/file_opener.rs index 679341badc2..80fc9255b3d 100644 --- a/lib/vfs/src/mem_fs/file_opener.rs +++ b/lib/vfs/src/mem_fs/file_opener.rs @@ -14,7 +14,7 @@ impl crate::FileOpener for FileOpener { &mut self, path: &Path, conf: &OpenOptionsConfig, - ) -> Result> { + ) -> Result> { let read = conf.read(); let mut write = conf.write(); let append = conf.append(); diff --git a/lib/wasi/src/state/builder.rs b/lib/wasi/src/state/builder.rs index 8a40d653c7d..a576baee119 100644 --- a/lib/wasi/src/state/builder.rs +++ b/lib/wasi/src/state/builder.rs @@ -46,9 +46,9 @@ pub struct WasiStateBuilder { vfs_preopens: Vec, #[allow(clippy::type_complexity)] setup_fs_fn: Option Result<(), String> + Send>>, - stdout_override: Option>, - stderr_override: Option>, - stdin_override: Option>, + stdout_override: Option>, + stderr_override: Option>, + stdin_override: Option>, fs_override: Option>, runtime_override: Option>, } @@ -284,7 +284,7 @@ impl WasiStateBuilder { /// Overwrite the default WASI `stdout`, if you want to hold on to the /// original `stdout` use [`WasiFs::swap_file`] after building. - pub fn stdout(&mut self, new_file: Box) -> &mut Self { + pub fn stdout(&mut self, new_file: Box) -> &mut Self { self.stdout_override = Some(new_file); self @@ -292,7 +292,7 @@ impl WasiStateBuilder { /// Overwrite the default WASI `stderr`, if you want to hold on to the /// original `stderr` use [`WasiFs::swap_file`] after building. - pub fn stderr(&mut self, new_file: Box) -> &mut Self { + pub fn stderr(&mut self, new_file: Box) -> &mut Self { self.stderr_override = Some(new_file); self @@ -300,7 +300,7 @@ impl WasiStateBuilder { /// Overwrite the default WASI `stdin`, if you want to hold on to the /// original `stdin` use [`WasiFs::swap_file`] after building. - pub fn stdin(&mut self, new_file: Box) -> &mut Self { + pub fn stdin(&mut self, new_file: Box) -> &mut Self { self.stdin_override = Some(new_file); self @@ -468,7 +468,7 @@ impl WasiStateBuilder { Ok(WasiState { fs: wasi_fs, - inodes, + inodes: Arc::new(inodes), args: self.args.clone(), envs: self .envs diff --git a/lib/wasi/src/state/guard.rs b/lib/wasi/src/state/guard.rs new file mode 100644 index 00000000000..17ec9164a5a --- /dev/null +++ b/lib/wasi/src/state/guard.rs @@ -0,0 +1,292 @@ +use std::{sync::{ + RwLockReadGuard, RwLockWriteGuard +}, io::Seek}; +use super::*; + +#[derive(Debug)] +pub(crate) struct InodeValFileReadGuard<'a> { + pub(crate) guard: RwLockReadGuard<'a, Kind>, +} + +impl<'a> Deref for InodeValFileReadGuard<'a> { + type Target = Option>; + fn deref(&self) -> &Self::Target { + if let Kind::File { handle, .. } = self.guard.deref() { + return handle; + } + unreachable!() + } +} + +#[derive(Debug)] +pub struct InodeValFileWriteGuard<'a> { + pub(crate) guard: RwLockWriteGuard<'a, Kind>, +} + +impl<'a> Deref for InodeValFileWriteGuard<'a> { + type Target = Option>; + fn deref(&self) -> &Self::Target { + if let Kind::File { handle, .. } = self.guard.deref() { + return handle; + } + unreachable!() + } +} + +impl<'a> DerefMut for InodeValFileWriteGuard<'a> { + fn deref_mut(&mut self) -> &mut Self::Target { + if let Kind::File { handle, .. } = self.guard.deref_mut() { + return handle; + } + unreachable!() + } +} + +#[derive(Debug)] +pub(crate) struct WasiStateFileGuard { + inodes: Arc>, + inode: generational_arena::Index, +} + +impl WasiStateFileGuard +{ + pub fn new(state: &WasiState, fd: __wasi_fd_t) -> Result { + let inodes = state.inodes.read().unwrap(); + let fd_map = state.fs.fd_map.read().unwrap(); + if let Some(fd) = fd_map.get(&fd) { + let guard = inodes.arena[fd.inode].read(); + if let Kind::File { .. } = guard.deref() { + Ok( + Self { + inodes: state.inodes.clone(), + inode: fd.inode + } + ) + } else { + // Our public API should ensure that this is not possible + Err(FsError::NotAFile) + } + + } else { + Err(FsError::NoDevice) + } + } + + pub fn lock_read<'a>(&self, inodes: &'a RwLockReadGuard) -> InodeValFileReadGuard<'a> + { + let guard = inodes.arena[self.inode].read(); + if let Kind::File { .. } = guard.deref() { + InodeValFileReadGuard { guard } + } else { + // Our public API should ensure that this is not possible + unreachable!("Non-file found in standard device location") + } + } + + pub fn lock_write<'a>(&self, inodes: &'a RwLockReadGuard) -> InodeValFileWriteGuard<'a> + { + let guard = inodes.arena[self.inode].write(); + if let Kind::File { .. } = guard.deref() { + InodeValFileWriteGuard { guard } + } else { + // Our public API should ensure that this is not possible + unreachable!("Non-file found in standard device location") + } + } +} + +impl VirtualFile +for WasiStateFileGuard +{ + fn last_accessed(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.last_accessed() + } else { + 0 + } + } + + fn last_modified(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.last_modified() + } else { + 0 + } + } + + fn created_time(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.created_time() + } else { + 0 + } + } + + fn size(&self) -> u64 { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.size() + } else { + 0 + } + } + + fn set_len(&mut self, new_size: u64) -> Result<(), FsError> { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.set_len(new_size) + } else { + Err(FsError::IOError) + } + } + + fn unlink(&mut self) -> Result<(), FsError> { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.unlink() + } else { + Err(FsError::IOError) + } + } + + fn sync_to_disk(&self) -> Result<(), FsError> { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.sync_to_disk() + } else { + Err(FsError::IOError) + } + } + + fn bytes_available(&self) -> Result { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.bytes_available() + } else { + Err(FsError::IOError) + } + } + + fn bytes_available_read(&self) -> Result, FsError> { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.bytes_available_read() + } else { + Err(FsError::IOError) + } + } + + fn bytes_available_write(&self) -> Result, FsError> { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.bytes_available_write() + } else { + Err(FsError::IOError) + } + } + + fn is_open(&self) -> bool { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.is_open() + } else { + false + } + } + + fn get_fd(&self) -> Option { + let inodes = self.inodes.read().unwrap(); + let guard = self.lock_read(&inodes); + if let Some(file) = guard.deref() { + file.get_fd() + } else { + None + } + } +} + +impl Write +for WasiStateFileGuard +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.write(buf) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } + + fn write_vectored(&mut self, bufs: &[std::io::IoSlice<'_>]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.write_vectored(bufs) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } + + fn flush(&mut self) -> std::io::Result<()> { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.flush() + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } +} + +impl Read +for WasiStateFileGuard +{ + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.read(buf) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } + + fn read_vectored(&mut self, bufs: &mut [std::io::IoSliceMut<'_>]) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.read_vectored(bufs) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } +} + +impl Seek +for WasiStateFileGuard +{ + fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { + let inodes = self.inodes.read().unwrap(); + let mut guard = self.lock_write(&inodes); + if let Some(file) = guard.deref_mut() { + file.seek(pos) + } else { + Err(std::io::ErrorKind::Unsupported.into()) + } + } +} \ No newline at end of file diff --git a/lib/wasi/src/state/mod.rs b/lib/wasi/src/state/mod.rs index 4897830db15..1b89c88b1d5 100644 --- a/lib/wasi/src/state/mod.rs +++ b/lib/wasi/src/state/mod.rs @@ -19,11 +19,13 @@ mod builder; mod pipe; mod socket; mod types; +mod guard; pub use self::builder::*; pub use self::pipe::*; pub use self::socket::*; pub use self::types::*; +pub use self::guard::*; use crate::syscalls::types::*; use crate::utils::map_io_err; use generational_arena::Arena; @@ -32,6 +34,7 @@ pub use generational_arena::Index as Inode; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::collections::VecDeque; +use std::io::Read; use std::sync::mpsc; use std::sync::Arc; use std::{ @@ -90,45 +93,6 @@ impl InodeVal { } } -#[derive(Debug)] -pub struct InodeValFileReadGuard<'a> { - pub(crate) guard: RwLockReadGuard<'a, Kind>, -} - -impl<'a> Deref for InodeValFileReadGuard<'a> { - type Target = Option>; - fn deref(&self) -> &Self::Target { - if let Kind::File { handle, .. } = self.guard.deref() { - return handle; - } - unreachable!() - } -} - -#[derive(Debug)] -pub struct InodeValFileWriteGuard<'a> { - pub(crate) guard: RwLockWriteGuard<'a, Kind>, -} - -impl<'a> Deref for InodeValFileWriteGuard<'a> { - type Target = Option>; - fn deref(&self) -> &Self::Target { - if let Kind::File { handle, .. } = self.guard.deref() { - return handle; - } - unreachable!() - } -} - -impl<'a> DerefMut for InodeValFileWriteGuard<'a> { - fn deref_mut(&mut self) -> &mut Self::Target { - if let Kind::File { handle, .. } = self.guard.deref_mut() { - return handle; - } - unreachable!() - } -} - /// The core of the filesystem abstraction. Includes directories, /// files, and symlinks. #[derive(Debug)] @@ -136,7 +100,7 @@ impl<'a> DerefMut for InodeValFileWriteGuard<'a> { pub enum Kind { File { /// The open file, if it's open - handle: Option>, + handle: Option>, /// The path on the host system where the file is located /// This is deprecated and will be removed soon path: PathBuf, @@ -264,14 +228,14 @@ impl WasiInodes { } /// Get the `VirtualFile` object at stdout - pub fn stdout( + pub(crate) fn stdout( &self, fd_map: &RwLock>, ) -> Result { self.std_dev_get(fd_map, __WASI_STDOUT_FILENO) } /// Get the `VirtualFile` object at stdout mutably - pub fn stdout_mut( + pub(crate) fn stdout_mut( &self, fd_map: &RwLock>, ) -> Result { @@ -279,14 +243,14 @@ impl WasiInodes { } /// Get the `VirtualFile` object at stderr - pub fn stderr( + pub(crate) fn stderr( &self, fd_map: &RwLock>, ) -> Result { self.std_dev_get(fd_map, __WASI_STDERR_FILENO) } /// Get the `VirtualFile` object at stderr mutably - pub fn stderr_mut( + pub(crate) fn stderr_mut( &self, fd_map: &RwLock>, ) -> Result { @@ -294,14 +258,14 @@ impl WasiInodes { } /// Get the `VirtualFile` object at stdin - pub fn stdin( + pub(crate) fn stdin( &self, fd_map: &RwLock>, ) -> Result { self.std_dev_get(fd_map, __WASI_STDIN_FILENO) } /// Get the `VirtualFile` object at stdin mutably - pub fn stdin_mut( + pub(crate) fn stdin_mut( &self, fd_map: &RwLock>, ) -> Result { @@ -310,18 +274,18 @@ impl WasiInodes { /// Internal helper function to get a standard device handle. /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. - fn std_dev_get( - &self, + fn std_dev_get<'a>( + &'a self, fd_map: &RwLock>, fd: __wasi_fd_t, - ) -> Result { + ) -> Result, FsError> { if let Some(fd) = fd_map.read().unwrap().get(&fd) { let guard = self.arena[fd.inode].read(); if let Kind::File { .. } = guard.deref() { Ok(InodeValFileReadGuard { guard }) } else { // Our public API should ensure that this is not possible - unreachable!("Non-file found in standard device location") + Err(FsError::NotAFile) } } else { // this should only trigger if we made a mistake in this crate @@ -330,18 +294,18 @@ impl WasiInodes { } /// Internal helper function to mutably get a standard device handle. /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. - fn std_dev_get_mut( - &self, + fn std_dev_get_mut<'a>( + &'a self, fd_map: &RwLock>, fd: __wasi_fd_t, - ) -> Result { + ) -> Result, FsError> { if let Some(fd) = fd_map.read().unwrap().get(&fd) { let guard = self.arena[fd.inode].write(); if let Kind::File { .. } = guard.deref() { Ok(InodeValFileWriteGuard { guard }) } else { // Our public API should ensure that this is not possible - unreachable!("Non-file found in standard device location") + Err(FsError::NotAFile) } } else { // this should only trigger if we made a mistake in this crate @@ -742,7 +706,7 @@ impl WasiFs { &mut self, inodes: &mut WasiInodes, base: __wasi_fd_t, - file: Box, + file: Box, open_flags: u16, name: String, rights: __wasi_rights_t, @@ -800,8 +764,8 @@ impl WasiFs { &self, inodes: &WasiInodes, fd: __wasi_fd_t, - file: Box, - ) -> Result>, FsError> { + file: Box, + ) -> Result>, FsError> { let mut ret = Some(file); match fd { __WASI_STDIN_FILENO => { @@ -1602,7 +1566,7 @@ impl WasiFs { fn create_std_dev_inner( &self, inodes: &mut WasiInodes, - handle: Box, + handle: Box, name: &'static str, raw_fd: __wasi_fd_t, rights: __wasi_rights_t, @@ -1862,7 +1826,7 @@ impl WasiState { #[cfg_attr(feature = "enable-serde", derive(Serialize, Deserialize))] pub struct WasiState { pub fs: WasiFs, - pub inodes: RwLock, + pub inodes: Arc>, pub args: Vec>, pub envs: Vec>, } @@ -1886,6 +1850,38 @@ impl WasiState { pub fn unfreeze(bytes: &[u8]) -> Option { bincode::deserialize(bytes).ok() } + + /// Get the `VirtualFile` object at stdout + pub fn stdout( + &self, + ) -> Result, FsError> { + self.std_dev_get(__WASI_STDOUT_FILENO) + } + + /// Get the `VirtualFile` object at stderr + pub fn stderr( + &self, + ) -> Result, FsError> { + self.std_dev_get(__WASI_STDERR_FILENO) + } + + /// Get the `VirtualFile` object at stdin + pub fn stdin( + &self, + ) -> Result, FsError> { + self.std_dev_get(__WASI_STDIN_FILENO) + } + + /// Internal helper function to get a standard device handle. + /// Expects one of `__WASI_STDIN_FILENO`, `__WASI_STDOUT_FILENO`, `__WASI_STDERR_FILENO`. + fn std_dev_get( + &self, + fd: __wasi_fd_t, + ) -> Result, FsError> { + Ok( + Box::new(WasiStateFileGuard::new(self, fd)?) + ) + } } pub fn virtual_file_type_to_wasi_file_type(file_type: wasmer_vfs::FileType) -> __wasi_filetype_t { diff --git a/lib/wasi/src/state/types.rs b/lib/wasi/src/state/types.rs index 85e988e7c83..f850f3eb177 100644 --- a/lib/wasi/src/state/types.rs +++ b/lib/wasi/src/state/types.rs @@ -218,7 +218,7 @@ impl PollEventBuilder { #[cfg(all(unix, feature = "sys-poll"))] pub(crate) fn poll( - selfs: &[&(dyn VirtualFile + Sync)], + selfs: &[&(dyn VirtualFile + Send + Sync + 'static)], events: &[PollEventSet], seen_events: &mut [PollEventSet], timeout: Duration, @@ -258,7 +258,7 @@ pub(crate) fn poll( #[cfg(any(not(unix), not(feature = "sys-poll")))] pub(crate) fn poll( - files: &[&(dyn VirtualFile + Sync)], + files: &[&(dyn VirtualFile + Send + Sync + 'static)], events: &[PollEventSet], seen_events: &mut [PollEventSet], timeout: Duration, diff --git a/lib/wasi/tests/js.rs b/lib/wasi/tests/stdio.rs similarity index 73% rename from lib/wasi/tests/js.rs rename to lib/wasi/tests/stdio.rs index fd1c80f831e..fcbbf99e733 100644 --- a/lib/wasi/tests/js.rs +++ b/lib/wasi/tests/stdio.rs @@ -1,10 +1,45 @@ -#![cfg(feature = "js")] +use std::io::{Read, Write}; +#[cfg(feature = "js")] use wasm_bindgen_test::*; use wasmer::{Instance, Module, Store}; -use wasmer_wasi::{Stdin, Stdout, WasiState}; +use wasmer_wasi::{Pipe, WasiState}; + +mod sys { + #[test] + fn test_stdout() { + super::test_stdout() + } + + #[test] + fn test_stdin() { + super::test_stdin() + } + + #[test] + fn test_env() { + super::test_env() + } +} + +#[cfg(feature = "js")] +mod js { + #[wasm_bindgen_test] + fn test_stdout() { + super::test_stdout() + } + + #[wasm_bindgen_test] + fn test_stdin() { + super::test_stdin() + } + + #[wasm_bindgen_test] + fn test_env() { + super::test_env() + } +} -#[wasm_bindgen_test] fn test_stdout() { let store = Store::default(); let module = Module::new(&store, br#" @@ -38,10 +73,10 @@ fn test_stdout() { "#).unwrap(); // Create the `WasiEnv`. - // let stdout = Stdout::default(); + let mut stdout = Pipe::default(); let wasi_env = WasiState::new("command-name") .args(&["Gordon"]) - // .stdout(Box::new(stdout)) + .stdout(Box::new(stdout.clone())) .finalize() .unwrap(); @@ -56,17 +91,17 @@ fn test_stdout() { let start = instance.exports.get_function("_start").unwrap(); start.call(&[]).unwrap(); - let state = wasi_env.state(); - let stdout = state.fs.stdout().unwrap().as_ref().unwrap(); - let stdout = stdout.downcast_ref::().unwrap(); - let stdout_as_str = std::str::from_utf8(&stdout.buf).unwrap(); + let mut stdout_str = String::new(); + stdout.read_to_string(&mut stdout_str).unwrap(); + let stdout_as_str = stdout_str.as_str(); assert_eq!(stdout_as_str, "hello world\n"); } -#[wasm_bindgen_test] + fn test_env() { let store = Store::default(); let module = Module::new(&store, include_bytes!("envvar.wasm")).unwrap(); + #[cfg(feature = "js")] tracing_wasm::set_as_global_default_with_config({ let mut builder = tracing_wasm::WASMLayerConfigBuilder::new(); builder.set_console_config(tracing_wasm::ConsoleConfig::ReportWithoutConsoleColor); @@ -74,7 +109,7 @@ fn test_env() { }); // Create the `WasiEnv`. - // let stdout = Stdout::default(); + let mut stdout = Pipe::new(); let mut wasi_state_builder = WasiState::new("command-name"); wasi_state_builder .args(&["Gordon"]) @@ -83,7 +118,7 @@ fn test_env() { .env("TEST2", "VALUE2"); // panic!("envs: {:?}", wasi_state_builder.envs); let wasi_env = wasi_state_builder - // .stdout(Box::new(stdout)) + .stdout(Box::new(stdout.clone())) .finalize() .unwrap(); @@ -98,28 +133,30 @@ fn test_env() { let start = instance.exports.get_function("_start").unwrap(); start.call(&[]).unwrap(); - let state = wasi_env.state(); - let stdout = state.fs.stdout().unwrap().as_ref().unwrap(); - let stdout = stdout.downcast_ref::().unwrap(); - let stdout_as_str = std::str::from_utf8(&stdout.buf).unwrap(); + let mut stdout_str = String::new(); + stdout.read_to_string(&mut stdout_str).unwrap(); + let stdout_as_str = stdout_str.as_str(); assert_eq!(stdout_as_str, "Env vars:\nDOG=X\nTEST2=VALUE2\nTEST=VALUE\nDOG Ok(\"X\")\nDOG_TYPE Err(NotPresent)\nSET VAR Ok(\"HELLO\")\n"); } -#[wasm_bindgen_test] fn test_stdin() { let store = Store::default(); let module = Module::new(&store, include_bytes!("stdin-hello.wasm")).unwrap(); // Create the `WasiEnv`. - let mut stdin = Stdin::default(); - stdin.buf = "Hello, stdin!".as_bytes().to_owned(); - let mut wasi_env = WasiState::new("command-name") - .stdin(Box::new(stdin)) + let mut stdin = Pipe::new(); + let wasi_env = WasiState::new("command-name") + .stdin(Box::new(stdin.clone())) .finalize() .unwrap(); + // Write to STDIN + let buf = "Hello, stdin!\n".as_bytes().to_owned(); + stdin.write(&buf[..]).unwrap(); + // Generate an `ImportObject`. - let import_object = wasi_env.import_object(&module).unwrap(); + let mut wasi_thread = wasi_env.new_thread(); + let import_object = wasi_thread.import_object(&module).unwrap(); // Let's instantiate the module with the imports. let instance = Instance::new(&module, &import_object).unwrap(); @@ -127,11 +164,10 @@ fn test_stdin() { // Let's call the `_start` function, which is our `main` function in Rust. let start = instance.exports.get_function("_start").unwrap(); let result = start.call(&[]); - assert!(result.is_err()); - // let status = result.unwrap_err().downcast::().unwrap(); - let state = wasi_env.state(); - let stdin = state.fs.stdin().unwrap().as_ref().unwrap(); - let stdin = stdin.downcast_ref::().unwrap(); + assert!(result.is_err() == false); + // We assure stdin is now empty - assert_eq!(stdin.buf.len(), 0); + let mut buf = Vec::new(); + stdin.read_to_end(&mut buf).unwrap(); + assert_eq!(buf.len(), 0); }