Skip to content

Commit 3665ec6

Browse files
authored
Merge pull request #144 from wingo/filesystem-stat
wasi:filesystem@0.3.0-rc-2025-09-16: Add tests for stat
2 parents 8f8c88d + 0961881 commit 3665ec6

2 files changed

Lines changed: 233 additions & 0 deletions

File tree

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

0 commit comments

Comments
 (0)