Skip to content

Commit b04d3fe

Browse files
committed
wasi:[email protected]: Add tests for read/write/append
1 parent d0e0344 commit b04d3fe

File tree

4 files changed

+227
-1
lines changed

4 files changed

+227
-1
lines changed

tests/rust/wasm32-wasip3/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/rust/wasm32-wasip3/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ version = "0.1.0"
44
edition = "2024"
55

66
[dependencies]
7-
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen.git" }
7+
futures = "0.3.31"
8+
wit-bindgen = { git = "https://github.com/bytecodealliance/wit-bindgen.git" }
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: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
use futures::join;
2+
use std::process;
3+
extern crate wit_bindgen;
4+
5+
wit_bindgen::generate!({
6+
inline: r"
7+
package test:test;
8+
9+
world test {
10+
include wasi:filesystem/[email protected];
11+
include wasi:cli/[email protected];
12+
}
13+
",
14+
additional_derives: [PartialEq, Eq, Hash, Clone],
15+
// Work around https://github.com/bytecodealliance/wasm-tools/issues/2285.
16+
features:["clocks-timezone"],
17+
generate_all
18+
});
19+
20+
use wasi::filesystem::types::Descriptor;
21+
use wasi::filesystem::types::{DescriptorFlags, ErrorCode, OpenFlags, PathFlags};
22+
use wit_bindgen::StreamResult;
23+
24+
async fn pread(fd: &Descriptor, size: usize, offset: u64) -> Result<Vec<u8>, ErrorCode> {
25+
let (mut rx, future) = fd.read_via_stream(offset);
26+
let data = Vec::<u8>::with_capacity(size);
27+
let mut bytes_read = 0;
28+
let (mut result, mut data) = rx.read(data).await;
29+
loop {
30+
match result {
31+
StreamResult::Complete(n) => {
32+
assert!(n <= size - bytes_read);
33+
bytes_read += n;
34+
assert_eq!(data.len(), bytes_read);
35+
if bytes_read == size {
36+
break;
37+
}
38+
(result, data) = rx.read(data).await;
39+
}
40+
StreamResult::Dropped => {
41+
assert_eq!(data.len(), bytes_read);
42+
break;
43+
}
44+
StreamResult::Cancelled => {
45+
panic!("who cancelled the stream?");
46+
}
47+
}
48+
};
49+
drop(rx);
50+
match future.await {
51+
Ok(()) => Ok(data),
52+
Err(err) => Err(err),
53+
}
54+
}
55+
56+
async fn pwrite(fd: &Descriptor, offset: u64, data: &[u8]) -> Result<usize, ErrorCode> {
57+
let (mut tx, rx) = wit_stream::new();
58+
let future = fd.write_via_stream(rx, offset);
59+
let len = data.len();
60+
let mut written: usize = 0;
61+
let mut result: Result<(), ErrorCode> = Ok(());
62+
join!{
63+
async {
64+
let (mut result, mut buf) = tx.write(data.to_vec()).await;
65+
loop {
66+
match result {
67+
StreamResult::Complete(n) => {
68+
assert!(n <= len - written);
69+
written += n;
70+
assert_eq!(buf.remaining(), len - written);
71+
if buf.remaining() != 0 {
72+
(result, buf) = tx.write_buf(buf).await;
73+
} else {
74+
break;
75+
}
76+
}
77+
StreamResult::Dropped => {
78+
panic!("receiver dropped the stream?");
79+
}
80+
StreamResult::Cancelled => {
81+
break;
82+
}
83+
}
84+
}
85+
assert_eq!(buf.remaining(), len - written);
86+
drop(tx);
87+
},
88+
async { result = future.await; }
89+
};
90+
match result {
91+
Ok(()) => Ok(written),
92+
Err(err) => Err(err),
93+
}
94+
}
95+
96+
async fn pappend(fd: &Descriptor, data: &[u8]) -> Result<usize, ErrorCode> {
97+
let (mut tx, rx) = wit_stream::new();
98+
let future = fd.append_via_stream(rx);
99+
let initial_size = fd.stat().await.unwrap().size as usize;
100+
let len = data.len();
101+
let mut written: usize = 0;
102+
let mut result: Result<(), ErrorCode> = Ok(());
103+
join!{
104+
async {
105+
let (mut result, mut buf) = tx.write(data.to_vec()).await;
106+
loop {
107+
match result {
108+
StreamResult::Complete(n) => {
109+
assert!(n <= len - written);
110+
written += n;
111+
assert_eq!(buf.remaining(), len - written);
112+
assert_eq!(fd.stat().await.unwrap().size as usize,
113+
initial_size + written);
114+
if buf.remaining() != 0 {
115+
(result, buf) = tx.write_buf(buf).await;
116+
} else {
117+
break;
118+
}
119+
}
120+
StreamResult::Dropped => {
121+
panic!("receiver dropped the stream?");
122+
}
123+
StreamResult::Cancelled => {
124+
break;
125+
}
126+
}
127+
}
128+
assert_eq!(buf.remaining(), len - written);
129+
drop(tx);
130+
},
131+
async { result = future.await; }
132+
};
133+
match result {
134+
Ok(()) => Ok(written),
135+
Err(err) => Err(err),
136+
}
137+
}
138+
139+
async fn read_to_eof(fd: &Descriptor, offset: u64) -> Vec<u8> {
140+
let (stream, success) = fd.read_via_stream(offset);
141+
let ret = stream.collect().await;
142+
success.await.unwrap();
143+
ret
144+
}
145+
146+
async fn test_io(dir: &Descriptor) {
147+
let open = |path: &str, oflags: OpenFlags, fdflags: DescriptorFlags| -> _ {
148+
dir.open_at(PathFlags::empty(), path.to_string(), oflags, fdflags)
149+
};
150+
let open_r = |path: &str| -> _ { open(path, OpenFlags::empty(), DescriptorFlags::READ) };
151+
let creat = |path: &str| -> _ {
152+
open(
153+
path,
154+
OpenFlags::CREATE | OpenFlags::EXCLUSIVE,
155+
DescriptorFlags::READ | DescriptorFlags::WRITE,
156+
)
157+
};
158+
let rm = |path: &str| dir.unlink_file_at(path.to_string());
159+
160+
let a = open_r("a.txt").await.unwrap();
161+
162+
pread(&a, 0, 0).await.unwrap();
163+
pread(&a, 0, 1).await.unwrap();
164+
pread(&a, 0, 6).await.unwrap();
165+
pread(&a, 0, 7).await.unwrap();
166+
pread(&a, 0, 17).await.unwrap();
167+
168+
assert_eq!(&pread(&a, 1, 0).await.unwrap(), b"t");
169+
assert_eq!(&pread(&a, 1, 1).await.unwrap(), b"e");
170+
assert_eq!(&pread(&a, 1, 6).await.unwrap(), b"\n");
171+
assert_eq!(&pread(&a, 1, 7).await.unwrap(), b"");
172+
assert_eq!(&pread(&a, 1, 17).await.unwrap(), b"");
173+
174+
assert_eq!(&read_to_eof(&a, 0).await, b"test-a\n");
175+
assert_eq!(&read_to_eof(&a, 1).await, b"est-a\n");
176+
assert_eq!(&read_to_eof(&a, 6).await, b"\n");
177+
assert_eq!(&read_to_eof(&a, 7).await, b"");
178+
assert_eq!(&read_to_eof(&a, 17).await, b"");
179+
180+
// No-op on read-only fds.
181+
a.sync_data().await.unwrap();
182+
a.sync().await.unwrap();
183+
184+
assert_eq!(pread(&a, 1, u64::MAX).await, Err(ErrorCode::Invalid));
185+
186+
let c = creat("c.cleanup").await.unwrap();
187+
assert_eq!(&read_to_eof(&c, 0).await, b"");
188+
assert_eq!(pwrite(&c, 0, b"hello!").await, Ok(b"hello!".len()));
189+
assert_eq!(&read_to_eof(&c, 0).await, b"hello!");
190+
assert_eq!(pwrite(&c, 0, b"byeee").await, Ok(b"byeee".len()));
191+
assert_eq!(&read_to_eof(&c, 0).await, b"byeee!");
192+
assert_eq!(pappend(&c, b" laters!!").await, Ok(b" laters!!".len()));
193+
assert_eq!(&read_to_eof(&c, 0).await, b"byeee! laters!!");
194+
c.sync_data().await.unwrap();
195+
assert_eq!(&read_to_eof(&open_r("c.cleanup").await.unwrap(), 0).await,
196+
b"byeee! laters!!");
197+
c.sync().await.unwrap();
198+
199+
rm("c.cleanup").await.unwrap();
200+
}
201+
202+
struct Component;
203+
export!(Component);
204+
impl exports::wasi::cli::run::Guest for Component {
205+
async fn run() -> Result<(), ()> {
206+
match &wasi::filesystem::preopens::get_directories()[..] {
207+
[(dir, dirname)] if dirname == "fs-tests.dir" => {
208+
test_io(dir).await;
209+
}
210+
[..] => {
211+
eprintln!("usage: run with one open dir named 'fs-tests.dir'");
212+
process::exit(1)
213+
}
214+
};
215+
Ok(())
216+
}
217+
}
218+
219+
fn main() {
220+
unreachable!("main is a stub");
221+
}

0 commit comments

Comments
 (0)