diff options
author | Shea Newton <sheanewt@gmail.com> | 2017-11-28 19:30:38 -0800 |
---|---|---|
committer | Shea Newton <sheanewt@gmail.com> | 2017-11-29 19:59:35 -0800 |
commit | 2aa9b1d2344ce2007b5bba2a4c9141c837f99676 (patch) | |
tree | 1c705395db767f2cca0cd47e8370e036b37f2758 | |
parent | c981808952bf1305d08828eb4ce8c8f7d15ba7c2 (diff) |
Address requested changes to quickchecking crate.
- 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
-rw-r--r-- | .travis.yml | 1 | ||||
-rwxr-xr-x | ci/script.sh | 5 | ||||
-rw-r--r-- | tests/property_test/Cargo.toml | 10 | ||||
-rw-r--r-- | tests/property_test/src/fuzzers.rs | 468 | ||||
-rw-r--r-- | tests/property_test/src/lib.rs | 5 | ||||
-rw-r--r-- | tests/quickchecking/Cargo.toml | 10 | ||||
-rw-r--r-- | tests/quickchecking/src/fuzzers.rs | 599 | ||||
-rw-r--r-- | tests/quickchecking/src/lib.rs | 28 | ||||
-rw-r--r-- | tests/quickchecking/tests/fuzzed-c-headers.rs (renamed from tests/property_test/tests/fuzzed-c-headers.rs) | 27 |
9 files changed, 655 insertions, 498 deletions
diff --git a/.travis.yml b/.travis.yml index e5d0e105..2f9cfd63 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 7338dc0b..b9e75892 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 fa644b4b..00000000 --- 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 <snewton@polysync.io>"] - -[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 113c9f33..00000000 --- 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: Gen>(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: Gen>(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: Gen>(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: Gen>(g: &mut G) -> PointerLevelC { - PointerLevelC { - def: (0..usize::arbitrary(g)).map(|_| "*").collect::<String>(), - } - } -} - -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: Gen>(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: Gen>(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: Gen>(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<DeclarationC> = - 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: Gen>(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<DeclarationC> = - 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: Gen>(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: Gen>(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: Gen>(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: Gen>(g: &mut G) -> ParameterListC { - let mut params_string = String::new(); - let params: Vec<ParameterC> = 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: Gen>(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<DeclarationC> = 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 c9bc9053..00000000 --- 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 00000000..2f5b3b71 --- /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 <snewton@polysync.io>"] + +[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 00000000..7549149f --- /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<ParameterC>, +} + +/// 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<DeclarationC>, +} + +/// 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: Gen>(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: Gen>(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: Gen>(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: Gen>(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: Gen>(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::<String>(), + } + } +} + +/// 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: Gen>(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: Gen>(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: Gen>(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: Gen>(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: Gen>(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: Gen>(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: Gen>(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: Gen>(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: Gen>(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 00000000..3bea8a8e --- /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 index b132a759..f550cf0c 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<O 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.write_all(header.to_string().as_bytes())?; header_file.sync_all()?; let header_path_string; @@ -39,20 +39,17 @@ fn run_predicate_script(header: fuzzers::HeaderC, header_name: &str) -> Result<O // 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()?; + 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 { @@ -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 |