Skip to content

Commit

Permalink
Port more tests from Wasmtime's testsuite
Browse files Browse the repository at this point in the history
Following up on ac32f57, port several
more tests from Wasmtime's testsuite to wasi-testsuite.

It is likely that at least some of these tests are testing behavior
which differs between Wasm engine implementations. The intent here is
not to instate these as authoritative, but to help surface these
differences so that we can discuss whether it's best to change the test,
or add documentation to the spec.

Other Wasm engines are welcome to submit their tests to wasi-testsuite
as well.
  • Loading branch information
sunfishcode authored and loganek committed Aug 21, 2023
1 parent ad174d5 commit 385cb01
Show file tree
Hide file tree
Showing 59 changed files with 2,756 additions and 25 deletions.
1 change: 1 addition & 0 deletions tests/rust/src/bin/dangling_fd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ unsafe fn test_dangling_fd(dir_fd: wasi::Fd) {
wasi::path_unlink_file(dir_fd, FILE_NAME).expect("failed to unlink");
let fd = wasi::path_open(dir_fd, 0, FILE_NAME, wasi::OFLAGS_CREAT, 0, 0, 0).unwrap();
wasi::fd_close(fd).unwrap();
wasi::path_unlink_file(dir_fd, FILE_NAME).expect("failed to unlink");

// Now, repeat the same process but for a directory
wasi::path_create_directory(dir_fd, DIR_NAME).expect("failed to create dir");
Expand Down
25 changes: 1 addition & 24 deletions tests/rust/src/bin/fd_readdir.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::{env, mem, process, slice, str};
use wasi::path_create_directory;
use wasi_tests::open_scratch_directory;
use wasi_tests::{create_tmp_dir, open_scratch_directory};

const BUF_LEN: usize = 256;

Expand Down Expand Up @@ -53,28 +52,6 @@ impl<'a> Iterator for ReadDir<'a> {
}
}

unsafe fn create_tmp_dir(dir_fd: wasi::Fd, name: &str) -> wasi::Fd {
path_create_directory(dir_fd, name).expect("failed to create dir");
wasi::path_open(
dir_fd,
0,
name,
wasi::OFLAGS_DIRECTORY,
wasi::RIGHTS_FD_FILESTAT_GET
| wasi::RIGHTS_FD_READDIR
| wasi::RIGHTS_PATH_CREATE_FILE
| wasi::RIGHTS_PATH_OPEN
| wasi::RIGHTS_PATH_UNLINK_FILE,
wasi::RIGHTS_FD_READ
| wasi::RIGHTS_FD_WRITE
| wasi::RIGHTS_FD_READDIR
| wasi::RIGHTS_FD_FILESTAT_GET
| wasi::RIGHTS_FD_SEEK,
0,
)
.expect("failed to open dir")
}

/// Return the entries plus a bool indicating EOF.
unsafe fn exec_fd_readdir(fd: wasi::Fd, cookie: wasi::Dircookie) -> (Vec<DirEntry>, bool) {
let mut buf: [u8; BUF_LEN] = [0; BUF_LEN];
Expand Down
77 changes: 77 additions & 0 deletions tests/rust/src/bin/file_allocate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::{env, process};
use wasi_tests::{create_tmp_dir, open_scratch_directory, TESTCONFIG};

unsafe fn test_file_allocate(dir_fd: wasi::Fd) {
// Create a file in the scratch directory.
let file_fd = wasi::path_open(
dir_fd,
0,
"file",
wasi::OFLAGS_CREAT,
wasi::RIGHTS_FD_READ
| wasi::RIGHTS_FD_WRITE
| wasi::RIGHTS_FD_ALLOCATE
| wasi::RIGHTS_FD_FILESTAT_GET,
0,
0,
)
.expect("opening a file");
assert!(
file_fd > libc::STDERR_FILENO as wasi::Fd,
"file descriptor range check",
);

// Check file size
let mut stat = wasi::fd_filestat_get(file_fd).expect("reading file stats");
assert_eq!(stat.size, 0, "file size should be 0");

if TESTCONFIG.support_fd_allocate() {
// Allocate some size
wasi::fd_allocate(file_fd, 0, 100).expect("allocating size");
stat = wasi::fd_filestat_get(file_fd).expect("reading file stats");
assert_eq!(stat.size, 100, "file size should be 100");

// Allocate should not modify if less than current size
wasi::fd_allocate(file_fd, 10, 10).expect("allocating size less than current size");
stat = wasi::fd_filestat_get(file_fd).expect("reading file stats");
assert_eq!(stat.size, 100, "file size should remain unchanged at 100");

// Allocate should modify if offset+len > current_len
wasi::fd_allocate(file_fd, 90, 20).expect("allocating size larger than current size");
stat = wasi::fd_filestat_get(file_fd).expect("reading file stats");
assert_eq!(stat.size, 110, "file size should increase from 100 to 110");
}
wasi::fd_close(file_fd).expect("closing a file");
wasi::path_unlink_file(dir_fd, "file").expect("removing a file");
}

fn main() {
let mut args = env::args();
let prog = args.next().unwrap();
let arg = if let Some(arg) = args.next() {
arg
} else {
eprintln!("usage: {} <scratch directory>", prog);
process::exit(1);
};

// Open scratch directory
let base_dir_fd = match open_scratch_directory(&arg) {
Ok(dir_fd) => dir_fd,
Err(err) => {
eprintln!("{}", err);
process::exit(1)
}
};

const DIR_NAME: &str = "file_allocate_dir.cleanup";
let dir_fd;
unsafe {
dir_fd = create_tmp_dir(base_dir_fd, DIR_NAME);
}

// Run the tests.
unsafe { test_file_allocate(dir_fd) }

unsafe { wasi::path_remove_directory(base_dir_fd, DIR_NAME).expect("failed to remove dir") }
}
160 changes: 160 additions & 0 deletions tests/rust/src/bin/file_pread_pwrite.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use std::convert::TryInto;
use std::{env, process};
use wasi_tests::{create_tmp_dir, open_scratch_directory};

unsafe fn test_file_pread_pwrite(dir_fd: wasi::Fd) {
// Create a file in the scratch directory.
let file_fd = wasi::path_open(
dir_fd,
0,
"file",
wasi::OFLAGS_CREAT,
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_SEEK | wasi::RIGHTS_FD_WRITE,
0,
0,
)
.expect("opening a file");
assert!(
file_fd > libc::STDERR_FILENO as wasi::Fd,
"file descriptor range check",
);

let contents = &[0u8, 1, 2, 3];
let ciovec = wasi::Ciovec {
buf: contents.as_ptr() as *const _,
buf_len: contents.len(),
};
let mut nwritten =
wasi::fd_pwrite(file_fd, &mut [ciovec], 0).expect("writing bytes at offset 0");
assert_eq!(nwritten, 4, "nwritten bytes check");

let contents = &mut [0u8; 4];
let iovec = wasi::Iovec {
buf: contents.as_mut_ptr() as *mut _,
buf_len: contents.len(),
};
let mut nread = wasi::fd_pread(file_fd, &[iovec], 0).expect("reading bytes at offset 0");
assert_eq!(nread, 4, "nread bytes check");
assert_eq!(contents, &[0u8, 1, 2, 3], "written bytes equal read bytes");

// Write all the data through multiple iovecs.
//
// Note that this needs to be done with a loop, because some
// platforms do not support writing multiple iovecs at once.
// See https://github.com/rust-lang/rust/issues/74825.
let contents = &[0u8, 1, 2, 3];
let mut offset = 0usize;
loop {
let mut ciovecs: Vec<wasi::Ciovec> = Vec::new();
let mut remaining = contents.len() - offset;
if remaining > 2 {
ciovecs.push(wasi::Ciovec {
buf: contents[offset..].as_ptr() as *const _,
buf_len: 2,
});
remaining -= 2;
}
ciovecs.push(wasi::Ciovec {
buf: contents[contents.len() - remaining..].as_ptr() as *const _,
buf_len: remaining,
});

nwritten = wasi::fd_pwrite(file_fd, ciovecs.as_slice(), offset.try_into().unwrap())
.expect("writing bytes at offset 0");

offset += nwritten;
if offset == contents.len() {
break;
}
}
assert_eq!(offset, 4, "nread bytes check");

// Read all the data through multiple iovecs.
//
// Note that this needs to be done with a loop, because some
// platforms do not support reading multiple iovecs at once.
// See https://github.com/rust-lang/rust/issues/74825.
let contents = &mut [0u8; 4];
let mut offset = 0usize;
loop {
let buffer = &mut [0u8; 4];
let iovecs = &[
wasi::Iovec {
buf: buffer.as_mut_ptr() as *mut _,
buf_len: 2,
},
wasi::Iovec {
buf: buffer[2..].as_mut_ptr() as *mut _,
buf_len: 2,
},
];
nread = wasi::fd_pread(file_fd, iovecs, offset as _).expect("reading bytes at offset 0");
if nread == 0 {
break;
}
contents[offset..offset + nread].copy_from_slice(&buffer[0..nread]);
offset += nread;
}
assert_eq!(offset, 4, "nread bytes check");
assert_eq!(contents, &[0u8, 1, 2, 3], "file cursor was overwritten");

let contents = &mut [0u8; 4];
let iovec = wasi::Iovec {
buf: contents.as_mut_ptr() as *mut _,
buf_len: contents.len(),
};
nread = wasi::fd_pread(file_fd, &[iovec], 2).expect("reading bytes at offset 2");
assert_eq!(nread, 2, "nread bytes check");
assert_eq!(contents, &[2u8, 3, 0, 0], "file cursor was overwritten");

let contents = &[1u8, 0];
let ciovec = wasi::Ciovec {
buf: contents.as_ptr() as *const _,
buf_len: contents.len(),
};
nwritten = wasi::fd_pwrite(file_fd, &mut [ciovec], 2).expect("writing bytes at offset 2");
assert_eq!(nwritten, 2, "nwritten bytes check");

let contents = &mut [0u8; 4];
let iovec = wasi::Iovec {
buf: contents.as_mut_ptr() as *mut _,
buf_len: contents.len(),
};
nread = wasi::fd_pread(file_fd, &[iovec], 0).expect("reading bytes at offset 0");
assert_eq!(nread, 4, "nread bytes check");
assert_eq!(contents, &[0u8, 1, 1, 0], "file cursor was overwritten");

wasi::fd_close(file_fd).expect("closing a file");
wasi::path_unlink_file(dir_fd, "file").expect("removing a file");
}

fn main() {
let mut args = env::args();
let prog = args.next().unwrap();
let arg = if let Some(arg) = args.next() {
arg
} else {
eprintln!("usage: {} <scratch directory>", prog);
process::exit(1);
};

// Open scratch directory
let base_dir_fd = match open_scratch_directory(&arg) {
Ok(dir_fd) => dir_fd,
Err(err) => {
eprintln!("{}", err);
process::exit(1)
}
};

const DIR_NAME: &str = "file_pread_pwrite_dir.cleanup";
let dir_fd;
unsafe {
dir_fd = create_tmp_dir(base_dir_fd, DIR_NAME);
}

// Run the tests.
unsafe { test_file_pread_pwrite(dir_fd) }

unsafe { wasi::path_remove_directory(base_dir_fd, DIR_NAME).expect("failed to remove dir") }
}
111 changes: 111 additions & 0 deletions tests/rust/src/bin/file_seek_tell.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
use std::{env, process};
use wasi_tests::{assert_errno, create_tmp_dir, open_scratch_directory};

unsafe fn test_file_seek_tell(dir_fd: wasi::Fd) {
// Create a file in the scratch directory.
let file_fd = wasi::path_open(
dir_fd,
0,
"file",
wasi::OFLAGS_CREAT,
wasi::RIGHTS_FD_READ | wasi::RIGHTS_FD_WRITE | wasi::RIGHTS_FD_SEEK | wasi::RIGHTS_FD_TELL,
0,
0,
)
.expect("opening a file");
assert!(
file_fd > libc::STDERR_FILENO as wasi::Fd,
"file descriptor range check",
);

// Check current offset
let mut offset = wasi::fd_tell(file_fd).expect("getting initial file offset");
assert_eq!(offset, 0, "current offset should be 0");

// Write to file
let data = &[0u8; 100];
let iov = wasi::Ciovec {
buf: data.as_ptr() as *const _,
buf_len: data.len(),
};
let nwritten = wasi::fd_write(file_fd, &[iov]).expect("writing to a file");
assert_eq!(nwritten, 100, "should write 100 bytes to file");

// Check current offset
offset = wasi::fd_tell(file_fd).expect("getting file offset after writing");
assert_eq!(offset, 100, "offset after writing should be 100");

// Seek to middle of the file
let mut newoffset =
wasi::fd_seek(file_fd, -50, wasi::WHENCE_CUR).expect("seeking to the middle of a file");
assert_eq!(
newoffset, 50,
"offset after seeking to the middle should be at 50"
);

// Seek to the beginning of the file
newoffset =
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET).expect("seeking to the beginning of the file");
assert_eq!(
newoffset, 0,
"offset after seeking to the beginning of the file should be at 0"
);

// Seek beyond the file should be possible
wasi::fd_seek(file_fd, 1000, wasi::WHENCE_CUR).expect("seeking beyond the end of the file");

// Seek before byte 0 is an error though
assert_errno!(
wasi::fd_seek(file_fd, -2000, wasi::WHENCE_CUR)
.expect_err("seeking before byte 0 should be an error"),
wasi::ERRNO_INVAL
);

// Check that fd_read properly updates the file offset
wasi::fd_seek(file_fd, 0, wasi::WHENCE_SET)
.expect("seeking to the beginning of the file again");

let buffer = &mut [0u8; 100];
let iovec = wasi::Iovec {
buf: buffer.as_mut_ptr(),
buf_len: buffer.len(),
};
let nread = wasi::fd_read(file_fd, &[iovec]).expect("reading file");
assert_eq!(nread, buffer.len(), "should read {} bytes", buffer.len());

offset = wasi::fd_tell(file_fd).expect("getting file offset after reading");
assert_eq!(offset, 100, "offset after reading should be 100");

wasi::fd_close(file_fd).expect("closing a file");
wasi::path_unlink_file(dir_fd, "file").expect("deleting a file");
}
fn main() {
let mut args = env::args();
let prog = args.next().unwrap();
let arg = if let Some(arg) = args.next() {
arg
} else {
eprintln!("usage: {} <scratch directory>", prog);
process::exit(1);
};

// Open scratch directory
let base_dir_fd = match open_scratch_directory(&arg) {
Ok(dir_fd) => dir_fd,
Err(err) => {
eprintln!("{}", err);
process::exit(1)
}
};

const DIR_NAME: &str = "file_seek_tell_dir.cleanup";
let dir_fd;
unsafe {
dir_fd = create_tmp_dir(base_dir_fd, DIR_NAME);
}

// Run the tests.
unsafe { test_file_seek_tell(dir_fd) }

unsafe { wasi::path_remove_directory(base_dir_fd, DIR_NAME).expect("failed to remove dir") }
}
Loading

0 comments on commit 385cb01

Please sign in to comment.