Add WASI serializing test

This commit is contained in:
Mark McCaskey
2019-08-28 10:50:59 -07:00
parent e88a08f973
commit 634aca8b9d
11 changed files with 248 additions and 50 deletions

1
Cargo.lock generated
View File

@ -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",

View File

@ -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

View File

@ -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 }

View File

@ -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)) }
}

View 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"
);
}

View File

@ -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;

View File

@ -0,0 +1,3 @@
SCENE IV. The Queen's closet.
Enter QUEEN GERTRUDE and POLO

View 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"
}

Binary file not shown.

View File

@ -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),
)
};

View File

@ -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;