Path: blob/main/crates/test-programs/src/preview1.rs
1693 views
use std::{sync::OnceLock, time::Duration};12pub fn config() -> &'static TestConfig {3static TESTCONFIG: OnceLock<TestConfig> = OnceLock::new();4TESTCONFIG.get_or_init(TestConfig::from_env)5}67// The `wasi` crate version 0.9.0 and beyond, doesn't8// seem to define these constants, so we do it ourselves.9pub const STDIN_FD: wasip1::Fd = 0x0;10pub const STDOUT_FD: wasip1::Fd = 0x1;11pub const STDERR_FD: wasip1::Fd = 0x2;1213/// Opens a fresh file descriptor for `path` where `path` should be a preopened14/// directory.15pub fn open_scratch_directory(path: &str) -> Result<wasip1::Fd, String> {16unsafe {17for i in 3.. {18let stat = match wasip1::fd_prestat_get(i) {19Ok(s) => s,20Err(_) => break,21};22if stat.tag != wasip1::PREOPENTYPE_DIR.raw() {23continue;24}25let mut dst = Vec::with_capacity(stat.u.dir.pr_name_len);26if wasip1::fd_prestat_dir_name(i, dst.as_mut_ptr(), dst.capacity()).is_err() {27continue;28}29dst.set_len(stat.u.dir.pr_name_len);30if dst == path.as_bytes() {31return Ok(32wasip1::path_open(i, 0, ".", wasip1::OFLAGS_DIRECTORY, 0, 0, 0)33.expect("failed to open dir"),34);35}36}3738Err(format!("failed to find scratch dir"))39}40}4142pub unsafe fn create_file(dir_fd: wasip1::Fd, filename: &str) {43unsafe {44let file_fd = wasip1::path_open(dir_fd, 0, filename, wasip1::OFLAGS_CREAT, 0, 0, 0)45.expect("creating a file");46assert!(file_fd > STDERR_FD, "file descriptor range check",);47wasip1::fd_close(file_fd).expect("closing a file");48}49}5051// Small workaround to get the crate's macros, through the52// `#[macro_export]` attribute below, also available from this module.53pub use crate::{assert_errno, assert_fs_time_eq};5455#[macro_export]56macro_rules! assert_errno {57($s:expr, windows => $i:expr, $( $rest:tt )+) => {58let e = $s;59if $crate::preview1::config().errno_expect_windows() {60assert_errno!(e, $i);61} else {62assert_errno!(e, $($rest)+, $i);63}64};65($s:expr, macos => $i:expr, $( $rest:tt )+) => {66let e = $s;67if $crate::preview1::config().errno_expect_macos() {68assert_errno!(e, $i);69} else {70assert_errno!(e, $($rest)+, $i);71}72};73($s:expr, unix => $i:expr, $( $rest:tt )+) => {74let e = $s;75if $crate::preview1::config().errno_expect_unix() {76assert_errno!(e, $i);77} else {78assert_errno!(e, $($rest)+, $i);79}80};81($s:expr, $( $i:expr ),+) => {82let e = $s;83{84// Pretty printing infrastructure85struct Alt<'a>(&'a [&'static str]);86impl<'a> std::fmt::Display for Alt<'a> {87fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {88let l = self.0.len();89if l == 0 {90unreachable!()91} else if l == 1 {92f.write_str(self.0[0])93} else if l == 2 {94f.write_str(self.0[0])?;95f.write_str(" or ")?;96f.write_str(self.0[1])97} else {98for (ix, s) in self.0.iter().enumerate() {99if ix == l - 1 {100f.write_str("or ")?;101f.write_str(s)?;102} else {103f.write_str(s)?;104f.write_str(", ")?;105}106}107Ok(())108}109}110}111assert!( $( e == $i || )+ false,112"expected errno {}; got {}",113Alt(&[ $( $i.name() ),+ ]),114e.name()115)116}117};118}119120#[macro_export]121macro_rules! assert_fs_time_eq {122($l:expr, $r:expr, $n:literal) => {123let diff = if $l > $r { $l - $r } else { $r - $l };124assert!(diff < $crate::preview1::config().fs_time_precision(), $n);125};126}127128pub struct TestConfig {129errno_mode: ErrnoMode,130fs_time_precision: u64,131no_dangling_filesystem: bool,132no_rename_dir_to_empty_dir: bool,133rename_dir_onto_file: bool,134}135136enum ErrnoMode {137Unix,138MacOS,139Windows,140Permissive,141}142143impl TestConfig {144pub fn from_env() -> Self {145let errno_mode = if std::env::var("ERRNO_MODE_UNIX").is_ok() {146ErrnoMode::Unix147} else if std::env::var("ERRNO_MODE_MACOS").is_ok() {148ErrnoMode::MacOS149} else if std::env::var("ERRNO_MODE_WINDOWS").is_ok() {150ErrnoMode::Windows151} else {152ErrnoMode::Permissive153};154let fs_time_precision = match std::env::var("FS_TIME_PRECISION") {155Ok(p) => p.parse().unwrap(),156Err(_) => 100,157};158let no_dangling_filesystem = std::env::var("NO_DANGLING_FILESYSTEM").is_ok();159let no_rename_dir_to_empty_dir = std::env::var("NO_RENAME_DIR_TO_EMPTY_DIR").is_ok();160TestConfig {161errno_mode,162fs_time_precision,163no_dangling_filesystem,164no_rename_dir_to_empty_dir,165rename_dir_onto_file: std::env::var("RENAME_DIR_ONTO_FILE").is_ok(),166}167}168pub fn errno_expect_unix(&self) -> bool {169match self.errno_mode {170ErrnoMode::Unix | ErrnoMode::MacOS => true,171_ => false,172}173}174pub fn errno_expect_macos(&self) -> bool {175match self.errno_mode {176ErrnoMode::MacOS => true,177_ => false,178}179}180pub fn errno_expect_windows(&self) -> bool {181match self.errno_mode {182ErrnoMode::Windows => true,183_ => false,184}185}186pub fn fs_time_precision(&self) -> Duration {187Duration::from_nanos(self.fs_time_precision)188}189pub fn support_dangling_filesystem(&self) -> bool {190!self.no_dangling_filesystem191}192pub fn support_rename_dir_to_empty_dir(&self) -> bool {193!self.no_rename_dir_to_empty_dir194}195pub fn support_rename_dir_onto_file(&self) -> bool {196self.rename_dir_onto_file197}198}199200201