diff options
Diffstat (limited to 'bindgen-tests/tests/quickchecking/src/lib.rs')
-rw-r--r-- | bindgen-tests/tests/quickchecking/src/lib.rs | 133 |
1 files changed, 133 insertions, 0 deletions
diff --git a/bindgen-tests/tests/quickchecking/src/lib.rs b/bindgen-tests/tests/quickchecking/src/lib.rs new file mode 100644 index 00000000..b09d1c49 --- /dev/null +++ b/bindgen-tests/tests/quickchecking/src/lib.rs @@ -0,0 +1,133 @@ +//! A library to generate __fuzzed__ C headers for use with `quickcheck` +//! +//! ## Example +//! +//! ```rust +//! extern crate quickcheck; +//! extern crate quickchecking; +//! extern crate rand; +//! use quickcheck::{Arbitrary, Gen, StdGen}; +//! use quickchecking::fuzzers; +//! use rand::thread_rng; +//! +//! fn main() { +//! let generate_range: usize = 10; // Determines things like the length of +//! // arbitrary vectors generated. +//! let header = fuzzers::HeaderC::arbitrary( +//! &mut StdGen::new(thread_rng(), generate_range)); +//! println!("{}", header); +//! } +//! ``` +//! +#![deny(missing_docs)] +#[macro_use] +extern crate lazy_static; +extern crate quickcheck; +extern crate rand; +extern crate tempdir; + +use quickcheck::{QuickCheck, StdGen, TestResult}; +use rand::thread_rng; +use std::error::Error; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; +use std::process::{Command, Output}; +use std::sync::Mutex; +use tempdir::TempDir; + +/// Contains definitions of and impls for types used to fuzz C declarations. +pub mod fuzzers; + +// Global singleton, manages context across tests. For now that context is +// only the output_path for inspecting fuzzed headers (if specified). +struct Context { + output_path: Option<String>, +} + +// Initialize global context. +lazy_static! { + static ref CONTEXT: Mutex<Context> = + Mutex::new(Context { output_path: None }); +} + +// Passes fuzzed header to the `csmith-fuzzing/predicate.py` script, returns +// output of the associated command. +fn run_predicate_script( + header: fuzzers::HeaderC, +) -> Result<Output, Box<dyn Error>> { + let dir = TempDir::new("bindgen_prop")?; + let header_path = dir.path().join("prop_test.h"); + + let mut header_file = File::create(&header_path)?; + header_file.write_all(header.to_string().as_bytes())?; + header_file.sync_all()?; + + let header_path_string; + match header_path.into_os_string().into_string() { + Ok(s) => header_path_string = s, + Err(_) => return Err(From::from("error converting path into String")), + } + + let mut predicate_script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + predicate_script_path.push("../../csmith-fuzzing/predicate.py"); + + let predicate_script_path_string; + match predicate_script_path.into_os_string().into_string() { + Ok(s) => predicate_script_path_string = s, + Err(_) => return Err(From::from("error converting path into String")), + } + + // Copy generated temp files to output_path directory for inspection. + // If `None`, output path not specified, don't copy. + match CONTEXT.lock().unwrap().output_path { + Some(ref path) => { + Command::new("cp") + .arg("-a") + .arg(&dir.path().to_str().unwrap()) + .arg(&path) + .output()?; + } + None => {} + } + + Ok(Command::new(&predicate_script_path_string) + .arg(&header_path_string) + .output()?) +} + +// Generatable property. Pass generated headers off to run through the +// `csmith-fuzzing/predicate.py` script. Success is measured by the success +// status of that command. +fn bindgen_prop(header: fuzzers::HeaderC) -> TestResult { + match run_predicate_script(header) { + Ok(o) => return TestResult::from_bool(o.status.success()), + Err(e) => { + println!("{:?}", e); + return TestResult::from_bool(false); + } + } +} + +/// Instantiate a Quickcheck object and use it to run property tests using +/// fuzzed C headers generated with types defined in the `fuzzers` module. +/// Success/Failure is dictated by the result of passing the fuzzed headers +/// to the `csmith-fuzzing/predicate.py` script. +pub fn test_bindgen( + generate_range: usize, + tests: usize, + output_path: Option<&str>, +) { + match output_path { + Some(path) => { + CONTEXT.lock().unwrap().output_path = + Some(String::from(PathBuf::from(path).to_str().unwrap())); + } + None => {} // Path not specified, don't provide output. + } + + QuickCheck::new() + .tests(tests) + .gen(StdGen::new(thread_rng(), generate_range)) + .quickcheck(bindgen_prop as fn(fuzzers::HeaderC) -> TestResult) +} |