use crate::config::Config; use crate::function_generator::FunctionGenerator; use anyhow::Result; use arbitrary::{Arbitrary, Unstructured}; use cranelift::codegen::data_value::DataValue; use cranelift::codegen::ir::types::*; use cranelift::codegen::ir::Function; use cranelift::prelude::*; mod config; mod function_generator; pub type TestCaseInput = Vec; #[derive(Debug)] pub struct TestCase { pub func: Function, /// Generate multiple test inputs for each test case. /// This allows us to get more coverage per compilation, which may be somewhat expensive. pub inputs: Vec, } impl<'a> Arbitrary<'a> for TestCase { fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { FuzzGen::new(u) .generate_test() .map_err(|_| arbitrary::Error::IncorrectFormat) } } pub struct FuzzGen<'r, 'data> where 'data: 'r, { u: &'r mut Unstructured<'data>, config: Config, } impl<'r, 'data> FuzzGen<'r, 'data> where 'data: 'r, { pub fn new(u: &'r mut Unstructured<'data>) -> Self { Self { u, config: Config::default(), } } fn generate_test_inputs(&mut self, signature: &Signature) -> Result> { let num_tests = self.u.int_in_range(self.config.test_case_inputs.clone())?; let mut inputs = Vec::with_capacity(num_tests); for _ in 0..num_tests { let test_args = signature .params .iter() .map(|p| { let imm = match p.value_type { I8 => self.u.arbitrary::()? as i128, I16 => self.u.arbitrary::()? as i128, I32 => self.u.arbitrary::()? as i128, I64 => self.u.arbitrary::()? as i128, _ => unreachable!(), }; Ok(DataValue::from_integer(imm, p.value_type)?) }) .collect::>()?; inputs.push(test_args); } Ok(inputs) } pub fn generate_test(mut self) -> Result { let func = FunctionGenerator::new(&mut self.u, &self.config).generate()?; let inputs = self.generate_test_inputs(&func.signature)?; Ok(TestCase { func, inputs }) } }