diff --git a/src/fs/actors.rs b/src/fs/actors.rs deleted file mode 100644 index 6d826fe5..00000000 --- a/src/fs/actors.rs +++ /dev/null @@ -1,143 +0,0 @@ -//! A quick'n'dirty JS-friendly actor framework, inspired by Actix. -//! -//! ## Deadlocks -//! -//! Most [`FileSystem`] methods are synchronous, whereas all -//! [`FileSystemDirectoryHandle`] operations are asynchronous. To implement a -//! synchronous API on top of an inherently asynchronous mechanism, we use -//! [`InlineWaker`] to block in-place until a future is resolved. -//! -//! When a blocking method is invoked from the same thread that called -//! [`spawn()`], we open ourselves up to a chicken-and-egg scenario where the -//! synchronous operation can't return until the future resolves, but in order -//! for the future to resolve we have to yield to the JavaScript event loop so -//! the asynchronous operations get a chance to make progress. -//! -//! This causes a deadlock. -//! -//! In the spirit of [Pre-Pooping Your Pants][poop], we use -//! [`wasmer::current_thread_id()`] to detect these scenarios and crash instead. -//! -//! [poop]: https://cglab.ca/~abeinges/blah/everyone-poops/ - -use futures::{channel::mpsc, future::LocalBoxFuture, SinkExt, StreamExt}; -use tokio::sync::oneshot; -use tracing::Instrument; -use virtual_fs::FsError; -use wasmer_wasix::runtime::task_manager::InlineWaker; - -#[async_trait::async_trait(?Send)] -pub(crate) trait Handler { - type Output: Send + 'static; - - async fn handle(&mut self, msg: Msg) -> Result; -} - -type Thunk = Box LocalBoxFuture<'_, ()> + Send>; - -#[derive(Debug, Clone)] -pub(crate) struct Mailbox { - original_thread: u32, - sender: mpsc::Sender>, -} - -impl Mailbox { - /// Spawn an actor on the current thread. - pub(crate) fn spawn(mut actor: A) -> Self - where - A: 'static, - { - let (sender, mut receiver) = mpsc::channel::>(1); - let original_thread = wasmer::current_thread_id(); - - wasm_bindgen_futures::spawn_local( - async move { - while let Some(thunk) = receiver.next().await { - thunk(&mut actor).await; - } - } - .in_current_span(), - ); - - Mailbox { - original_thread, - sender, - } - } - - /// Asynchronously send a message to the actor. - pub(crate) async fn send(&self, msg: M) -> Result<>::Output, FsError> - where - A: Handler, - M: Send + 'static, - { - let (ret_sender, ret_receiver) = oneshot::channel(); - - let thunk: Thunk = Box::new(move |actor: &mut A| { - Box::pin(async move { - let result = actor.handle(msg).await; - - if ret_sender.send(result).is_err() { - tracing::warn!( - message_type = std::any::type_name::(), - "Unable to send the result back", - ); - } - }) - }); - - // Note: This isn't technically necessary, but it means our methods can - // take &self rather than forcing higher layers to add unnecessary - // synchronisation. - let mut sender = self.sender.clone(); - - if let Err(e) = sender.send(thunk).await { - tracing::warn!( - error = &e as &dyn std::error::Error, - message_type = std::any::type_name::(), - actor_type = std::any::type_name::(), - "Message sending failed", - ); - return Err(FsError::UnknownError); - } - - match ret_receiver.await { - Ok(result) => result, - Err(e) => { - tracing::warn!( - error = &e as &dyn std::error::Error, - message_type = std::any::type_name::(), - actor_type = std::any::type_name::(), - "Unable to receive the result", - ); - Err(FsError::UnknownError) - } - } - } - - /// Send a message to the actor and synchronously block until a response - /// is received. - /// - /// # Deadlocks - /// - /// To avoid deadlocks, this will error out with [`FsError::Lock`] if called - /// from the thread that the actor was spawned on. - pub(crate) fn handle(&self, msg: M) -> Result<>::Output, FsError> - where - A: Handler, - M: Send + 'static, - { - // Note: See the module doc-comments for more context on deadlocks - let current_thread = wasmer::current_thread_id(); - if self.original_thread == current_thread { - tracing::error!( - thread.id=current_thread, - caller=%std::panic::Location::caller(), - "Running a synchronous FileSystem operation on this thread will result in a deadlock" - ); - return Err(FsError::Lock); - } - - InlineWaker::block_on(self.send(msg)) - } -} diff --git a/src/fs/directory.rs b/src/fs/directory.rs index 33d9c31d..a6060a3e 100644 --- a/src/fs/directory.rs +++ b/src/fs/directory.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use tracing::Instrument; use virtual_fs::{AsyncReadExt, AsyncWriteExt, FileSystem}; use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue}; -use web_sys::FileSystemDirectoryHandle; use crate::{utils::Error, StringOrBytes}; @@ -20,27 +19,6 @@ impl Directory { Directory::default() } - /// Create a new [`Directory`] using the [File System API][mdn]. - /// - /// > **Important:** this API will only work inside a secure context. - /// - /// Some ways a [`FileSystemDirectoryHandle`] can be created are... - /// - /// - Using the [Origin private file system API][opfs] - /// - Calling [`window.showDirectoryPicker()`][sdp] - /// to access a file on the host machine (i.e. outside of the browser) - /// - From the [HTML Drag & Drop API][dnd] by calling [`DataTransferItem.getAsFileSystemHandle()`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem/getAsFileSystemHandle) - /// - /// - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API - /// [dnd]: https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API - /// [sdp]: https://developer.mozilla.org/en-US/docs/Web/API/window/showDirectoryPicker - /// [opfs]: https://developer.mozilla.org/en-US/docs/Web/API/File_System_API/Origin_private_file_system - #[wasm_bindgen(js_name = "fromBrowser")] - pub fn from_browser(handle: FileSystemDirectoryHandle) -> Self { - Directory(Arc::new(crate::fs::web::spawn(handle))) - } - #[wasm_bindgen(js_name = "readDir")] pub async fn read_dir(&self, mut path: String) -> Result { if !path.starts_with('/') { diff --git a/src/fs/mod.rs b/src/fs/mod.rs index a8e469d2..f1e552a3 100644 --- a/src/fs/mod.rs +++ b/src/fs/mod.rs @@ -1,5 +1,3 @@ mod directory; -mod web; -mod actors; pub use self::directory::Directory; diff --git a/src/fs/web.rs b/src/fs/web.rs deleted file mode 100644 index 042121f5..00000000 --- a/src/fs/web.rs +++ /dev/null @@ -1,543 +0,0 @@ -//! A [`FileSystem`] implementation backed by the browser's -//! [`FileSystemDirectoryHandle`]. -//! -//! Similar to the [`crate::tasks`] module, this provides multi-threaded access -//! to single-threaded JavaScript objects by encapsulating them in actors that -//! run in the original JavaScript context, then invoking methods by sending -//! messages back and forth. -//! -//! ## Deadlocks -//! -//! Most [`FileSystem`] methods are synchronous, whereas all -//! [`FileSystemDirectoryHandle`] operations are asynchronous. To implement a -//! synchronous API on top of an inherently asynchronous mechanism, we use -//! [`InlineWaker`] to block in-place until a future is resolved. -//! -//! When a blocking method is invoked from the same thread that called -//! [`spawn()`], we open ourselves up to a chicken-and-egg scenario where the -//! synchronous operation can't return until the future resolves, but in order -//! for the future to resolve we have to yield to the JavaScript event loop so -//! the asynchronous operations get a chance to make progress. -//! -//! This causes a deadlock. -//! -//! In the spirit of [Pre-Pooping Your Pants][poop], we use -//! [`wasmer::current_thread_id()`] to detect these scenarios and crash instead. -//! -//! [poop]: https://cglab.ca/~abeinges/blah/everyone-poops/ - -use std::path::{Path, PathBuf}; - -use async_trait::async_trait; -use futures::{SinkExt, TryStream, TryStreamExt}; -use js_sys::{JsString, Reflect}; -use virtual_fs::{ - DirEntry, FileOpener, FileSystem, FileType, FsError, OpenOptionsConfig, ReadDir, VirtualFile, -}; -use wasm_bindgen::{JsCast, JsValue}; -use web_sys::{DomException, FileSystemDirectoryHandle, FileSystemGetDirectoryOptions}; - -use crate::fs::actors::{Handler, Mailbox}; - -#[tracing::instrument(level = "debug", skip_all)] -pub(crate) fn spawn(handle: FileSystemDirectoryHandle) -> impl FileSystem + 'static { - Mailbox::spawn(Directory(handle)) -} - -#[derive(Debug, Clone)] -struct Directory(FileSystemDirectoryHandle); - -impl Directory { - /// Execute a thunk and use [`JsCast`] to downcast its result. - /// - /// # Implementation Details - /// - /// Note that this will do string matching against [`DomException::name()`] - /// to try and interpret an exception as a [`FsError`]. Technically, there - /// is [`DomException::code()`] that we could `match` against, but the docs - /// mark it as a legacy feature and recomend against using it. - /// - /// See [the MDN page][mdn] for more. - /// - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/DOMException - async fn exec(&self, error_codes: &[(&str, FsError)], thunk: F) -> Result - where - F: FnOnce(&FileSystemDirectoryHandle) -> js_sys::Promise, - Ret: JsCast, - { - let promise = thunk(&self.0); - - match wasm_bindgen_futures::JsFuture::from(promise).await { - Ok(value) => match value.dyn_into() { - Ok(v) => Ok(v), - Err(other) => unreachable!( - "Unable to cast {other:?} to a {}", - std::any::type_name::() - ), - }, - Err(e) => { - if let Some(e) = e - .dyn_ref::() - .and_then(|ex| interpret_dom_exception(ex, error_codes)) - { - Err(e) - } else { - let error = crate::utils::js_error(e); - tracing::error!( - error = &*error, - operation = %std::any::type_name::().replace("::{{closure}}", ""), - "An error occurred while interacting with the FileSystemDirectoryHandle", - ); - Err(FsError::UnknownError) - } - } - } - } - - /// Evaluate a thunk and throw away the result. - async fn eval(&self, error_codes: &[(&str, FsError)], thunk: F) -> Result<(), FsError> - where - F: FnOnce(&FileSystemDirectoryHandle) -> js_sys::Promise, - { - let _: JsValue = self.exec(error_codes, thunk).await?; - Ok(()) - } -} - -/// Convert a [`DomException`] into a known [`FsError`]. -/// -fn interpret_dom_exception(e: &DomException, error_codes: &[(&str, FsError)]) -> Option { - let name = e.name(); - - for (error_name, fs_error) in error_codes.iter().copied() { - if name == error_name { - return Some(fs_error); - } - } - - None -} - -#[derive(Debug, Clone)] -struct ReadDirectory(PathBuf); - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = ReadDir; - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, msg: ReadDirectory) -> Result { - let path = msg.0.as_os_str().to_str().ok_or(FsError::InvalidInput)?; - - let error_codes = [ - ("NotAllowedError", FsError::PermissionDenied), - ("TypeMismatchError", FsError::BaseNotDirectory), - ("NotFoundError", FsError::EntryNotFound), - ]; - - let dir: FileSystemDirectoryHandle = self - .exec(&error_codes, |d| { - d.get_directory_handle_with_options( - path, - FileSystemGetDirectoryOptions::new().create(true), - ) - }) - .await?; - - let iter: js_sys::AsyncIterator = Reflect::get(&dir, &JsValue::from_str("entries")) - .unwrap() - .unchecked_into::() - .call0(&dir) - .unwrap() - .unchecked_into(); - - let dir_entries = async_iterator_to_stream(iter) - .and_then(|pair| async { - let array: js_sys::Array = pair.dyn_into().unwrap(); - - let key: JsString = array.get(0).dyn_into().unwrap(); - let name = String::from(key); - let full_path = msg.0.join(name); - - let handle: web_sys::FileSystemHandle = array.get(1).dyn_into().unwrap(); - - let metadata = metadata_from_fs_handle(&handle).await.map_err(|e| { - tracing::debug!( - path=%full_path.display(), - error=&*e, - "Unable to get an item's metadata" - ); - FsError::UnknownError - }); - - Ok(DirEntry { - path: full_path, - metadata, - }) - }) - .try_collect::>() - .await; - - match dir_entries { - Ok(entries) => Ok(ReadDir::new(entries)), - Err(e) => { - let error = crate::utils::js_error(e); - tracing::debug!( - path, - error = &*error, - "Unable to read a directory's metadata", - ); - Err(FsError::UnknownError) - } - } - } -} - -async fn metadata_from_fs_handle( - handle: &web_sys::FileSystemHandle, -) -> Result { - if let Some(_dir_handle) = handle.dyn_ref::() { - Ok(virtual_fs::Metadata { - ft: FileType::new_dir(), - accessed: 0, - created: 0, - modified: 0, - len: 0, - }) - } else if let Some(file_handle) = handle.dyn_ref::() { - let file: web_sys::File = wasm_bindgen_futures::JsFuture::from(file_handle.get_file()) - .await - .map_err(crate::utils::js_error)? - .dyn_into() - .unwrap(); - - Ok(virtual_fs::Metadata { - ft: FileType { - file: true, - ..Default::default() - }, - accessed: 0, - created: 0, - modified: 0, - len: file.size() as u64, - }) - } else { - unreachable!() - } -} - -fn async_iterator_to_stream( - iter: js_sys::AsyncIterator, -) -> impl TryStream { - let (mut sender, receiver) = futures::channel::mpsc::channel(1); - - async fn iter_next(iter: &js_sys::AsyncIterator) -> Result, JsValue> { - let promise = iter.next()?; - let result = wasm_bindgen_futures::JsFuture::from(promise).await?; - - let done = js_sys::Reflect::get(&result, &JsValue::from_str("done"))? - .as_bool() - .unwrap_or(true); - - if done { - return Ok(None); - } - - let value = js_sys::Reflect::get(&result, &JsValue::from_str("value")) - .unwrap_or(JsValue::UNDEFINED); - - Ok(Some(value)) - } - - wasm_bindgen_futures::spawn_local(async move { - loop { - match iter_next(&iter).await { - Ok(Some(value)) => { - if sender.send(Ok(value)).await.is_err() { - break; - } - } - Ok(None) => break, - Err(e) => { - let _ = sender.send(Err(e)).await; - break; - } - } - } - }); - - receiver -} - -#[derive(Debug, Clone)] -struct CreateDirectory(PathBuf); - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = (); - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, msg: CreateDirectory) -> Result { - let path = msg.0.as_os_str().to_str().ok_or(FsError::InvalidInput)?; - - let error_codes = [ - ("NotAllowedError", FsError::PermissionDenied), - ("TypeMismatchError", FsError::BaseNotDirectory), - ("NotFoundError", FsError::EntryNotFound), - ]; - - self.eval(&error_codes, |d| { - d.get_directory_handle_with_options( - path, - FileSystemGetDirectoryOptions::new().create(true), - ) - }) - .await?; - - Ok(()) - } -} - -#[derive(Debug, Clone)] -struct RemoveDirectory(PathBuf); - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = (); - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, msg: RemoveDirectory) -> Result { - let path = msg.0.as_os_str().to_str().ok_or(FsError::InvalidInput)?; - - let error_codes = [ - ("NotAllowedError", FsError::PermissionDenied), - ("InvalidModificationError", FsError::DirectoryNotEmpty), - ("NotFoundError", FsError::EntryNotFound), - ]; - - self.eval(&error_codes, |d| d.remove_entry(path)).await?; - - Ok(()) - } -} - -#[derive(Debug, Clone)] -struct Rename { - from: PathBuf, - to: PathBuf, -} - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = (); - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, Rename { from, to }: Rename) -> Result { - // TODO: Add a polyfill for renaming an item - tracing::warn!( - ?from, - ?to, - "Renaming isn't implemented by the FileSystem API" - ); - Err(FsError::UnknownError) - } -} - -#[derive(Debug, Clone)] -struct RemoveFile(PathBuf); - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = (); - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, msg: RemoveFile) -> Result { - let path = msg.0.as_os_str().to_str().ok_or(FsError::InvalidInput)?; - - let error_codes = [ - ("NotAllowedError", FsError::PermissionDenied), - ("InvalidModificationError", FsError::DirectoryNotEmpty), - ("NotFoundError", FsError::EntryNotFound), - ]; - - self.eval(&error_codes, |d| d.remove_entry(path)).await?; - - Ok(()) - } -} - -#[derive(Debug, Clone)] -struct Open { - path: PathBuf, - conf: OpenOptionsConfig, -} - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = Box; - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, _msg: Open) -> Result { - todo!(); - } -} - -#[derive(Debug, Clone)] -struct Metadata(PathBuf); - -#[async_trait(?Send)] -impl Handler for Directory { - type Output = virtual_fs::Metadata; - - #[tracing::instrument(level = "trace", skip(self))] - async fn handle(&mut self, _msg: Metadata) -> Result { - todo!(); - } -} - -impl FileSystem for Mailbox { - fn read_dir(&self, path: &Path) -> virtual_fs::Result { - self.handle(ReadDirectory(path.to_path_buf())) - } - - fn create_dir(&self, path: &Path) -> virtual_fs::Result<()> { - self.handle(CreateDirectory(path.to_path_buf())) - } - - fn remove_dir(&self, path: &Path) -> virtual_fs::Result<()> { - self.handle(RemoveDirectory(path.to_path_buf())) - } - - fn rename<'a>( - &'a self, - from: &'a Path, - to: &'a Path, - ) -> futures::prelude::future::BoxFuture<'a, virtual_fs::Result<()>> { - Box::pin(self.send(Rename { - from: from.to_path_buf(), - to: to.to_path_buf(), - })) - } - - fn metadata(&self, path: &Path) -> virtual_fs::Result { - self.handle(Metadata(path.to_path_buf())) - } - - fn remove_file(&self, path: &Path) -> virtual_fs::Result<()> { - self.handle(RemoveFile(path.to_path_buf())) - } - - fn new_open_options(&self) -> virtual_fs::OpenOptions { - virtual_fs::OpenOptions::new(self) - } -} - -impl FileOpener for Mailbox { - fn open( - &self, - path: &Path, - conf: &OpenOptionsConfig, - ) -> virtual_fs::Result> { - let path = path.to_path_buf(); - let conf = conf.clone(); - - self.handle(Open { path, conf }) - } -} - -#[cfg(test)] -mod tests { - use std::{fmt::Debug, num::NonZeroUsize}; - - use tokio::sync::oneshot; - use wasm_bindgen::JsCast; - use wasm_bindgen_futures::JsFuture; - use wasm_bindgen_test::wasm_bindgen_test; - use wasmer_wasix::VirtualTaskManager; - - use super::*; - - #[wasm_bindgen_test] - async fn detect_deadlocks() { - let storage = web_sys::window().unwrap().navigator().storage(); - let handle: FileSystemDirectoryHandle = JsFuture::from(storage.get_directory()) - .await - .unwrap() - .dyn_into() - .unwrap(); - let fs = spawn(handle); - - let err = fs.read_dir("/".as_ref()).unwrap_err(); - - assert_eq!(err, FsError::Lock); - } - - /// Create a [`FileSystem`] from a [`FileSystemDirectoryHandle`] and - /// interact with it in a way that won't create deadlocks. - async fn with_fs(handle: FileSystemDirectoryHandle, op: F) -> Ret - where - F: FnOnce(&(dyn FileSystem + 'static)) -> Ret + Send + 'static, - Ret: Debug + Send + 'static, - { - let fs = spawn(handle); - let thread_pool = crate::tasks::ThreadPool::new(NonZeroUsize::new(2).unwrap()); - let (sender, receiver) = oneshot::channel(); - - thread_pool - .task_dedicated(Box::new(move || { - sender.send(op(&fs)).unwrap(); - })) - .unwrap(); - - receiver.await.unwrap() - } - - #[wasm_bindgen_test] - async fn read_dir() { - crate::on_start(); - let _ = crate::initialize_logger(Some("info,wasmer_js::fs=trace".into())); - let storage = web_sys::window().unwrap().navigator().storage(); - let handle: FileSystemDirectoryHandle = JsFuture::from(storage.get_directory()) - .await - .unwrap() - .dyn_into() - .unwrap(); - - let read_dir = with_fs(handle, |fs| fs.read_dir("/".as_ref())).await; - - let entries: Vec<_> = read_dir.unwrap().map(|e| e.unwrap().path()).collect(); - assert!(entries.is_empty()); - } - - #[wasm_bindgen_test] - async fn create_dir() { - crate::on_start(); - let _ = crate::initialize_logger(Some("info,wasmer_js::fs=trace".into())); - let storage = web_sys::window().unwrap().navigator().storage(); - let handle: FileSystemDirectoryHandle = JsFuture::from(storage.get_directory()) - .await - .unwrap() - .dyn_into() - .unwrap(); - - let entries: Vec<_> = with_fs(handle.clone(), |fs| { - fs.create_dir("root".as_ref()).unwrap(); - fs.create_dir("root/nested".as_ref()).unwrap(); - fs.read_dir("root".as_ref()).unwrap() - }) - .await - .map(|result| result.unwrap()) - .collect(); - - assert_eq!( - entries, - vec![DirEntry { - path: PathBuf::from("/root/nested"), - metadata: Ok(virtual_fs::Metadata { - ft: FileType::new_dir(), - ..Default::default() - }) - }] - ); - } -} diff --git a/tests/directory.test.ts b/tests/directory.test.ts index 01ec1fc0..97f32be7 100644 --- a/tests/directory.test.ts +++ b/tests/directory.test.ts @@ -49,63 +49,3 @@ describe("In-Memory Directory", function() { }); }); -describe("Web FileSystem", function() { - this.beforeAll(async () => await initialized); - - it("can read an empty dir", async() => { - const dirHandle = await navigator.storage.getDirectory(); - - const dir = Directory.fromBrowser(dirHandle); - const entries = await dir.readDir("/"); - - expect(entries).to.eql([]); - }); - - it("can read a file", async() => { - const dirHandle = await navigator.storage.getDirectory(); - const f = await dirHandle.getFileHandle("file.txt"); - const writer = await f.createWritable(); - await writer.write("Hello, World!"); - await writer.close(); - const dir = Directory.fromBrowser(dirHandle); - - const contents = await dir.readFile("/file.txt"); - - expect(contents).to.equal("Hello, World!"); - }); - - it("can create a file", async() => { - const dirHandle = await navigator.storage.getDirectory(); - const dir = Directory.fromBrowser(dirHandle); - - await dir.writeFile("/file.txt", "Hello, World!"); - - const handle = await dirHandle.getFileHandle("file.txt"); - const f = await handle.getFile(); - expect(await f.text()).to.equal("Hello, World!"); - }); - - it("can list a directory", async() => { - const dirHandle = await navigator.storage.getDirectory(); - const f = await dirHandle.getFileHandle("file.txt", {create: true}); - const dir = Directory.fromBrowser(dirHandle); - - const entries = await dir.readDir("/"); - - expect(entries).to.eql(["/file.txt"]); - }); - - it("can delete a file", async() => { - const dirHandle = await navigator.storage.getDirectory(); - const f = await dirHandle.getFileHandle("file.txt", {create: true}); - const dir = Directory.fromBrowser(dirHandle); - - await dir.removeFile("/file.txt"); - - const entries: string[] = []; - for await (const key of (dirHandle as any).keys()) { - entries.push(key); - } - expect(entries).to.eql([]); - }); -}); diff --git a/tests/integration.test.ts b/tests/integration.test.ts index 2bfdae6b..09618203 100644 --- a/tests/integration.test.ts +++ b/tests/integration.test.ts @@ -6,12 +6,12 @@ const decoder = new TextDecoder("utf-8"); const initialized = (async () => { await init(); - initializeLogger("info,wasmer_js::fs=trace"); + initializeLogger("info"); })(); const ansiEscapeCode = /\u001B\[[\d;]*[JDm]/g; -describe.skip("Wasmer.spawn", function() { +describe("Wasmer.spawn", function() { let wasmer: Wasmer; this.timeout("120s") @@ -294,50 +294,6 @@ describe.skip("Wasmer.spawn", function() { }); }); -describe("fs tests", function() { - let wasmer: Wasmer; - - this.timeout("120s") - .beforeAll(async () => { - await initialized; - - // Note: technically we should use a separate Wasmer instance so tests can't - // interact with each other, but in this case the caching benefits mean we - // complete in tens of seconds rather than several minutes. - wasmer = new Wasmer(); - }); - - it("can do all fs operations using the FileSystem API", async () => { - const dirHandle = await navigator.storage.getDirectory(); - const dir = Directory.fromBrowser(dirHandle); - const script = ` - ls / - ls /mounted - echo "Hello, World!" > /mounted/file.txt - cat /mounted/file.txt - mkdir /mounted/nested - ls /mounted - rm /mounted/file.txt - rmdir /mounted/nested/ - `; - - const instance = await wasmer.spawn("sharrattj/bash", { - command: "bash", - args: ["-xe", "-c", script], - mount: { "/mounted": dir }, - }); - const output = await instance.wait(); - - console.log({ - ...output, - stdout: decoder.decode(output.stdout), - stderr: decoder.decode(output.stderr), - }); - expect(output.ok).to.be.true; - expect(decoder.decode(output.stdout)).to.equal("asdf"); - }); -}); - // FIXME: Re-enable these test and move it to the "Wasmer.spawn" test suite // when we fix TTY handling with static inputs. describe.skip("failing tty handling tests", function() { diff --git a/tests/run.test.ts b/tests/run.test.ts index ddcde011..4c320011 100644 --- a/tests/run.test.ts +++ b/tests/run.test.ts @@ -7,11 +7,11 @@ const encoder = new TextEncoder(); const initialized = (async () => { await init(); - initializeLogger("info,wasmer_js::run=trace,wasmer_js::fs=trace,wasmer_wasix::syscalls=trace"); + initializeLogger("info"); wasmer = new Wasmer(); })(); -describe.skip("run", function() { +describe("run", function() { this.timeout("60s") .beforeAll(async () => await initialized);