16506567cSGuy Bedford use std::{sync::OnceLock, time::Duration};
2f4be3606SAlex Crichton
config() -> &'static TestConfig3f4be3606SAlex Crichton pub fn config() -> &'static TestConfig {
4f4be3606SAlex Crichton static TESTCONFIG: OnceLock<TestConfig> = OnceLock::new();
5f4be3606SAlex Crichton TESTCONFIG.get_or_init(TestConfig::from_env)
6f4be3606SAlex Crichton }
7f4be3606SAlex Crichton
8f4be3606SAlex Crichton // The `wasi` crate version 0.9.0 and beyond, doesn't
9f4be3606SAlex Crichton // seem to define these constants, so we do it ourselves.
10953adeedSAlex Crichton pub const STDIN_FD: wasip1::Fd = 0x0;
11953adeedSAlex Crichton pub const STDOUT_FD: wasip1::Fd = 0x1;
12953adeedSAlex Crichton pub const STDERR_FD: wasip1::Fd = 0x2;
13f4be3606SAlex Crichton
14f4be3606SAlex Crichton /// Opens a fresh file descriptor for `path` where `path` should be a preopened
15f4be3606SAlex Crichton /// directory.
open_scratch_directory(path: &str) -> Result<wasip1::Fd, String>16953adeedSAlex Crichton pub fn open_scratch_directory(path: &str) -> Result<wasip1::Fd, String> {
17f4be3606SAlex Crichton unsafe {
18f4be3606SAlex Crichton for i in 3.. {
19953adeedSAlex Crichton let stat = match wasip1::fd_prestat_get(i) {
20f4be3606SAlex Crichton Ok(s) => s,
21f4be3606SAlex Crichton Err(_) => break,
22f4be3606SAlex Crichton };
23953adeedSAlex Crichton if stat.tag != wasip1::PREOPENTYPE_DIR.raw() {
24f4be3606SAlex Crichton continue;
25f4be3606SAlex Crichton }
26f4be3606SAlex Crichton let mut dst = Vec::with_capacity(stat.u.dir.pr_name_len);
27953adeedSAlex Crichton if wasip1::fd_prestat_dir_name(i, dst.as_mut_ptr(), dst.capacity()).is_err() {
28f4be3606SAlex Crichton continue;
29f4be3606SAlex Crichton }
30f4be3606SAlex Crichton dst.set_len(stat.u.dir.pr_name_len);
31f4be3606SAlex Crichton if dst == path.as_bytes() {
32953adeedSAlex Crichton return Ok(
33953adeedSAlex Crichton wasip1::path_open(i, 0, ".", wasip1::OFLAGS_DIRECTORY, 0, 0, 0)
34953adeedSAlex Crichton .expect("failed to open dir"),
35953adeedSAlex Crichton );
36f4be3606SAlex Crichton }
37f4be3606SAlex Crichton }
38f4be3606SAlex Crichton
39f4be3606SAlex Crichton Err(format!("failed to find scratch dir"))
40f4be3606SAlex Crichton }
41f4be3606SAlex Crichton }
42f4be3606SAlex Crichton
create_file(dir_fd: wasip1::Fd, filename: &str)43953adeedSAlex Crichton pub unsafe fn create_file(dir_fd: wasip1::Fd, filename: &str) {
44*073aedabSAlex Crichton unsafe {
45953adeedSAlex Crichton let file_fd = wasip1::path_open(dir_fd, 0, filename, wasip1::OFLAGS_CREAT, 0, 0, 0)
46953adeedSAlex Crichton .expect("creating a file");
47f4be3606SAlex Crichton assert!(file_fd > STDERR_FD, "file descriptor range check",);
48953adeedSAlex Crichton wasip1::fd_close(file_fd).expect("closing a file");
49f4be3606SAlex Crichton }
50*073aedabSAlex Crichton }
51f4be3606SAlex Crichton
526506567cSGuy Bedford // Small workaround to get the crate's macros, through the
53f4be3606SAlex Crichton // `#[macro_export]` attribute below, also available from this module.
546506567cSGuy Bedford pub use crate::{assert_errno, assert_fs_time_eq};
55f4be3606SAlex Crichton
56f4be3606SAlex Crichton #[macro_export]
57f4be3606SAlex Crichton macro_rules! assert_errno {
58f4be3606SAlex Crichton ($s:expr, windows => $i:expr, $( $rest:tt )+) => {
59f4be3606SAlex Crichton let e = $s;
60f4be3606SAlex Crichton if $crate::preview1::config().errno_expect_windows() {
61f4be3606SAlex Crichton assert_errno!(e, $i);
62f4be3606SAlex Crichton } else {
63f4be3606SAlex Crichton assert_errno!(e, $($rest)+, $i);
64f4be3606SAlex Crichton }
65f4be3606SAlex Crichton };
66f4be3606SAlex Crichton ($s:expr, macos => $i:expr, $( $rest:tt )+) => {
67f4be3606SAlex Crichton let e = $s;
68f4be3606SAlex Crichton if $crate::preview1::config().errno_expect_macos() {
69f4be3606SAlex Crichton assert_errno!(e, $i);
70f4be3606SAlex Crichton } else {
71f4be3606SAlex Crichton assert_errno!(e, $($rest)+, $i);
72f4be3606SAlex Crichton }
73f4be3606SAlex Crichton };
74f4be3606SAlex Crichton ($s:expr, unix => $i:expr, $( $rest:tt )+) => {
75f4be3606SAlex Crichton let e = $s;
76f4be3606SAlex Crichton if $crate::preview1::config().errno_expect_unix() {
77f4be3606SAlex Crichton assert_errno!(e, $i);
78f4be3606SAlex Crichton } else {
79f4be3606SAlex Crichton assert_errno!(e, $($rest)+, $i);
80f4be3606SAlex Crichton }
81f4be3606SAlex Crichton };
82f4be3606SAlex Crichton ($s:expr, $( $i:expr ),+) => {
83f4be3606SAlex Crichton let e = $s;
84f4be3606SAlex Crichton {
85f4be3606SAlex Crichton // Pretty printing infrastructure
86f4be3606SAlex Crichton struct Alt<'a>(&'a [&'static str]);
87f4be3606SAlex Crichton impl<'a> std::fmt::Display for Alt<'a> {
88f4be3606SAlex Crichton fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
89f4be3606SAlex Crichton let l = self.0.len();
90f4be3606SAlex Crichton if l == 0 {
91f4be3606SAlex Crichton unreachable!()
92f4be3606SAlex Crichton } else if l == 1 {
93f4be3606SAlex Crichton f.write_str(self.0[0])
94f4be3606SAlex Crichton } else if l == 2 {
95f4be3606SAlex Crichton f.write_str(self.0[0])?;
96f4be3606SAlex Crichton f.write_str(" or ")?;
97f4be3606SAlex Crichton f.write_str(self.0[1])
98f4be3606SAlex Crichton } else {
99f4be3606SAlex Crichton for (ix, s) in self.0.iter().enumerate() {
100f4be3606SAlex Crichton if ix == l - 1 {
101f4be3606SAlex Crichton f.write_str("or ")?;
102f4be3606SAlex Crichton f.write_str(s)?;
103f4be3606SAlex Crichton } else {
104f4be3606SAlex Crichton f.write_str(s)?;
105f4be3606SAlex Crichton f.write_str(", ")?;
106f4be3606SAlex Crichton }
107f4be3606SAlex Crichton }
108f4be3606SAlex Crichton Ok(())
109f4be3606SAlex Crichton }
110f4be3606SAlex Crichton }
111f4be3606SAlex Crichton }
112f4be3606SAlex Crichton assert!( $( e == $i || )+ false,
113f4be3606SAlex Crichton "expected errno {}; got {}",
114f4be3606SAlex Crichton Alt(&[ $( $i.name() ),+ ]),
115f4be3606SAlex Crichton e.name()
116f4be3606SAlex Crichton )
117f4be3606SAlex Crichton }
118f4be3606SAlex Crichton };
119f4be3606SAlex Crichton }
120f4be3606SAlex Crichton
1216506567cSGuy Bedford #[macro_export]
1226506567cSGuy Bedford macro_rules! assert_fs_time_eq {
1236506567cSGuy Bedford ($l:expr, $r:expr, $n:literal) => {
1246506567cSGuy Bedford let diff = if $l > $r { $l - $r } else { $r - $l };
1256506567cSGuy Bedford assert!(diff < $crate::preview1::config().fs_time_precision(), $n);
1266506567cSGuy Bedford };
1276506567cSGuy Bedford }
1286506567cSGuy Bedford
129f4be3606SAlex Crichton pub struct TestConfig {
130f4be3606SAlex Crichton errno_mode: ErrnoMode,
1316506567cSGuy Bedford fs_time_precision: u64,
132f4be3606SAlex Crichton no_dangling_filesystem: bool,
133f4be3606SAlex Crichton no_rename_dir_to_empty_dir: bool,
134b98f75aaSAlex Crichton rename_dir_onto_file: bool,
135f4be3606SAlex Crichton }
136f4be3606SAlex Crichton
137f4be3606SAlex Crichton enum ErrnoMode {
138f4be3606SAlex Crichton Unix,
139f4be3606SAlex Crichton MacOS,
140f4be3606SAlex Crichton Windows,
141f4be3606SAlex Crichton Permissive,
142f4be3606SAlex Crichton }
143f4be3606SAlex Crichton
144f4be3606SAlex Crichton impl TestConfig {
from_env() -> Self145f4be3606SAlex Crichton pub fn from_env() -> Self {
146f4be3606SAlex Crichton let errno_mode = if std::env::var("ERRNO_MODE_UNIX").is_ok() {
147f4be3606SAlex Crichton ErrnoMode::Unix
148f4be3606SAlex Crichton } else if std::env::var("ERRNO_MODE_MACOS").is_ok() {
149f4be3606SAlex Crichton ErrnoMode::MacOS
150f4be3606SAlex Crichton } else if std::env::var("ERRNO_MODE_WINDOWS").is_ok() {
151f4be3606SAlex Crichton ErrnoMode::Windows
152f4be3606SAlex Crichton } else {
153f4be3606SAlex Crichton ErrnoMode::Permissive
154f4be3606SAlex Crichton };
1556506567cSGuy Bedford let fs_time_precision = match std::env::var("FS_TIME_PRECISION") {
1566506567cSGuy Bedford Ok(p) => p.parse().unwrap(),
1576506567cSGuy Bedford Err(_) => 100,
1586506567cSGuy Bedford };
159f4be3606SAlex Crichton let no_dangling_filesystem = std::env::var("NO_DANGLING_FILESYSTEM").is_ok();
160f4be3606SAlex Crichton let no_rename_dir_to_empty_dir = std::env::var("NO_RENAME_DIR_TO_EMPTY_DIR").is_ok();
161f4be3606SAlex Crichton TestConfig {
162f4be3606SAlex Crichton errno_mode,
1636506567cSGuy Bedford fs_time_precision,
164f4be3606SAlex Crichton no_dangling_filesystem,
165f4be3606SAlex Crichton no_rename_dir_to_empty_dir,
166b98f75aaSAlex Crichton rename_dir_onto_file: std::env::var("RENAME_DIR_ONTO_FILE").is_ok(),
167f4be3606SAlex Crichton }
168f4be3606SAlex Crichton }
errno_expect_unix(&self) -> bool169f4be3606SAlex Crichton pub fn errno_expect_unix(&self) -> bool {
170f4be3606SAlex Crichton match self.errno_mode {
171f4be3606SAlex Crichton ErrnoMode::Unix | ErrnoMode::MacOS => true,
172f4be3606SAlex Crichton _ => false,
173f4be3606SAlex Crichton }
174f4be3606SAlex Crichton }
errno_expect_macos(&self) -> bool175f4be3606SAlex Crichton pub fn errno_expect_macos(&self) -> bool {
176f4be3606SAlex Crichton match self.errno_mode {
177f4be3606SAlex Crichton ErrnoMode::MacOS => true,
178f4be3606SAlex Crichton _ => false,
179f4be3606SAlex Crichton }
180f4be3606SAlex Crichton }
errno_expect_windows(&self) -> bool181f4be3606SAlex Crichton pub fn errno_expect_windows(&self) -> bool {
182f4be3606SAlex Crichton match self.errno_mode {
183f4be3606SAlex Crichton ErrnoMode::Windows => true,
184f4be3606SAlex Crichton _ => false,
185f4be3606SAlex Crichton }
186f4be3606SAlex Crichton }
fs_time_precision(&self) -> Duration1876506567cSGuy Bedford pub fn fs_time_precision(&self) -> Duration {
1886506567cSGuy Bedford Duration::from_nanos(self.fs_time_precision)
1896506567cSGuy Bedford }
support_dangling_filesystem(&self) -> bool190f4be3606SAlex Crichton pub fn support_dangling_filesystem(&self) -> bool {
191f4be3606SAlex Crichton !self.no_dangling_filesystem
192f4be3606SAlex Crichton }
support_rename_dir_to_empty_dir(&self) -> bool193f4be3606SAlex Crichton pub fn support_rename_dir_to_empty_dir(&self) -> bool {
194f4be3606SAlex Crichton !self.no_rename_dir_to_empty_dir
195f4be3606SAlex Crichton }
support_rename_dir_onto_file(&self) -> bool196b98f75aaSAlex Crichton pub fn support_rename_dir_onto_file(&self) -> bool {
197b98f75aaSAlex Crichton self.rename_dir_onto_file
198b98f75aaSAlex Crichton }
199f4be3606SAlex Crichton }
200