mirror of
https://github.com/fluencelabs/wasmer
synced 2025-06-22 05:01:33 +00:00
Add WASI serializing test
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1742,6 +1742,7 @@ dependencies = [
|
||||
"wasmer-clif-backend 0.6.0",
|
||||
"wasmer-dev-utils 0.6.0",
|
||||
"wasmer-llvm-backend 0.6.0",
|
||||
"wasmer-runtime 0.6.0",
|
||||
"wasmer-runtime-core 0.6.0",
|
||||
"wasmer-singlepass-backend 0.6.0",
|
||||
"wasmer-wasi 0.6.0",
|
||||
|
1
Makefile
1
Makefile
@ -76,6 +76,7 @@ wasitests-llvm: wasitests-setup
|
||||
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features llvm -- --test-threads=1
|
||||
|
||||
wasitests-unit:
|
||||
cargo test --manifest-path lib/wasi-tests/Cargo.toml --release --features clif -- --test-threads=1
|
||||
cargo test --manifest-path lib/wasi/Cargo.toml --release
|
||||
|
||||
wasitests: wasitests-unit wasitests-singlepass wasitests-cranelift wasitests-llvm
|
||||
|
@ -10,6 +10,7 @@ build = "build/mod.rs"
|
||||
|
||||
[dependencies]
|
||||
wasmer-runtime-core = { path = "../runtime-core", version = "0.6.0" }
|
||||
wasmer-runtime = { path = "../runtime", version = "0.6.0" }
|
||||
wasmer-wasi = { path = "../wasi", version = "0.6.0" }
|
||||
# hack to get tests to work
|
||||
wasmer-singlepass-backend = { path = "../singlepass-backend", version = "0.6.0", optional = true }
|
||||
|
@ -1 +1,60 @@
|
||||
// nothing to see here
|
||||
#![cfg(test)]
|
||||
use wasmer_runtime::{compile, Func};
|
||||
use wasmer_runtime_core::vm::Ctx;
|
||||
use wasmer_wasi::{state::*, *};
|
||||
|
||||
use std::ffi::c_void;
|
||||
|
||||
#[test]
|
||||
fn serializing_works() {
|
||||
let args = vec![
|
||||
b"program_name".into_iter().cloned().collect(),
|
||||
b"arg1".into_iter().cloned().collect(),
|
||||
];
|
||||
let envs = vec![
|
||||
b"PATH=/bin".into_iter().cloned().collect(),
|
||||
b"GOROOT=$HOME/.cargo/bin".into_iter().cloned().collect(),
|
||||
];
|
||||
let wasm_binary = include_bytes!("../wasitests/fd_read.wasm");
|
||||
let import_object = generate_import_object(
|
||||
args.clone(),
|
||||
envs.clone(),
|
||||
vec![],
|
||||
vec![(
|
||||
".".to_string(),
|
||||
std::path::PathBuf::from("wasitests/test_fs/hamlet"),
|
||||
)],
|
||||
);
|
||||
let module = compile(&wasm_binary[..])
|
||||
.map_err(|e| format!("Can't compile module: {:?}", e))
|
||||
.unwrap();
|
||||
|
||||
let state_bytes = {
|
||||
let instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
let start: Func<(), ()> = instance.func("_start").unwrap();
|
||||
start.call().unwrap();
|
||||
let state = get_wasi_state(instance.context());
|
||||
|
||||
assert_eq!(state.args, args);
|
||||
assert_eq!(state.envs, envs);
|
||||
let bytes = state.freeze().unwrap();
|
||||
|
||||
bytes
|
||||
};
|
||||
|
||||
let mut instance = module.instantiate(&import_object).unwrap();
|
||||
|
||||
let wasi_state = Box::new(WasiState::unfreeze(&state_bytes).unwrap());
|
||||
|
||||
instance.context_mut().data = Box::into_raw(wasi_state) as *mut c_void;
|
||||
|
||||
let second_entry: Func<(), i32> = instance.func("second_entry").unwrap();
|
||||
let result = second_entry.call().unwrap();
|
||||
assert_eq!(result, true as i32);
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) fn get_wasi_state(ctx: &Ctx) -> &mut WasiState {
|
||||
unsafe { state::get_wasi_state(&mut *(ctx as *const Ctx as *mut Ctx)) }
|
||||
}
|
||||
|
14
lib/wasi-tests/tests/wasitests/fd_read.rs
Normal file
14
lib/wasi-tests/tests/wasitests/fd_read.rs
Normal file
@ -0,0 +1,14 @@
|
||||
#[test]
|
||||
fn test_fd_read() {
|
||||
assert_wasi_output!(
|
||||
"../../wasitests/fd_read.wasm",
|
||||
"fd_read",
|
||||
vec![],
|
||||
vec![(
|
||||
".".to_string(),
|
||||
::std::path::PathBuf::from("wasitests/test_fs/hamlet")
|
||||
),],
|
||||
vec![],
|
||||
"../../wasitests/fd_read.out"
|
||||
);
|
||||
}
|
@ -9,6 +9,7 @@ mod create_dir;
|
||||
mod envvar;
|
||||
mod fd_allocate;
|
||||
mod fd_pread;
|
||||
mod fd_read;
|
||||
mod fd_sync;
|
||||
mod file_metadata;
|
||||
mod fs_sandbox_test;
|
||||
|
3
lib/wasi-tests/wasitests/fd_read.out
Normal file
3
lib/wasi-tests/wasitests/fd_read.out
Normal file
@ -0,0 +1,3 @@
|
||||
SCENE IV. The Queen's closet.
|
||||
|
||||
Enter QUEEN GERTRUDE and POLO
|
86
lib/wasi-tests/wasitests/fd_read.rs
Normal file
86
lib/wasi-tests/wasitests/fd_read.rs
Normal file
@ -0,0 +1,86 @@
|
||||
// Args:
|
||||
// mapdir: .:wasitests/test_fs/hamlet
|
||||
|
||||
// this program is used in the pause/resume test
|
||||
|
||||
use std::fs;
|
||||
#[cfg(target_os = "wasi")]
|
||||
use std::os::wasi::prelude::AsRawFd;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
#[repr(C)]
|
||||
struct WasiIovec {
|
||||
pub buf: u32,
|
||||
pub buf_len: u32,
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
#[link(wasm_import_module = "wasi_unstable")]
|
||||
extern "C" {
|
||||
fn fd_read(fd: u32, iovs: u32, iovs_len: u32, nread: u32) -> u16;
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
fn read(fd: u32, iovs: &[&mut [u8]]) -> u32 {
|
||||
let mut nread = 0;
|
||||
let mut processed_iovs = vec![];
|
||||
|
||||
for iov in iovs {
|
||||
processed_iovs.push(WasiIovec {
|
||||
buf: iov.as_ptr() as usize as u32,
|
||||
buf_len: iov.len() as u32,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe {
|
||||
fd_read(
|
||||
fd,
|
||||
processed_iovs.as_ptr() as usize as u32,
|
||||
processed_iovs.len() as u32,
|
||||
&mut nread as *mut u32 as usize as u32,
|
||||
);
|
||||
}
|
||||
nread
|
||||
}
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
let mut base = PathBuf::from("wasitests/test_fs/hamlet");
|
||||
#[cfg(target_os = "wasi")]
|
||||
let mut base = PathBuf::from(".");
|
||||
|
||||
base.push("act3/scene4.txt");
|
||||
let mut file = fs::File::open(&base).expect("Could not open file");
|
||||
let mut buffer = [0u8; 64];
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
{
|
||||
let raw_fd = file.as_raw_fd();
|
||||
assert_eq!(read(raw_fd, &[&mut buffer]), 64);
|
||||
let str_val = std::str::from_utf8(&buffer[..]).unwrap().to_string();
|
||||
println!("{}", &str_val);
|
||||
}
|
||||
// leak the file handle so that we can use it later
|
||||
std::mem::forget(file);
|
||||
|
||||
#[cfg(not(target_os = "wasi"))]
|
||||
{
|
||||
// eh, just print the output directly
|
||||
print!(
|
||||
"SCENE IV. The Queen's closet.
|
||||
|
||||
Enter QUEEN GERTRUDE and POLO"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "wasi")]
|
||||
#[no_mangle]
|
||||
fn second_entry() -> bool {
|
||||
let raw_fd = 5;
|
||||
let mut buffer = [0u8; 8];
|
||||
let result = read(raw_fd, &[&mut buffer]);
|
||||
|
||||
&buffer == b"NIUS \n\nL"
|
||||
}
|
BIN
lib/wasi-tests/wasitests/fd_read.wasm
Executable file
BIN
lib/wasi-tests/wasitests/fd_read.wasm
Executable file
Binary file not shown.
@ -54,7 +54,7 @@ pub fn generate_import_object(
|
||||
});
|
||||
|
||||
(
|
||||
Box::leak(state) as *mut WasiState as *mut c_void,
|
||||
Box::into_raw(state) as *mut c_void,
|
||||
state_destructor as fn(*mut c_void),
|
||||
)
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
/// types for use in the WASI filesystem
|
||||
use crate::syscalls::types::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde::{de, Deserialize, Serialize};
|
||||
#[cfg(unix)]
|
||||
use std::convert::TryInto;
|
||||
use std::{
|
||||
@ -349,61 +349,93 @@ pub struct HostFile {
|
||||
flags: u16,
|
||||
}
|
||||
|
||||
struct HostFileVisitor;
|
||||
|
||||
impl<'de> serde::de::Visitor<'de> for HostFileVisitor {
|
||||
type Value = HostFile;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("a HostFile struct")
|
||||
}
|
||||
|
||||
/*fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E> {
|
||||
let host_path = unimplemented!();
|
||||
let flags = unimplemented!();
|
||||
Ok(HostFile {
|
||||
inner,
|
||||
host_path,
|
||||
flags,
|
||||
})
|
||||
}*/
|
||||
|
||||
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: serde::de::SeqAccess<'de>,
|
||||
{
|
||||
let host_path = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
|
||||
let flags = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
|
||||
let inner = std::fs::OpenOptions::new()
|
||||
.read(flags & HostFile::READ != 0)
|
||||
.write(flags & HostFile::WRITE != 0)
|
||||
.append(flags & HostFile::APPEND != 0)
|
||||
.open(&host_path)
|
||||
.map_err(|_| serde::de::Error::custom("Could not open file on this system"))?;
|
||||
Ok(HostFile {
|
||||
inner,
|
||||
host_path,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for HostFile {
|
||||
fn deserialize<D>(deserializer: D) -> Result<HostFile, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
deserializer.deserialize_i32(HostFileVisitor)
|
||||
#[derive(Deserialize)]
|
||||
#[serde(field_identifier, rename_all = "snake_case")]
|
||||
enum Field {
|
||||
HostPath,
|
||||
Flags,
|
||||
}
|
||||
|
||||
struct HostFileVisitor;
|
||||
|
||||
impl<'de> de::Visitor<'de> for HostFileVisitor {
|
||||
type Value = HostFile;
|
||||
|
||||
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
formatter.write_str("struct HostFile")
|
||||
}
|
||||
|
||||
fn visit_seq<V>(self, mut seq: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: de::SeqAccess<'de>,
|
||||
{
|
||||
let host_path = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
|
||||
let flags = seq
|
||||
.next_element()?
|
||||
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
|
||||
let inner = std::fs::OpenOptions::new()
|
||||
.read(flags & HostFile::READ != 0)
|
||||
.write(flags & HostFile::WRITE != 0)
|
||||
.append(flags & HostFile::APPEND != 0)
|
||||
.open(&host_path)
|
||||
.map_err(|_| de::Error::custom("Could not open file on this system"))?;
|
||||
Ok(HostFile {
|
||||
inner,
|
||||
host_path,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
|
||||
where
|
||||
V: de::MapAccess<'de>,
|
||||
{
|
||||
let mut host_path = None;
|
||||
let mut flags = None;
|
||||
while let Some(key) = map.next_key()? {
|
||||
match key {
|
||||
Field::HostPath => {
|
||||
if host_path.is_some() {
|
||||
return Err(de::Error::duplicate_field("host_path"));
|
||||
}
|
||||
host_path = Some(map.next_value()?);
|
||||
}
|
||||
Field::Flags => {
|
||||
if flags.is_some() {
|
||||
return Err(de::Error::duplicate_field("flags"));
|
||||
}
|
||||
flags = Some(map.next_value()?);
|
||||
}
|
||||
}
|
||||
}
|
||||
let host_path = host_path.ok_or_else(|| de::Error::missing_field("host_path"))?;
|
||||
let flags = flags.ok_or_else(|| de::Error::missing_field("flags"))?;
|
||||
let inner = std::fs::OpenOptions::new()
|
||||
.read(flags & HostFile::READ != 0)
|
||||
.write(flags & HostFile::WRITE != 0)
|
||||
.append(flags & HostFile::APPEND != 0)
|
||||
.open(&host_path)
|
||||
.map_err(|_| de::Error::custom("Could not open file on this system"))?;
|
||||
Ok(HostFile {
|
||||
inner,
|
||||
host_path,
|
||||
flags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const FIELDS: &'static [&'static str] = &["host_path", "flags"];
|
||||
deserializer.deserialize_struct("HostFile", FIELDS, HostFileVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
// manually implement Deserialize here such that it uses actual data to open a file;
|
||||
// I guess we need to add r/w flags and stuff here too..
|
||||
|
||||
impl HostFile {
|
||||
const READ: u16 = 1;
|
||||
const WRITE: u16 = 2;
|
||||
|
Reference in New Issue
Block a user