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:
Joel Dice
2022-08-04 11:02:55 -06:00
committed by GitHub
parent ad223c5234
commit ed8908efcf
29 changed files with 1963 additions and 266 deletions

View File

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