From c981808952bf1305d08828eb4ce8c8f7d15ba7c2 Mon Sep 17 00:00:00 2001 From: Shea Newton Date: Sun, 12 Nov 2017 15:24:21 -0800 Subject: [PATCH 1/2] This PR represents an attempt to address issue #970. It also represents a portion of the meta issue for fuzzing #972. The code base reflected here uses quickcheck to generate C headers that include a variety of types including basic types, structs, unions, function prototypes and function pointers. The headers generated by quickcheck are passed to the `csmith-fuzzing/predicate.py` script. Examples of headers generated by this iteration of the tooling can be viewed [here](https://gist.github.com/snewt/03ce934f35c5b085807d2d5cf11d1d5c). At the top of each header are two simple struct definitions, `whitelistable` and `blacklistable`. Those types are present in the vector that represents otherwise primitive types used to generate. They represent a naive approach to exposing custom types without having to intuit generated type names like `struct_21_8` though _any actual whitelisting logic isn't implemented here_. Test success is measured by the success of the `csmith-fuzzing/predicate.py` script. This means that for a test to pass the following must be true: - bindgen doesn't panic - the resulting bindings compile - the resulting bindings layout tests pass ```bash cd tests/property_test cargo test ``` Some things I'm unsure of: At the moment it lives in `tests/property_test` but isn't run when `cargo test` is invoked from bindgen's cargo manifest directory. At this point, the source is genereated in ~1 second but the files are large enough that it takes the `predicate.py` script ~30 seconds to run through each one. In order for the tests to run in under a minute only 2 are generated by quickcheck by default. This can be changed in the `test_bindgen` function of the `tests/property_test/tests/fuzzed-c-headers.rs` file. For now the `run_predicate_script` function in the `tests/property_test/tests/fuzzed-c-headers.rs` file contains a commented block that will copy generated source in the `tests/property_test/tests` directory. Should it be easier? There is some logic in the fuzzer that disallows 0 sized arrays because tests will regulary fail due to issues documented in #684 and #1153. Should this be special casing? After any iterations the reviewers are interested in required to make this a functional testing tool, should/could the fuzzing library be made into its own crate? I didn't move in that direction yet because having it all in one place seemed like the best way to figure out what works an doesn't but I'm interested in whether it might be useful as a standalone library. I'm looking forward to feedback on how to make this a more useful tool and one that provides the right configurability. Thanks! r? @fitzgen --- tests/property_test/Cargo.toml | 10 + tests/property_test/src/fuzzers.rs | 468 ++++++++++++++++++ tests/property_test/src/lib.rs | 5 + tests/property_test/tests/fuzzed-c-headers.rs | 81 +++ 4 files changed, 564 insertions(+) create mode 100644 tests/property_test/Cargo.toml create mode 100644 tests/property_test/src/fuzzers.rs create mode 100644 tests/property_test/src/lib.rs create mode 100644 tests/property_test/tests/fuzzed-c-headers.rs diff --git a/tests/property_test/Cargo.toml b/tests/property_test/Cargo.toml new file mode 100644 index 0000000000..fa644b4bb5 --- /dev/null +++ b/tests/property_test/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "property_test" +description = "Bindgen property tests. Generate random valid C code and pass it to the csmith/predicate.py script" +version = "0.1.0" +authors = ["Shea Newton "] + +[dependencies] +quickcheck = "0.4" +tempdir = "0.3" +rand = "0.3" diff --git a/tests/property_test/src/fuzzers.rs b/tests/property_test/src/fuzzers.rs new file mode 100644 index 0000000000..113c9f33c4 --- /dev/null +++ b/tests/property_test/src/fuzzers.rs @@ -0,0 +1,468 @@ +use quickcheck::{Arbitrary, Gen, StdGen}; +use std::fmt; +use rand::thread_rng; + +#[derive(PartialEq, Debug, Clone)] +pub struct BaseTypeC { + pub def: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct TypeQualifierC { + pub def: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct PointerLevelC { + pub def: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct ArrayDimensionC { + pub def: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct BasicTypeDeclarationC { + pub type_name: String, + pub type_qualifier: String, + pub pointer_level: String, + pub ident_id: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct StructDeclarationC { + pub fields: String, + pub ident_id: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct UnionDeclarationC { + pub fields: String, + pub ident_id: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct FunctionPointerDeclarationC { + pub type_qualifier: String, + pub type_name: String, + pub pointer_level: String, + pub params: String, + pub ident_id: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct FunctionPrototypeC { + pub type_qualifier: String, + pub type_name: String, + pub pointer_level: String, + pub params: String, + pub ident_id: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct ParameterC { + pub type_qualifier: String, + pub type_name: String, + pub pointer_level: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct ParameterListC { + def: String, +} + +#[derive(PartialEq, Debug, Clone)] +pub struct HeaderC { + pub def: String, +} + +#[derive(PartialEq, Debug, Clone)] +enum DeclarationC { + FunctionDecl(FunctionPrototypeC), + FunctionPtrDecl(FunctionPointerDeclarationC), + StructDecl(StructDeclarationC), + UnionDecl(UnionDeclarationC), + VariableDecl(BasicTypeDeclarationC), +} + +trait MakeUnique { + fn make_unique(&mut self, stamp: usize); +} + +impl MakeUnique for DeclarationC { + fn make_unique(&mut self, stamp: usize) { + match self { + &mut DeclarationC::FunctionDecl(ref mut d) => d.make_unique(stamp), + &mut DeclarationC::FunctionPtrDecl(ref mut d) => d.make_unique(stamp), + &mut DeclarationC::StructDecl(ref mut d) => d.make_unique(stamp), + &mut DeclarationC::UnionDecl(ref mut d) => d.make_unique(stamp), + &mut DeclarationC::VariableDecl(ref mut d) => d.make_unique(stamp), + } + } +} + +impl Arbitrary for DeclarationC { + fn arbitrary(g: &mut G) -> DeclarationC { + match usize::arbitrary(g) % 5 { + 0 => DeclarationC::FunctionDecl(FunctionPrototypeC::arbitrary(g)), + 1 => DeclarationC::FunctionPtrDecl(FunctionPointerDeclarationC::arbitrary(g)), + 2 => DeclarationC::StructDecl(StructDeclarationC::arbitrary(g)), + 3 => DeclarationC::UnionDecl(UnionDeclarationC::arbitrary(g)), + _ => DeclarationC::VariableDecl(BasicTypeDeclarationC::arbitrary(g)), + } + } +} + +impl fmt::Display for DeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + &DeclarationC::FunctionPtrDecl(ref d) => write!(f, "{}", d), + &DeclarationC::StructDecl(ref d) => write!(f, "{}", d), + &DeclarationC::UnionDecl(ref d) => write!(f, "{}", d), + &DeclarationC::VariableDecl(ref d) => write!(f, "{}", d), + &DeclarationC::FunctionDecl(ref d) => write!(f, "{}", d), + } + } +} + +impl Arbitrary for BaseTypeC { + fn arbitrary(g: &mut G) -> BaseTypeC { + let base_type = vec![ + "char", + "signed char", + "unsigned char", + "short", + "short int", + "signed short", + "signed short int", + "unsigned short", + "unsigned short int", + "int", + "signed", + "signed int", + "unsigned", + "unsigned int", + "long", + "long int", + "signed long", + "signed long int", + "unsigned long", + "unsigned long int", + "long long", + "long long int", + "signed long long", + "signed long long int", + "unsigned long long", + "unsigned long long int", + "float", + "double", + "long double", + "void*", + "whitelistable", + "blacklistable", + ]; + match base_type.iter().nth(usize::arbitrary(g) % base_type.len()) { + Some(s) => BaseTypeC { + def: String::from(*s), + }, + None => BaseTypeC { + def: String::from("int"), + }, + } + } +} + +impl fmt::Display for BaseTypeC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +impl Arbitrary for TypeQualifierC { + fn arbitrary(g: &mut G) -> TypeQualifierC { + let qualifier = vec!["const", ""]; + match qualifier.iter().nth(usize::arbitrary(g) % qualifier.len()) { + Some(s) => TypeQualifierC { + def: String::from(*s), + }, + None => TypeQualifierC { + def: String::from(""), + }, + } + } +} + +impl fmt::Display for TypeQualifierC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +impl Arbitrary for PointerLevelC { + fn arbitrary(g: &mut G) -> PointerLevelC { + PointerLevelC { + def: (0..usize::arbitrary(g)).map(|_| "*").collect::(), + } + } +} + +impl fmt::Display for PointerLevelC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +impl Arbitrary for ArrayDimensionC { + fn arbitrary(g: &mut G) -> ArrayDimensionC { + // keep these small, they clang complains when they get too big + let dimensions = usize::arbitrary(g) % 5; + let mut def = String::new(); + // don't allow size 0 dimension until #684 and #1153 are closed + for _ in 1..dimensions { + def += &format!("[{}]", (usize::arbitrary(g) % 15) + 1); + } + ArrayDimensionC { def: def } + } +} + +impl fmt::Display for ArrayDimensionC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +impl MakeUnique for BasicTypeDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +impl Arbitrary for BasicTypeDeclarationC { + fn arbitrary(g: &mut G) -> BasicTypeDeclarationC { + BasicTypeDeclarationC { + type_qualifier: TypeQualifierC::arbitrary(g).def, + type_name: BaseTypeC::arbitrary(g).def, + pointer_level: PointerLevelC::arbitrary(g).def, + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +impl fmt::Display for BasicTypeDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {} ident_{};", + self.type_qualifier, + self.type_name, + self.pointer_level, + self.ident_id + ) + } +} + +impl MakeUnique for StructDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +impl Arbitrary for StructDeclarationC { + fn arbitrary(g: &mut G) -> StructDeclarationC { + let mut fields_string = String::new(); + // reduce generator size as a method of putting a bound on recursion. + // when size < 1 the empty list is generated. + let reduced_size: usize = (g.size() / 2) as usize + 1; + let mut decls: Vec = + Arbitrary::arbitrary(&mut StdGen::new(thread_rng(), reduced_size)); + + for (i, decl) in decls.iter_mut().enumerate() { + match decl { + &mut DeclarationC::FunctionDecl(_) => {} + decl => { + decl.make_unique(i); + fields_string += &format!("{}", decl); + } + } + } + + StructDeclarationC { + fields: fields_string, + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +impl fmt::Display for StructDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "struct {{ {} }} struct_{};", self.fields, self.ident_id) + } +} + +impl MakeUnique for UnionDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +impl Arbitrary for UnionDeclarationC { + fn arbitrary(g: &mut G) -> UnionDeclarationC { + let mut fields_string = String::new(); + // reduce generator size as a method of putting a bound on recursion. + // when size < 1 the empty list is generated. + let reduced_size: usize = (g.size() / 2) as usize + 1; + let mut decls: Vec = + Arbitrary::arbitrary(&mut StdGen::new(thread_rng(), reduced_size)); + + for (i, decl) in decls.iter_mut().enumerate() { + match decl { + &mut DeclarationC::FunctionDecl(_) => {} + decl => { + decl.make_unique(i); + fields_string += &format!("{}", decl); + } + } + } + + UnionDeclarationC { + fields: fields_string, + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +impl fmt::Display for UnionDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "union {{ {} }} union_{};", self.fields, self.ident_id) + } +} + +impl MakeUnique for FunctionPointerDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +impl Arbitrary for FunctionPointerDeclarationC { + fn arbitrary(g: &mut G) -> FunctionPointerDeclarationC { + FunctionPointerDeclarationC { + type_qualifier: format!("{}", TypeQualifierC::arbitrary(g)), + type_name: format!("{}", BaseTypeC::arbitrary(g)), + pointer_level: format!("{}", PointerLevelC::arbitrary(g)), + params: format!("{}", ParameterListC::arbitrary(g)), + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +impl fmt::Display for FunctionPointerDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {} (*func_ptr_{})({});", + self.type_qualifier, + self.type_name, + self.pointer_level, + self.ident_id, + self.params + ) + } +} + +impl MakeUnique for FunctionPrototypeC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +impl Arbitrary for FunctionPrototypeC { + fn arbitrary(g: &mut G) -> FunctionPrototypeC { + FunctionPrototypeC { + type_qualifier: format!("{}", TypeQualifierC::arbitrary(g)), + type_name: format!("{}", BaseTypeC::arbitrary(g)), + pointer_level: format!("{}", PointerLevelC::arbitrary(g)), + params: format!("{}", ParameterListC::arbitrary(g)), + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +impl fmt::Display for FunctionPrototypeC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {} func_{}({});", + self.type_qualifier, + self.type_name, + self.pointer_level, + self.ident_id, + self.params + ) + } +} + +impl Arbitrary for ParameterC { + fn arbitrary(g: &mut G) -> ParameterC { + ParameterC { + type_qualifier: format!("{}", TypeQualifierC::arbitrary(g)), + type_name: format!("{}", BaseTypeC::arbitrary(g)), + pointer_level: format!("{}", PointerLevelC::arbitrary(g)), + } + } +} + +impl fmt::Display for ParameterC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {}", + self.type_qualifier, + self.type_name, + self.pointer_level + ) + } +} + +impl Arbitrary for ParameterListC { + fn arbitrary(g: &mut G) -> ParameterListC { + let mut params_string = String::new(); + let params: Vec = Arbitrary::arbitrary(g); + for (i, p) in params.iter().enumerate() { + match i { + 0 => params_string += &format!("{}", p), + _ => params_string += &format!(",{}", p), + } + } + + ParameterListC { def: params_string } + } +} + +impl fmt::Display for ParameterListC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +impl Arbitrary for HeaderC { + fn arbitrary(g: &mut G) -> HeaderC { + let known_types = "typedef struct { short s; } whitelistable; \ + typedef struct { float f;} blacklistable;"; + let mut header_c = String::from(known_types); + let mut decls: Vec = Arbitrary::arbitrary(g); + + for (i, decl) in decls.iter_mut().enumerate() { + decl.make_unique(i); + header_c += &format!("{}", decl); + } + + HeaderC { def: header_c } + } +} + +impl fmt::Display for HeaderC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} diff --git a/tests/property_test/src/lib.rs b/tests/property_test/src/lib.rs new file mode 100644 index 0000000000..c9bc9053bb --- /dev/null +++ b/tests/property_test/src/lib.rs @@ -0,0 +1,5 @@ +extern crate quickcheck; +extern crate rand; +extern crate tempdir; + +pub mod fuzzers; diff --git a/tests/property_test/tests/fuzzed-c-headers.rs b/tests/property_test/tests/fuzzed-c-headers.rs new file mode 100644 index 0000000000..b132a7591f --- /dev/null +++ b/tests/property_test/tests/fuzzed-c-headers.rs @@ -0,0 +1,81 @@ +extern crate property_test; +extern crate quickcheck; +extern crate rand; +extern crate tempdir; + +use property_test::fuzzers; +use quickcheck::{QuickCheck, StdGen, TestResult}; +use std::fs::File; +use std::io::Write; +use tempdir::TempDir; +use std::process::{Command, Output}; +use std::path::PathBuf; +use std::error::Error; +use rand::thread_rng; + +fn run_predicate_script(header: fuzzers::HeaderC, header_name: &str) -> Result> { + let dir = TempDir::new("bindgen_prop")?; + let header_path = dir.path().join(header_name); + + let mut header_file = File::create(&header_path)?; + header_file.write_all(header.def.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 test directory for inspection. + // Preserved for anyone interested in validating the behavior. + + // let mut debug_output_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // debug_output_path.push("tests"); + // Command::new("cp") + // .arg("-a") + // .arg(&dir.path().to_str().unwrap()) + // .arg(&debug_output_path.to_str().unwrap()) + // .output()?; + + Ok(Command::new(&predicate_script_path_string) + .arg(&header_path_string) + .output()?) + + // omit close, from tempdir crate's docs: + // "Closing the directory is actually optional, as it would be done on drop." +} + +fn bindgen_prop(header: fuzzers::HeaderC) -> TestResult { + match run_predicate_script(header, "prop_test.h") { + Ok(o) => return TestResult::from_bool(o.status.success()), + Err(e) => { + println!("{:?}", e); + return TestResult::from_bool(false); + } + } +} + +#[test] +fn test_bindgen() { + // enough to generate any value in the PrimitiveTypeC `base_type` list + let generate_range: usize = 32; + QuickCheck::new() + // generating is relatively quick (generate_range 150 takes ~5 seconds) + // but running predicate.py takes ~30 seconds per source file / test + // when the generation range is just 32. It can take a lot longer with a + // higher generate_range. Up the number of tests or generate_range if + // you're willing to wait awhile. + .tests(2) + .gen(StdGen::new(thread_rng(), generate_range)) + .quickcheck(bindgen_prop as fn(fuzzers::HeaderC) -> TestResult) +} From 2aa9b1d2344ce2007b5bba2a4c9141c837f99676 Mon Sep 17 00:00:00 2001 From: Shea Newton Date: Tue, 28 Nov 2017 19:30:38 -0800 Subject: [PATCH 2/2] Address requested changes to quickchecking crate. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove `whitelistable` and `blacklistable` types. - Rename test crate directory from `property_test` to `quickchecking`. - Add new CI job that checks that this crate continues to build. - Revise matching logic to be more idomatic. - Phase out modular arithmetic in favor of `gen_range`. - Incorporate `unreachable!` into match statements. - Revise logic for accessing random element of vector, favor `choose` over `nth`. - Proper punctuation and capitalization in comments. - Using actual structures rather than converting everything to strings in order to leverage type system. - Add `#![deny(missing_docs)]` and filled in documentation required for the project to build again. - Add special case logic so we don't generate structs with `long double` fields as it will cause tests to fail unitl issue \#550 is resolved Note on making sure we don't lose test cases we're interested in preserving: We're copying the directories `TempDir` makes so we get things like this: ``` ├── bindgen_prop.1WYe3F5HZU1c │   └── prop_test.h ├── bindgen_prop.H4SLI1JX0jd8 │   └── prop_test.h ``` I'm not sure that `TempDir` makes any claims about uniqueness, so collisions probably aren't impossible. I'm up for any suggestions on a more bulletproof solution. _Tasks not addressed by this PR:_ * TODO: Add `cargo features` logic to allow generating problematic code. * TODO: Make a [bin] target with CLI to manage test settings. * TODO: Whitelisting and opaque types. * TODO: Generate bitfields, C++, I-bogus-codegen cases. Figured this would be a good point to update the PR but if any of the above TODO items should be incorporated before moving forward I'm up for it! Thanks for taking another look! r? @fitzgen --- .travis.yml | 1 + ci/script.sh | 5 + tests/property_test/Cargo.toml | 10 - tests/property_test/src/fuzzers.rs | 468 -------------- tests/property_test/src/lib.rs | 5 - tests/quickchecking/Cargo.toml | 10 + tests/quickchecking/src/fuzzers.rs | 599 ++++++++++++++++++ tests/quickchecking/src/lib.rs | 28 + .../tests/fuzzed-c-headers.rs | 27 +- 9 files changed, 655 insertions(+), 498 deletions(-) delete mode 100644 tests/property_test/Cargo.toml delete mode 100644 tests/property_test/src/fuzzers.rs delete mode 100644 tests/property_test/src/lib.rs create mode 100644 tests/quickchecking/Cargo.toml create mode 100644 tests/quickchecking/src/fuzzers.rs create mode 100644 tests/quickchecking/src/lib.rs rename tests/{property_test => quickchecking}/tests/fuzzed-c-headers.rs (77%) diff --git a/.travis.yml b/.travis.yml index e5d0e105d2..2f9cfd63f8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ env: - LLVM_VERSION="4.0.0" BINDGEN_JOB="expectations" BINDGEN_PROFILE= - LLVM_VERSION="4.0.0" BINDGEN_JOB="expectations" BINDGEN_PROFILE="--release" - LLVM_VERSION="4.0.0" BINDGEN_JOB="misc" + - LLVM_VERSION="4.0.0" BINDGEN_JOB="quickchecking" matrix: fast_finish: true diff --git a/ci/script.sh b/ci/script.sh index 7338dc0bd9..b9e7589266 100755 --- a/ci/script.sh +++ b/ci/script.sh @@ -38,6 +38,11 @@ case "$BINDGEN_JOB" in # ./ci/assert-rustfmt.sh ;; + "quickchecking") + cd ./tests/quickchecking + # TODO: Actually run quickchecks once `bindgen` is reliable enough. + cargo check + ;; *) echo "Error! Unknown \$BINDGEN_JOB: '$BINDGEN_JOB'" exit 1 diff --git a/tests/property_test/Cargo.toml b/tests/property_test/Cargo.toml deleted file mode 100644 index fa644b4bb5..0000000000 --- a/tests/property_test/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "property_test" -description = "Bindgen property tests. Generate random valid C code and pass it to the csmith/predicate.py script" -version = "0.1.0" -authors = ["Shea Newton "] - -[dependencies] -quickcheck = "0.4" -tempdir = "0.3" -rand = "0.3" diff --git a/tests/property_test/src/fuzzers.rs b/tests/property_test/src/fuzzers.rs deleted file mode 100644 index 113c9f33c4..0000000000 --- a/tests/property_test/src/fuzzers.rs +++ /dev/null @@ -1,468 +0,0 @@ -use quickcheck::{Arbitrary, Gen, StdGen}; -use std::fmt; -use rand::thread_rng; - -#[derive(PartialEq, Debug, Clone)] -pub struct BaseTypeC { - pub def: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct TypeQualifierC { - pub def: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct PointerLevelC { - pub def: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct ArrayDimensionC { - pub def: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct BasicTypeDeclarationC { - pub type_name: String, - pub type_qualifier: String, - pub pointer_level: String, - pub ident_id: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct StructDeclarationC { - pub fields: String, - pub ident_id: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct UnionDeclarationC { - pub fields: String, - pub ident_id: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct FunctionPointerDeclarationC { - pub type_qualifier: String, - pub type_name: String, - pub pointer_level: String, - pub params: String, - pub ident_id: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct FunctionPrototypeC { - pub type_qualifier: String, - pub type_name: String, - pub pointer_level: String, - pub params: String, - pub ident_id: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct ParameterC { - pub type_qualifier: String, - pub type_name: String, - pub pointer_level: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct ParameterListC { - def: String, -} - -#[derive(PartialEq, Debug, Clone)] -pub struct HeaderC { - pub def: String, -} - -#[derive(PartialEq, Debug, Clone)] -enum DeclarationC { - FunctionDecl(FunctionPrototypeC), - FunctionPtrDecl(FunctionPointerDeclarationC), - StructDecl(StructDeclarationC), - UnionDecl(UnionDeclarationC), - VariableDecl(BasicTypeDeclarationC), -} - -trait MakeUnique { - fn make_unique(&mut self, stamp: usize); -} - -impl MakeUnique for DeclarationC { - fn make_unique(&mut self, stamp: usize) { - match self { - &mut DeclarationC::FunctionDecl(ref mut d) => d.make_unique(stamp), - &mut DeclarationC::FunctionPtrDecl(ref mut d) => d.make_unique(stamp), - &mut DeclarationC::StructDecl(ref mut d) => d.make_unique(stamp), - &mut DeclarationC::UnionDecl(ref mut d) => d.make_unique(stamp), - &mut DeclarationC::VariableDecl(ref mut d) => d.make_unique(stamp), - } - } -} - -impl Arbitrary for DeclarationC { - fn arbitrary(g: &mut G) -> DeclarationC { - match usize::arbitrary(g) % 5 { - 0 => DeclarationC::FunctionDecl(FunctionPrototypeC::arbitrary(g)), - 1 => DeclarationC::FunctionPtrDecl(FunctionPointerDeclarationC::arbitrary(g)), - 2 => DeclarationC::StructDecl(StructDeclarationC::arbitrary(g)), - 3 => DeclarationC::UnionDecl(UnionDeclarationC::arbitrary(g)), - _ => DeclarationC::VariableDecl(BasicTypeDeclarationC::arbitrary(g)), - } - } -} - -impl fmt::Display for DeclarationC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - &DeclarationC::FunctionPtrDecl(ref d) => write!(f, "{}", d), - &DeclarationC::StructDecl(ref d) => write!(f, "{}", d), - &DeclarationC::UnionDecl(ref d) => write!(f, "{}", d), - &DeclarationC::VariableDecl(ref d) => write!(f, "{}", d), - &DeclarationC::FunctionDecl(ref d) => write!(f, "{}", d), - } - } -} - -impl Arbitrary for BaseTypeC { - fn arbitrary(g: &mut G) -> BaseTypeC { - let base_type = vec![ - "char", - "signed char", - "unsigned char", - "short", - "short int", - "signed short", - "signed short int", - "unsigned short", - "unsigned short int", - "int", - "signed", - "signed int", - "unsigned", - "unsigned int", - "long", - "long int", - "signed long", - "signed long int", - "unsigned long", - "unsigned long int", - "long long", - "long long int", - "signed long long", - "signed long long int", - "unsigned long long", - "unsigned long long int", - "float", - "double", - "long double", - "void*", - "whitelistable", - "blacklistable", - ]; - match base_type.iter().nth(usize::arbitrary(g) % base_type.len()) { - Some(s) => BaseTypeC { - def: String::from(*s), - }, - None => BaseTypeC { - def: String::from("int"), - }, - } - } -} - -impl fmt::Display for BaseTypeC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.def) - } -} - -impl Arbitrary for TypeQualifierC { - fn arbitrary(g: &mut G) -> TypeQualifierC { - let qualifier = vec!["const", ""]; - match qualifier.iter().nth(usize::arbitrary(g) % qualifier.len()) { - Some(s) => TypeQualifierC { - def: String::from(*s), - }, - None => TypeQualifierC { - def: String::from(""), - }, - } - } -} - -impl fmt::Display for TypeQualifierC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.def) - } -} - -impl Arbitrary for PointerLevelC { - fn arbitrary(g: &mut G) -> PointerLevelC { - PointerLevelC { - def: (0..usize::arbitrary(g)).map(|_| "*").collect::(), - } - } -} - -impl fmt::Display for PointerLevelC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.def) - } -} - -impl Arbitrary for ArrayDimensionC { - fn arbitrary(g: &mut G) -> ArrayDimensionC { - // keep these small, they clang complains when they get too big - let dimensions = usize::arbitrary(g) % 5; - let mut def = String::new(); - // don't allow size 0 dimension until #684 and #1153 are closed - for _ in 1..dimensions { - def += &format!("[{}]", (usize::arbitrary(g) % 15) + 1); - } - ArrayDimensionC { def: def } - } -} - -impl fmt::Display for ArrayDimensionC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.def) - } -} - -impl MakeUnique for BasicTypeDeclarationC { - fn make_unique(&mut self, stamp: usize) { - self.ident_id += &format!("_{}", stamp); - } -} - -impl Arbitrary for BasicTypeDeclarationC { - fn arbitrary(g: &mut G) -> BasicTypeDeclarationC { - BasicTypeDeclarationC { - type_qualifier: TypeQualifierC::arbitrary(g).def, - type_name: BaseTypeC::arbitrary(g).def, - pointer_level: PointerLevelC::arbitrary(g).def, - ident_id: format!("{}", usize::arbitrary(g)), - } - } -} - -impl fmt::Display for BasicTypeDeclarationC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{} {} {} ident_{};", - self.type_qualifier, - self.type_name, - self.pointer_level, - self.ident_id - ) - } -} - -impl MakeUnique for StructDeclarationC { - fn make_unique(&mut self, stamp: usize) { - self.ident_id += &format!("_{}", stamp); - } -} - -impl Arbitrary for StructDeclarationC { - fn arbitrary(g: &mut G) -> StructDeclarationC { - let mut fields_string = String::new(); - // reduce generator size as a method of putting a bound on recursion. - // when size < 1 the empty list is generated. - let reduced_size: usize = (g.size() / 2) as usize + 1; - let mut decls: Vec = - Arbitrary::arbitrary(&mut StdGen::new(thread_rng(), reduced_size)); - - for (i, decl) in decls.iter_mut().enumerate() { - match decl { - &mut DeclarationC::FunctionDecl(_) => {} - decl => { - decl.make_unique(i); - fields_string += &format!("{}", decl); - } - } - } - - StructDeclarationC { - fields: fields_string, - ident_id: format!("{}", usize::arbitrary(g)), - } - } -} - -impl fmt::Display for StructDeclarationC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "struct {{ {} }} struct_{};", self.fields, self.ident_id) - } -} - -impl MakeUnique for UnionDeclarationC { - fn make_unique(&mut self, stamp: usize) { - self.ident_id += &format!("_{}", stamp); - } -} - -impl Arbitrary for UnionDeclarationC { - fn arbitrary(g: &mut G) -> UnionDeclarationC { - let mut fields_string = String::new(); - // reduce generator size as a method of putting a bound on recursion. - // when size < 1 the empty list is generated. - let reduced_size: usize = (g.size() / 2) as usize + 1; - let mut decls: Vec = - Arbitrary::arbitrary(&mut StdGen::new(thread_rng(), reduced_size)); - - for (i, decl) in decls.iter_mut().enumerate() { - match decl { - &mut DeclarationC::FunctionDecl(_) => {} - decl => { - decl.make_unique(i); - fields_string += &format!("{}", decl); - } - } - } - - UnionDeclarationC { - fields: fields_string, - ident_id: format!("{}", usize::arbitrary(g)), - } - } -} - -impl fmt::Display for UnionDeclarationC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "union {{ {} }} union_{};", self.fields, self.ident_id) - } -} - -impl MakeUnique for FunctionPointerDeclarationC { - fn make_unique(&mut self, stamp: usize) { - self.ident_id += &format!("_{}", stamp); - } -} - -impl Arbitrary for FunctionPointerDeclarationC { - fn arbitrary(g: &mut G) -> FunctionPointerDeclarationC { - FunctionPointerDeclarationC { - type_qualifier: format!("{}", TypeQualifierC::arbitrary(g)), - type_name: format!("{}", BaseTypeC::arbitrary(g)), - pointer_level: format!("{}", PointerLevelC::arbitrary(g)), - params: format!("{}", ParameterListC::arbitrary(g)), - ident_id: format!("{}", usize::arbitrary(g)), - } - } -} - -impl fmt::Display for FunctionPointerDeclarationC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{} {} {} (*func_ptr_{})({});", - self.type_qualifier, - self.type_name, - self.pointer_level, - self.ident_id, - self.params - ) - } -} - -impl MakeUnique for FunctionPrototypeC { - fn make_unique(&mut self, stamp: usize) { - self.ident_id += &format!("_{}", stamp); - } -} - -impl Arbitrary for FunctionPrototypeC { - fn arbitrary(g: &mut G) -> FunctionPrototypeC { - FunctionPrototypeC { - type_qualifier: format!("{}", TypeQualifierC::arbitrary(g)), - type_name: format!("{}", BaseTypeC::arbitrary(g)), - pointer_level: format!("{}", PointerLevelC::arbitrary(g)), - params: format!("{}", ParameterListC::arbitrary(g)), - ident_id: format!("{}", usize::arbitrary(g)), - } - } -} - -impl fmt::Display for FunctionPrototypeC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{} {} {} func_{}({});", - self.type_qualifier, - self.type_name, - self.pointer_level, - self.ident_id, - self.params - ) - } -} - -impl Arbitrary for ParameterC { - fn arbitrary(g: &mut G) -> ParameterC { - ParameterC { - type_qualifier: format!("{}", TypeQualifierC::arbitrary(g)), - type_name: format!("{}", BaseTypeC::arbitrary(g)), - pointer_level: format!("{}", PointerLevelC::arbitrary(g)), - } - } -} - -impl fmt::Display for ParameterC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{} {} {}", - self.type_qualifier, - self.type_name, - self.pointer_level - ) - } -} - -impl Arbitrary for ParameterListC { - fn arbitrary(g: &mut G) -> ParameterListC { - let mut params_string = String::new(); - let params: Vec = Arbitrary::arbitrary(g); - for (i, p) in params.iter().enumerate() { - match i { - 0 => params_string += &format!("{}", p), - _ => params_string += &format!(",{}", p), - } - } - - ParameterListC { def: params_string } - } -} - -impl fmt::Display for ParameterListC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.def) - } -} - -impl Arbitrary for HeaderC { - fn arbitrary(g: &mut G) -> HeaderC { - let known_types = "typedef struct { short s; } whitelistable; \ - typedef struct { float f;} blacklistable;"; - let mut header_c = String::from(known_types); - let mut decls: Vec = Arbitrary::arbitrary(g); - - for (i, decl) in decls.iter_mut().enumerate() { - decl.make_unique(i); - header_c += &format!("{}", decl); - } - - HeaderC { def: header_c } - } -} - -impl fmt::Display for HeaderC { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.def) - } -} diff --git a/tests/property_test/src/lib.rs b/tests/property_test/src/lib.rs deleted file mode 100644 index c9bc9053bb..0000000000 --- a/tests/property_test/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -extern crate quickcheck; -extern crate rand; -extern crate tempdir; - -pub mod fuzzers; diff --git a/tests/quickchecking/Cargo.toml b/tests/quickchecking/Cargo.toml new file mode 100644 index 0000000000..2f5b3b71bc --- /dev/null +++ b/tests/quickchecking/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "quickchecking" +description = "Bindgen property tests with quickcheck. Generate random valid C code and pass it to the csmith/predicate.py script" +version = "0.1.0" +authors = ["Shea Newton "] + +[dependencies] +quickcheck = "0.4" +tempdir = "0.3" +rand = "0.3" diff --git a/tests/quickchecking/src/fuzzers.rs b/tests/quickchecking/src/fuzzers.rs new file mode 100644 index 0000000000..7549149f54 --- /dev/null +++ b/tests/quickchecking/src/fuzzers.rs @@ -0,0 +1,599 @@ +use quickcheck::{Arbitrary, Gen, StdGen}; +use std::fmt; +use rand::thread_rng; + +/// BaseTypeC is used in generation of C headers to represent the C language's +/// primitive types as well as `void*`. +#[derive(Debug, Clone)] +pub struct BaseTypeC { + /// String representation of C type. + pub def: String, +} + +/// TypeQualifierC is used in generation of C headers to represent qualifiers +/// such as `const`. +#[derive(Debug, Clone)] +pub struct TypeQualifierC { + /// String representation of C type qualifier. + pub def: String, +} + +/// PointerLevelC is used in generation of C headers to represent number of +/// `*` for pointer types. +#[derive(Debug, Clone)] +pub struct PointerLevelC { + /// String representation of C declaration's pointer level. + pub def: String, +} + +/// ArrayDimensionC is used in generation of C headers to represent number of +/// `[]` used to define array types. +#[derive(Debug, Clone)] +pub struct ArrayDimensionC { + /// String representation of C declaration's array dimension. + pub def: String, +} + +/// BasicTypeDeclarationC is used in generation of C headers to represent +/// declarations outside of function pointers that take the form +/// `BaseTypeC` + `TypeQualifierC` + `PointerLevelC` + `ident_id`. +#[derive(Debug, Clone)] +pub struct BasicTypeDeclarationC { + /// The declaration's base type, i.e. `int`. + pub type_name: BaseTypeC, + /// The declaration's type qualifier, i.e. `const`. + pub type_qualifier: TypeQualifierC, + /// The declaration's pointer level, i.e. `***`. + pub pointer_level: PointerLevelC, + /// The declaration's identifier, i.e. ident_N. + pub ident_id: String, +} + +/// StructDeclarationC is used in generation of C headers to represent the +/// definition of a struct type. +#[derive(Debug, Clone)] +pub struct StructDeclarationC { + /// The declaration's fields. + pub fields: DeclarationListC, + /// The declaration's identifier, i.e. struct_N. + pub ident_id: String, +} + +/// UnionDeclarationC is used in generation of C headers to represent the +/// definition of a union type. +#[derive(Debug, Clone)] +pub struct UnionDeclarationC { + /// The declaration's fields. + pub fields: DeclarationListC, + /// The declaration's identifier, i.e. union_N. + pub ident_id: String, +} + +/// FunctionPointerDeclarationC is used in generation of C headers to represent +/// the definition of a function pointer type. +#[derive(Debug, Clone)] +pub struct FunctionPointerDeclarationC { + /// The function's type qualifier, i.e. `const`. + pub type_qualifier: TypeQualifierC, + /// The function's return type, i.e. `int`. + pub type_name: BaseTypeC, + /// The function's pointer level, i.e. `***`. + pub pointer_level: PointerLevelC, + /// The function's parameters. + pub params: ParameterListC, + /// The declaration's identifier, i.e. func_ptr_N. + pub ident_id: String, +} + +/// FunctionPrototypeC is used in generation of C headers to represent the +/// definition of a function prototype. +#[derive(Debug, Clone)] +pub struct FunctionPrototypeC { + /// The function's type qualifier, i.e. `const`. + pub type_qualifier: TypeQualifierC, + /// The function's return type, i.e. `int`. + pub type_name: BaseTypeC, + /// The function's pointer level, i.e. `***`. + pub pointer_level: PointerLevelC, + /// The function's parameters. + pub params: ParameterListC, + /// The prototype's identifier, i.e. `func_N`. + pub ident_id: String, +} + +/// ParameterC is used in generation of C headers to represent the +/// definition function parameters. +#[derive(Debug, Clone)] +pub struct ParameterC { + /// The parameter's type qualifier, i.e. `const`. + pub type_qualifier: TypeQualifierC, + /// The parameter's base type, i.e. `int`. + pub type_name: BaseTypeC, + /// The parameter's pointer level, i.e. `***`. + pub pointer_level: PointerLevelC, +} + +/// ParameterListC is used in generation of C headers to represent a list of +/// definitions of function parameters. +#[derive(Debug, Clone)] +pub struct ParameterListC { + /// Parameters that define a C function signature. + pub params: Vec, +} + +/// DeclarationC is used in generation of C headers to represent all supported +/// C type declarations allowed in the generated header. +#[derive(Debug, Clone)] +pub enum DeclarationC { + /// Function prototype declaration kind. + FunctionDecl(FunctionPrototypeC), + /// Function pointer declaration kind. + FunctionPtrDecl(FunctionPointerDeclarationC), + /// Struct declaration kind. + StructDecl(StructDeclarationC), + /// Union declaration kind. + UnionDecl(UnionDeclarationC), + /// Basic type declaration kind. + VariableDecl(BasicTypeDeclarationC), +} + +/// DeclarationListC is used in generation of C headers to represent a list of +/// declarations. +#[derive(Debug, Clone)] +pub struct DeclarationListC { + /// Grouping of C declarations. + pub decls: Vec, +} + +/// HeaderC is used in generation of C headers to represent a collection of +/// declarations. +#[derive(Debug, Clone)] +pub struct HeaderC { + /// The header's declarations. + pub def: DeclarationListC, +} + +/// MakeUnique is used in generation of C headers to make declaration +/// identifiers unique by incorporating the `stamp` parameter into it's name. +trait MakeUnique { + fn make_unique(&mut self, stamp: usize); +} + +/// MakeUnique is used in generation of C headers to make DeclarationC +/// identifiers unique. +impl MakeUnique for DeclarationC { + fn make_unique(&mut self, stamp: usize) { + match *self { + DeclarationC::FunctionDecl(ref mut d) => d.make_unique(stamp), + DeclarationC::FunctionPtrDecl(ref mut d) => d.make_unique(stamp), + DeclarationC::StructDecl(ref mut d) => d.make_unique(stamp), + DeclarationC::UnionDecl(ref mut d) => d.make_unique(stamp), + DeclarationC::VariableDecl(ref mut d) => d.make_unique(stamp), + } + } +} + +/// A qucickcheck trait for describing how DeclarationC types can be +/// randomly generated and shrunk. +impl Arbitrary for DeclarationC { + fn arbitrary(g: &mut G) -> DeclarationC { + match g.gen_range(0, 5) { + 0 => DeclarationC::FunctionDecl(FunctionPrototypeC::arbitrary(g)), + 1 => DeclarationC::FunctionPtrDecl(FunctionPointerDeclarationC::arbitrary(g)), + 2 => DeclarationC::StructDecl(StructDeclarationC::arbitrary(g)), + 3 => DeclarationC::UnionDecl(UnionDeclarationC::arbitrary(g)), + 4 => DeclarationC::VariableDecl(BasicTypeDeclarationC::arbitrary(g)), + _ => unreachable!(), + } + } +} + +/// Enables to string and format for DeclarationC types. +impl fmt::Display for DeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DeclarationC::FunctionPtrDecl(ref d) => write!(f, "{}", d), + DeclarationC::StructDecl(ref d) => write!(f, "{}", d), + DeclarationC::UnionDecl(ref d) => write!(f, "{}", d), + DeclarationC::VariableDecl(ref d) => write!(f, "{}", d), + DeclarationC::FunctionDecl(ref d) => write!(f, "{}", d), + } + } +} + +/// A qucickcheck trait for describing how DeclarationListC types can be +/// randomly generated and shrunk. +impl Arbitrary for DeclarationListC { + fn arbitrary(g: &mut G) -> DeclarationListC { + DeclarationListC { + decls: Arbitrary::arbitrary(g), + } + } +} + +/// Enables to string and format for DeclarationListC types. +impl fmt::Display for DeclarationListC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut display = String::new(); + for decl in &self.decls { + display += &format!("{}", decl); + } + write!(f, "{}", display) + } +} + +/// A qucickcheck trait for describing how BaseTypeC types can be +/// randomly generated and shrunk. +impl Arbitrary for BaseTypeC { + fn arbitrary(g: &mut G) -> BaseTypeC { + // Special case `long double` until issue #550 is resolved. + let base_type = vec![ + "char", + "signed char", + "unsigned char", + "short", + "short int", + "signed short", + "signed short int", + "unsigned short", + "unsigned short int", + "int", + "signed", + "signed int", + "unsigned", + "unsigned int", + "long", + "long int", + "signed long", + "signed long int", + "unsigned long", + "unsigned long int", + "long long", + "long long int", + "signed long long", + "signed long long int", + "unsigned long long", + "unsigned long long int", + "float", + "double", + // "long double", + "void*", + ]; + BaseTypeC { + def: String::from(*g.choose(&base_type).unwrap()), + } + } +} + +/// Enables to string and format for BaseTypeC types, +impl fmt::Display for BaseTypeC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +/// A qucickcheck trait for describing how TypeQualifierC types can be +/// randomly generated and shrunk. +impl Arbitrary for TypeQualifierC { + fn arbitrary(g: &mut G) -> TypeQualifierC { + let qualifier = vec!["const", ""]; + TypeQualifierC { + def: String::from(*g.choose(&qualifier).unwrap()), + } + } +} + +/// Enables to string and format for TypeQualifierC types. +impl fmt::Display for TypeQualifierC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +/// A qucickcheck trait for describing how PointerLevelC types can be +/// randomly generated and shrunk. +impl Arbitrary for PointerLevelC { + fn arbitrary(g: &mut G) -> PointerLevelC { + PointerLevelC { + // 16 is an arbitrary "not too big" number for capping pointer level. + def: (0..g.gen_range(0, 16)).map(|_| "*").collect::(), + } + } +} + +/// Enables to string and format for PointerLevelC types. +impl fmt::Display for PointerLevelC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +/// A qucickcheck trait for describing how ArrayDimensionC types can be +/// randomly generated and shrunk. +impl Arbitrary for ArrayDimensionC { + fn arbitrary(g: &mut G) -> ArrayDimensionC { + // Keep these small, clang complains when they get too big. + let dimensions = g.gen_range(0, 5); + let mut def = String::new(); + // Don't allow size 0 dimension until #684 and #1153 are closed. + // 16 is an arbitrary "not too big" number for capping array size. + for _ in 1..dimensions { + def += &format!("[{}]", g.gen_range(1, 16)); + } + ArrayDimensionC { def } + } +} + +/// Enables to string and format for ArrayDimensionC types. +impl fmt::Display for ArrayDimensionC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.def) + } +} + +/// MakeUnique is used in generation of C headers to make BasicTypeDeclarationC +/// identifiers unique. +impl MakeUnique for BasicTypeDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +/// A qucickcheck trait for describing how BasicTypeDeclarationC types can be +/// randomly generated and shrunk. +impl Arbitrary for BasicTypeDeclarationC { + fn arbitrary(g: &mut G) -> BasicTypeDeclarationC { + BasicTypeDeclarationC { + type_qualifier: Arbitrary::arbitrary(g), + type_name: Arbitrary::arbitrary(g), + pointer_level: Arbitrary::arbitrary(g), + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +/// Enables to string and format for BasicTypeDeclarationC types. +impl fmt::Display for BasicTypeDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {} ident_{};", + self.type_qualifier, + self.type_name, + self.pointer_level, + self.ident_id + ) + } +} + +/// MakeUnique is used in generation of C headers to make StructDeclarationC +/// identifiers unique. +impl MakeUnique for StructDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +/// A qucickcheck trait for describing how StructDeclarationC types can be +/// randomly generated and shrunk. +impl Arbitrary for StructDeclarationC { + fn arbitrary(g: &mut G) -> StructDeclarationC { + // Reduce generator size as a method of putting a bound on recursion. + // When size < 1 the empty list is generated. + let reduced_size: usize = (g.size() / 2) as usize + 1; + let mut decl_list: DeclarationListC = + Arbitrary::arbitrary(&mut StdGen::new(thread_rng(), reduced_size)); + let mut fields: DeclarationListC = DeclarationListC { decls: vec![] }; + + for (i, decl) in decl_list.decls.iter_mut().enumerate() { + match *decl { + DeclarationC::FunctionDecl(_) => {} + ref mut decl => { + decl.make_unique(i); + fields.decls.push(decl.clone()); + } + } + } + + StructDeclarationC { + fields, + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +/// Enables to string and format for StructDeclarationC types. +impl fmt::Display for StructDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "struct {{ {} }} struct_{};", self.fields, self.ident_id) + } +} + +/// MakeUnique is used in generation of C headers to make UnionDeclarationC +/// identifiers unique. +impl MakeUnique for UnionDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +/// A qucickcheck trait for describing how UnionDeclarationC types can be +/// randomly generated and shrunk. +impl Arbitrary for UnionDeclarationC { + fn arbitrary(g: &mut G) -> UnionDeclarationC { + // Reduce generator size as a method of putting a bound on recursion. + // When size < 1 the empty list is generated. + let reduced_size: usize = (g.size() / 2) as usize + 1; + let mut decl_list: DeclarationListC = + Arbitrary::arbitrary(&mut StdGen::new(thread_rng(), reduced_size)); + let mut fields: DeclarationListC = DeclarationListC { decls: vec![] }; + + for (i, decl) in decl_list.decls.iter_mut().enumerate() { + match *decl { + DeclarationC::FunctionDecl(_) => {} + ref mut decl => { + decl.make_unique(i); + fields.decls.push(decl.clone()); + } + } + } + + UnionDeclarationC { + fields, + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +/// Enables to string and format for UnionDeclarationC types. +impl fmt::Display for UnionDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "union {{ {} }} union_{};", self.fields, self.ident_id) + } +} + +/// MakeUnique is used in generation of C headers to make +/// FunctionPointerDeclarationC identifiers unique. +impl MakeUnique for FunctionPointerDeclarationC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +/// A qucickcheck trait for describing how FunctionPointerDeclarationC types can +/// be randomly generated and shrunk. +impl Arbitrary for FunctionPointerDeclarationC { + fn arbitrary(g: &mut G) -> FunctionPointerDeclarationC { + FunctionPointerDeclarationC { + type_qualifier: Arbitrary::arbitrary(g), + type_name: Arbitrary::arbitrary(g), + pointer_level: Arbitrary::arbitrary(g), + params: Arbitrary::arbitrary(g), + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +/// Enables to string and format for FunctionPointerDeclarationC types. +impl fmt::Display for FunctionPointerDeclarationC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {} (*func_ptr_{})({});", + self.type_qualifier, + self.type_name, + self.pointer_level, + self.ident_id, + self.params + ) + } +} + +/// MakeUnique is used in generation of C headers to make FunctionPrototypeC +/// identifiers unique. +impl MakeUnique for FunctionPrototypeC { + fn make_unique(&mut self, stamp: usize) { + self.ident_id += &format!("_{}", stamp); + } +} + +/// A qucickcheck trait for describing how FunctionPrototypeC types can be +/// randomly generated and shrunk. +impl Arbitrary for FunctionPrototypeC { + fn arbitrary(g: &mut G) -> FunctionPrototypeC { + FunctionPrototypeC { + type_qualifier: Arbitrary::arbitrary(g), + type_name: Arbitrary::arbitrary(g), + pointer_level: Arbitrary::arbitrary(g), + params: Arbitrary::arbitrary(g), + ident_id: format!("{}", usize::arbitrary(g)), + } + } +} + +/// Enables to string and format for FunctionPrototypeC types. +impl fmt::Display for FunctionPrototypeC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {} func_{}({});", + self.type_qualifier, + self.type_name, + self.pointer_level, + self.ident_id, + self.params + ) + } +} + +/// A qucickcheck trait for describing how ParameterC types can be +/// randomly generated and shrunk. +impl Arbitrary for ParameterC { + fn arbitrary(g: &mut G) -> ParameterC { + ParameterC { + type_qualifier: Arbitrary::arbitrary(g), + type_name: Arbitrary::arbitrary(g), + pointer_level: Arbitrary::arbitrary(g), + } + } +} + +/// Enables to string and format for ParameterC types. +impl fmt::Display for ParameterC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{} {} {}", + self.type_qualifier, + self.type_name, + self.pointer_level + ) + } +} + +/// A qucickcheck trait for describing how ParameterListC types can be +/// randomly generated and shrunk. +impl Arbitrary for ParameterListC { + fn arbitrary(g: &mut G) -> ParameterListC { + ParameterListC { + params: Arbitrary::arbitrary(g), + } + } +} + +/// Enables to string and format for ParameterListC types. +impl fmt::Display for ParameterListC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut display = String::new(); + for (i, p) in self.params.iter().enumerate() { + match i { + 0 => display += &format!("{}", p), + _ => display += &format!(",{}", p), + } + } + write!(f, "{}", display) + } +} + +/// A qucickcheck trait for describing how HeaderC types can be +/// randomly generated and shrunk. +impl Arbitrary for HeaderC { + fn arbitrary(g: &mut G) -> HeaderC { + let mut decl_list: DeclarationListC = Arbitrary::arbitrary(g); + for (i, decl) in decl_list.decls.iter_mut().enumerate() { + decl.make_unique(i); + } + HeaderC { def: decl_list } + } +} + +/// Enables to string and format for HeaderC types. +impl fmt::Display for HeaderC { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut display = String::new(); + for decl in &self.def.decls { + display += &format!("{}", decl); + } + write!(f, "{}", display) + } +} diff --git a/tests/quickchecking/src/lib.rs b/tests/quickchecking/src/lib.rs new file mode 100644 index 0000000000..3bea8a8ebb --- /dev/null +++ b/tests/quickchecking/src/lib.rs @@ -0,0 +1,28 @@ +//! 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)] +extern crate quickcheck; +extern crate rand; +extern crate tempdir; + +/// Contains definitions of and impls for types used to fuzz C declarations. +pub mod fuzzers; diff --git a/tests/property_test/tests/fuzzed-c-headers.rs b/tests/quickchecking/tests/fuzzed-c-headers.rs similarity index 77% rename from tests/property_test/tests/fuzzed-c-headers.rs rename to tests/quickchecking/tests/fuzzed-c-headers.rs index b132a7591f..f550cf0c27 100644 --- a/tests/property_test/tests/fuzzed-c-headers.rs +++ b/tests/quickchecking/tests/fuzzed-c-headers.rs @@ -1,9 +1,9 @@ -extern crate property_test; extern crate quickcheck; +extern crate quickchecking; extern crate rand; extern crate tempdir; -use property_test::fuzzers; +use quickchecking::fuzzers; use quickcheck::{QuickCheck, StdGen, TestResult}; use std::fs::File; use std::io::Write; @@ -18,7 +18,7 @@ fn run_predicate_script(header: fuzzers::HeaderC, header_name: &str) -> Result Result TestResult { @@ -67,10 +64,10 @@ fn bindgen_prop(header: fuzzers::HeaderC) -> TestResult { #[test] fn test_bindgen() { - // enough to generate any value in the PrimitiveTypeC `base_type` list + // Enough to generate any value in the PrimitiveTypeC `base_type` list. let generate_range: usize = 32; QuickCheck::new() - // generating is relatively quick (generate_range 150 takes ~5 seconds) + // Generating is relatively quick (generate_range 150 takes ~5 seconds) // but running predicate.py takes ~30 seconds per source file / test // when the generation range is just 32. It can take a lot longer with a // higher generate_range. Up the number of tests or generate_range if