You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

145 lines
4.5 KiB

fn main() -> anyhow::Result<()> {
component::generate_static_api_tests()?;
Ok(())
}
mod component {
use anyhow::{anyhow, Context, Error, Result};
use arbitrary::{Arbitrary, Unstructured};
use component_fuzz_util::{self, Declarations, TestCase};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use rand::rngs::StdRng;
use rand::{Rng, SeedableRng};
use std::env;
use std::fmt::Write;
use std::fs;
use std::iter;
use std::path::PathBuf;
use std::process::Command;
pub fn generate_static_api_tests() -> Result<()> {
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(
env::var_os("OUT_DIR").expect("The OUT_DIR environment variable must be set"),
);
let mut out = String::new();
write_static_api_tests(&mut out)?;
let output = out_dir.join("static_component_api.rs");
fs::write(&output, out)?;
drop(Command::new("rustfmt").arg(&output).status());
Ok(())
}
fn write_static_api_tests(out: &mut String) -> Result<()> {
let seed = if let Ok(seed) = env::var("WASMTIME_FUZZ_SEED") {
seed.parse::<u64>()
.with_context(|| anyhow!("expected u64 in WASMTIME_FUZZ_SEED"))?
} else {
StdRng::from_entropy().gen()
};
eprintln!(
"using seed {seed} (set WASMTIME_FUZZ_SEED={seed} in your environment to reproduce)"
);
let mut rng = StdRng::seed_from_u64(seed);
const TEST_CASE_COUNT: usize = 100;
let mut tests = TokenStream::new();
let name_counter = &mut 0;
let mut declarations = TokenStream::new();
for index in 0..TEST_CASE_COUNT {
let mut bytes = Vec::new();
let case = loop {
let count = rng.gen_range(1000..2000);
bytes.extend(iter::repeat_with(|| rng.gen::<u8>()).take(count));
match TestCase::arbitrary(&mut Unstructured::new(&bytes)) {
Ok(case) => break case,
Err(arbitrary::Error::NotEnoughData) => (),
Err(error) => return Err(Error::from(error)),
}
};
let Declarations {
types,
params,
result,
import_and_export,
} = case.declarations();
let test = format_ident!("static_api_test{}", case.params.len());
let rust_params = case
.params
.iter()
.map(|ty| {
let ty = component_fuzz_util::rust_type(&ty, name_counter, &mut declarations);
quote!(#ty,)
})
.collect::<TokenStream>();
let rust_result =
component_fuzz_util::rust_type(&case.result, name_counter, &mut declarations);
let test = quote!(#index => component_types::#test::<#rust_params #rust_result>(
input,
&Declarations {
types: #types.into(),
params: #params.into(),
result: #result.into(),
import_and_export: #import_and_export.into()
}
),);
tests.extend(test);
}
let module = quote! {
#[allow(unused_imports)]
fn static_component_api_target(input: &mut arbitrary::Unstructured) -> arbitrary::Result<()> {
use anyhow::Result;
use arbitrary::{Unstructured, Arbitrary};
use component_test_util::{self, Float32, Float64};
use component_fuzz_util::Declarations;
use std::sync::{Arc, Once};
use wasmtime::component::{ComponentType, Lift, Lower};
use wasmtime_fuzzing::generators::component_types;
const SEED: u64 = #seed;
static ONCE: Once = Once::new();
ONCE.call_once(|| {
eprintln!(
"Seed {SEED} was used to generate static component API fuzz tests.\n\
Set WASMTIME_FUZZ_SEED={SEED} in your environment at build time to reproduce."
);
});
#declarations
match input.int_in_range(0..=(#TEST_CASE_COUNT-1))? {
#tests
_ => unreachable!()
}
}
};
write!(out, "{module}")?;
Ok(())
}
}