use anyhow::{Context, Error}; use cap_std::time::Duration; use std::collections::HashMap; use wasi_common::{ dir::OpenResult, file::{FdFlags, OFlags}, sched::{Poll, RwEventFlags, SubscriptionResult, Userdata}, WasiDir, WasiFile, }; use wasi_tokio::{clocks_ctx, sched::poll_oneoff, Dir}; const TIMEOUT: Duration = Duration::from_millis(200); // Required for slow execution in CI #[tokio::test(flavor = "multi_thread")] async fn empty_file_readable() -> Result<(), Error> { let clocks = clocks_ctx(); let workspace = cap_tempfile::tempdir(cap_tempfile::ambient_authority()).expect("create tempdir"); workspace.create_dir("d").context("create dir")?; let d = workspace.open_dir("d").context("open dir")?; let d = Dir::from_cap_std(d); let f = d .open_file(false, "f", OFlags::CREATE, false, true, FdFlags::empty()) .await .context("create writable file f")?; let to_write: Vec = vec![0]; if let OpenResult::File(ref f) = f { f.write_vectored(&vec![std::io::IoSlice::new(&to_write)]) .await .context("write to f")?; } else { unreachable!(); } drop(f); let f = d .open_file(false, "f", OFlags::empty(), true, false, FdFlags::empty()) .await .context("open f as readable")?; let mut poll = Poll::new(); if let OpenResult::File(ref f) = f { poll.subscribe_read(f.as_ref(), Userdata::from(123)); } else { unreachable!(); } // Timeout bounds time in poll_oneoff let monotonic = &*clocks.monotonic()?.abs_clock; poll.subscribe_monotonic_clock( monotonic, monotonic .now(monotonic.resolution()) .checked_add(TIMEOUT) .unwrap(), monotonic.resolution(), Userdata::from(0), ); poll_oneoff(&mut poll).await?; let events = poll.results(); match events.get(0).expect("at least one event") { (SubscriptionResult::Read(Ok((1, flags))), ud) => { assert_eq!(*flags, RwEventFlags::empty()); assert_eq!(*ud, Userdata::from(123)); } _ => panic!("expected (Read(Ok(1, empty), 123), got: {:?}", events[0]), } Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn empty_file_writable() -> Result<(), Error> { let clocks = clocks_ctx(); let workspace = cap_tempfile::tempdir(cap_tempfile::ambient_authority()).expect("create tempdir"); workspace.create_dir("d").context("create dir")?; let d = workspace.open_dir("d").context("open dir")?; let d = Dir::from_cap_std(d); let writable_f = d .open_file(false, "f", OFlags::CREATE, true, true, FdFlags::empty()) .await .context("create writable file")?; let mut poll = Poll::new(); if let OpenResult::File(ref writable_f) = writable_f { poll.subscribe_write(writable_f.as_ref(), Userdata::from(123)); } else { unreachable!(); } // Timeout bounds time in poll_oneoff let monotonic = &*clocks.monotonic()?.abs_clock; poll.subscribe_monotonic_clock( monotonic, monotonic .now(monotonic.resolution()) .checked_add(TIMEOUT) .unwrap(), monotonic.resolution(), Userdata::from(0), ); poll_oneoff(&mut poll).await?; let events = poll.results(); match events.get(0).expect("at least one event") { (SubscriptionResult::Write(Ok((0, flags))), ud) => { assert_eq!(*flags, RwEventFlags::empty()); assert_eq!(*ud, Userdata::from(123)); } _ => panic!(""), } Ok(()) } #[tokio::test(flavor = "multi_thread")] async fn stdio_readable() -> Result<(), Error> { let clocks = clocks_ctx(); let monotonic = &*clocks.monotonic()?.abs_clock; let deadline = monotonic .now(monotonic.resolution()) .checked_add(TIMEOUT) .unwrap(); let mut waiting_on: HashMap> = vec![ ( 1, Box::new(wasi_tokio::stdio::stdout()) as Box, ), (2, Box::new(wasi_tokio::stdio::stderr())), ] .into_iter() .collect(); while !waiting_on.is_empty() { let mut poll = Poll::new(); for (ix, file) in waiting_on.iter_mut() { poll.subscribe_write(&mut **file, Userdata::from(*ix)); } // Timeout bounds time in poll_oneoff let monotonic = &*clocks.monotonic()?.abs_clock; poll.subscribe_monotonic_clock( monotonic, deadline, monotonic.resolution(), Userdata::from(999), ); poll_oneoff(&mut poll).await?; let events = poll.results(); for e in events { match e { (SubscriptionResult::Write(Ok(_)), ud) => { let _ = waiting_on.remove(&u64::from(ud)); } (SubscriptionResult::Write(Err(_)), ud) => { panic!("error on ix {}", u64::from(ud)) } (SubscriptionResult::Read { .. }, _) => unreachable!(), (SubscriptionResult::MonotonicClock { .. }, _) => { panic!("timed out before stdin and stdout ready for reading") } } } } Ok(()) }