Skip to content

Commit 566551c

Browse files
committed
wasi:filesystem@0.3.0-rc-2025-09-16: Add tests for stat
1 parent d0e0344 commit 566551c

File tree

2 files changed

+220
-0
lines changed

2 files changed

+220
-0
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"dirs": ["fs-tests.dir"]
3+
}
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
use std::process;
2+
extern crate wit_bindgen;
3+
4+
wit_bindgen::generate!({
5+
inline: r"
6+
package test:test;
7+
8+
world test {
9+
include wasi:filesystem/imports@0.3.0-rc-2025-09-16;
10+
include wasi:cli/command@0.3.0-rc-2025-09-16;
11+
}
12+
",
13+
additional_derives: [PartialEq, Eq, Hash, Clone],
14+
// Work around https://github.com/bytecodealliance/wasm-tools/issues/2285.
15+
features:["clocks-timezone"],
16+
async: [
17+
"wasi:cli/run@0.3.0-rc-2025-09-16#run",
18+
],
19+
generate_all
20+
});
21+
22+
use wasi::clocks::wall_clock::Datetime;
23+
use wasi::filesystem::types::Descriptor;
24+
use wasi::filesystem::types::NewTimestamp;
25+
use wasi::filesystem::types::{DescriptorFlags, ErrorCode, OpenFlags, PathFlags};
26+
use wasi::filesystem::types::{DescriptorStat, DescriptorType};
27+
28+
fn check_timestamp(t: Datetime) {
29+
assert!(t.nanoseconds < 1_000_000_000);
30+
}
31+
32+
fn check_stat(stat: &DescriptorStat, type_: DescriptorType) {
33+
assert_eq!(stat.type_, type_);
34+
// assert_eq!(stat.link_count, 0) ?
35+
// assert_eq!(stat.size, 0) ?
36+
if let Some(t) = stat.data_access_timestamp {
37+
check_timestamp(t)
38+
};
39+
if let Some(t) = stat.data_modification_timestamp {
40+
check_timestamp(t)
41+
};
42+
if let Some(t) = stat.status_change_timestamp {
43+
check_timestamp(t)
44+
}
45+
}
46+
47+
async fn test_stat(dir: &Descriptor) {
48+
let afd = dir
49+
.open_at(
50+
PathFlags::empty(),
51+
"a.txt".to_string(),
52+
OpenFlags::empty(),
53+
DescriptorFlags::READ,
54+
)
55+
.await
56+
.unwrap();
57+
let bfd = dir
58+
.open_at(
59+
PathFlags::empty(),
60+
"b.txt".to_string(),
61+
OpenFlags::empty(),
62+
DescriptorFlags::READ,
63+
)
64+
.await
65+
.unwrap();
66+
let stat_with_flags = |flags: PathFlags, path: &str| dir.stat_at(flags, path.to_string());
67+
let stat = |path: &str| stat_with_flags(PathFlags::empty(), path);
68+
let stat_follow = |path: &str| stat_with_flags(PathFlags::SYMLINK_FOLLOW, path);
69+
70+
// stat: async func() -> result<descriptor-stat, error-code>;
71+
check_stat(&dir.stat().await.unwrap(), DescriptorType::Directory);
72+
check_stat(&afd.stat().await.unwrap(), DescriptorType::RegularFile);
73+
check_stat(&bfd.stat().await.unwrap(), DescriptorType::RegularFile);
74+
75+
// stat-at: async func(path-flags: path-flags, path: string) -> result<descriptor-stat, error-code>;
76+
assert_eq!(
77+
afd.stat_at(PathFlags::empty(), "z.txt".to_string()).await,
78+
Err(ErrorCode::NotDirectory)
79+
);
80+
assert_eq!(
81+
afd.stat_at(PathFlags::empty(), ".".to_string()).await,
82+
Err(ErrorCode::NotDirectory)
83+
);
84+
85+
assert_eq!(stat("").await, Err(ErrorCode::NoEntry));
86+
assert_eq!(stat("..").await, Err(ErrorCode::NotPermitted));
87+
assert_eq!(stat_follow("parent").await, Err(ErrorCode::NotPermitted));
88+
assert_eq!(
89+
stat_follow("parent/fs-tests.dir").await,
90+
Err(ErrorCode::NotPermitted)
91+
);
92+
assert_eq!(stat(".").await, dir.stat().await);
93+
// FIXME: https://github.com/bytecodealliance/wasmtime/issues/11606
94+
// assert_eq!(stat_at("/").await,
95+
// Err(ErrorCode::NotPermitted));
96+
assert_eq!(stat("/etc/passwd").await, Err(ErrorCode::NotPermitted));
97+
assert_eq!(stat("z.txt").await, Err(ErrorCode::NoEntry));
98+
99+
// set-times-at: async func(path-flags: path-flags, path: string, data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>;
100+
// set-times: async func(data-access-timestamp: new-timestamp, data-modification-timestamp: new-timestamp) -> result<_, error-code>;
101+
let no_flags = PathFlags::empty();
102+
let follow_flag = PathFlags::SYMLINK_FOLLOW;
103+
let set_times_at = |flags, path: &str, atime, mtime| -> _ {
104+
dir.set_times_at(flags, path.to_string(),
105+
NewTimestamp::Timestamp(atime),
106+
NewTimestamp::Timestamp(mtime))
107+
};
108+
{
109+
let atime = Datetime {
110+
seconds: 42,
111+
nanoseconds: 0,
112+
};
113+
let mtime = Datetime {
114+
seconds: 69,
115+
nanoseconds: 0,
116+
};
117+
assert_eq!(set_times_at(no_flags, "z.txt", atime, mtime).await,
118+
Err(ErrorCode::NoEntry));
119+
assert_eq!(set_times_at(follow_flag, "", atime, mtime).await,
120+
Err(ErrorCode::NoEntry));
121+
assert_eq!(set_times_at(no_flags, "/", atime, mtime).await,
122+
Err(ErrorCode::NotPermitted));
123+
assert_eq!(set_times_at(no_flags, "..", atime, mtime).await,
124+
Err(ErrorCode::NotPermitted));
125+
assert_eq!(set_times_at(follow_flag, "parent", atime, mtime).await,
126+
Err(ErrorCode::NotPermitted));
127+
assert_eq!(set_times_at(no_flags, "../foo", atime, mtime).await,
128+
Err(ErrorCode::NotPermitted));
129+
assert_eq!(set_times_at(no_flags, "parent/foo", atime, mtime).await,
130+
Err(ErrorCode::NotPermitted));
131+
}
132+
133+
if let Some(atime) = afd.stat().await.unwrap().data_access_timestamp {
134+
let mtime = afd
135+
.stat()
136+
.await
137+
.unwrap()
138+
.data_modification_timestamp
139+
.unwrap();
140+
let new_atime = Datetime {
141+
seconds: 42,
142+
nanoseconds: 0,
143+
};
144+
let new_mtime = Datetime {
145+
seconds: 69,
146+
nanoseconds: 0,
147+
};
148+
assert_eq!(set_times_at(no_flags, "a.txt", new_atime, new_mtime).await,
149+
Ok(()));
150+
assert_eq!(
151+
afd.stat().await.unwrap().data_access_timestamp,
152+
Some(new_atime)
153+
);
154+
assert_eq!(
155+
afd.stat().await.unwrap().data_modification_timestamp,
156+
Some(new_mtime)
157+
);
158+
assert_eq!(set_times_at(no_flags, "a.txt", atime, mtime).await,
159+
Ok(()));
160+
assert_eq!(afd.stat().await.unwrap().data_access_timestamp, Some(atime));
161+
assert_eq!(
162+
afd.stat().await.unwrap().data_modification_timestamp,
163+
Some(mtime)
164+
);
165+
166+
assert_eq!(
167+
afd.set_times(
168+
NewTimestamp::Timestamp(new_atime),
169+
NewTimestamp::Timestamp(new_mtime)
170+
)
171+
.await,
172+
Ok(())
173+
);
174+
assert_eq!(
175+
afd.stat().await.unwrap().data_access_timestamp,
176+
Some(new_atime)
177+
);
178+
assert_eq!(
179+
afd.stat().await.unwrap().data_modification_timestamp,
180+
Some(new_mtime)
181+
);
182+
assert_eq!(
183+
afd.set_times(
184+
NewTimestamp::Timestamp(atime),
185+
NewTimestamp::Timestamp(mtime)
186+
)
187+
.await,
188+
Ok(())
189+
);
190+
assert_eq!(afd.stat().await.unwrap().data_access_timestamp, Some(atime));
191+
assert_eq!(
192+
afd.stat().await.unwrap().data_modification_timestamp,
193+
Some(mtime)
194+
);
195+
}
196+
}
197+
198+
struct Component;
199+
export!(Component);
200+
impl exports::wasi::cli::run::Guest for Component {
201+
async fn run() -> Result<(), ()> {
202+
match &wasi::filesystem::preopens::get_directories()[..] {
203+
[(dir, dirname)] if dirname == "fs-tests.dir" => {
204+
test_stat(dir).await;
205+
}
206+
[..] => {
207+
eprintln!("usage: run with one open dir named 'fs-tests.dir'");
208+
process::exit(1)
209+
}
210+
};
211+
Ok(())
212+
}
213+
}
214+
215+
fn main() {
216+
unreachable!("main is a stub");
217+
}

0 commit comments

Comments
 (0)