implement fuzzing for component types (#4537)
This addresses #4307. For the static API we generate 100 arbitrary test cases at build time, each of which includes 0-5 parameter types, a result type, and a WAT fragment containing an imported function and an exported function. The exported function calls the imported function, which is implemented by the host. At runtime, the fuzz test selects a test case at random and feeds it zero or more sets of arbitrary parameters and results, checking that values which flow host-to-guest and guest-to-host make the transition unchanged. The fuzz test for the dynamic API follows a similar pattern, the only difference being that test cases are generated at runtime. Signed-off-by: Joel Dice <joel.dice@fermyon.com>
This commit is contained in:
@@ -1073,3 +1073,60 @@ fn set_fuel<T>(store: &mut Store<T>, fuel: u64) {
|
||||
// double-check that the store has the expected amount of fuel remaining
|
||||
assert_eq!(store.consume_fuel(0).unwrap(), fuel);
|
||||
}
|
||||
|
||||
/// Generate and execute a `crate::generators::component_types::TestCase` using the specified `input` to create
|
||||
/// arbitrary types and values.
|
||||
pub fn dynamic_component_api_target(input: &mut arbitrary::Unstructured) -> arbitrary::Result<()> {
|
||||
use crate::generators::component_types;
|
||||
use anyhow::Result;
|
||||
use component_fuzz_util::{TestCase, EXPORT_FUNCTION, IMPORT_FUNCTION};
|
||||
use component_test_util::FuncExt;
|
||||
use wasmtime::component::{Component, Linker, Val};
|
||||
|
||||
crate::init_fuzzing();
|
||||
|
||||
let case = input.arbitrary::<TestCase>()?;
|
||||
|
||||
let engine = component_test_util::engine();
|
||||
let mut store = Store::new(&engine, (Box::new([]) as Box<[Val]>, None));
|
||||
let component =
|
||||
Component::new(&engine, case.declarations().make_component().as_bytes()).unwrap();
|
||||
let mut linker = Linker::new(&engine);
|
||||
|
||||
linker
|
||||
.root()
|
||||
.func_new(&component, IMPORT_FUNCTION, {
|
||||
move |cx: StoreContextMut<'_, (Box<[Val]>, Option<Val>)>, args: &[Val]| -> Result<Val> {
|
||||
let (expected_args, result) = cx.data();
|
||||
assert_eq!(args.len(), expected_args.len());
|
||||
for (expected, actual) in expected_args.iter().zip(args) {
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
Ok(result.as_ref().unwrap().clone())
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let instance = linker.instantiate(&mut store, &component).unwrap();
|
||||
let func = instance.get_func(&mut store, EXPORT_FUNCTION).unwrap();
|
||||
let params = func.params(&store);
|
||||
let result = func.result(&store);
|
||||
|
||||
while input.arbitrary()? {
|
||||
let args = params
|
||||
.iter()
|
||||
.map(|ty| component_types::arbitrary_val(ty, input))
|
||||
.collect::<arbitrary::Result<Box<[_]>>>()?;
|
||||
|
||||
let result = component_types::arbitrary_val(&result, input)?;
|
||||
|
||||
*store.data_mut() = (args.clone(), Some(result.clone()));
|
||||
|
||||
assert_eq!(
|
||||
func.call_and_post_return(&mut store, &args).unwrap(),
|
||||
result
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user