summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2017-11-30 12:09:46 -0600
committerGitHub <noreply@github.com>2017-11-30 12:09:46 -0600
commitcd920a398384f15f909e98f656bbbf7dd89cf1a8 (patch)
tree4b2da3531336b951b84dea53db36b2f53d37e8b5
parent0af9c897939c68eed3e5fb3ee07d99b5f53fd7a5 (diff)
parent2aa9b1d2344ce2007b5bba2a4c9141c837f99676 (diff)
Auto merge of #1159 - snewt:feat/970-quickcheck-fuzzing, r=fitzgen
Property testing with quickcheck 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 #### Usage ```bash cd tests/property_test cargo test ``` Some things I'm unsure of: #### Where should this feature live? At the moment it lives in `tests/property_test` but isn't run when `cargo test` is invoked from bindgen's cargo manifest directory. #### What's an acceptable ammount of time for these tests to take? 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. #### How do we expose the generated code for easy inspection? 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? #### Special casing 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? #### Does the fuzzer warrant its own crate? 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. #### What does it look like to expose more useful functionality? 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
-rw-r--r--.travis.yml1
-rwxr-xr-xci/script.sh5
-rw-r--r--tests/quickchecking/Cargo.toml10
-rw-r--r--tests/quickchecking/src/fuzzers.rs599
-rw-r--r--tests/quickchecking/src/lib.rs28
-rw-r--r--tests/quickchecking/tests/fuzzed-c-headers.rs78
6 files changed, 721 insertions, 0 deletions
diff --git a/.travis.yml b/.travis.yml
index 79e35e42..1e813229 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -34,6 +34,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/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/quickchecking/tests/fuzzed-c-headers.rs b/tests/quickchecking/tests/fuzzed-c-headers.rs
new file mode 100644
index 00000000..f550cf0c
--- /dev/null
+++ b/tests/quickchecking/tests/fuzzed-c-headers.rs
@@ -0,0 +1,78 @@
+extern crate quickcheck;
+extern crate quickchecking;
+extern crate rand;
+extern crate tempdir;
+
+use quickchecking::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<Output, Box<Error>> {
+ 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.to_string().as_bytes())?;
+ header_file.sync_all()?;
+
+ let header_path_string;
+ match header_path.into_os_string().into_string() {
+ Ok(s) => header_path_string = s,
+ Err(_) => return Err(From::from("error converting path into String")),
+ }
+
+ let mut predicate_script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ predicate_script_path.push("../../csmith-fuzzing/predicate.py");
+
+ let predicate_script_path_string;
+ match predicate_script_path.into_os_string().into_string() {
+ Ok(s) => predicate_script_path_string = s,
+ Err(_) => return Err(From::from("error converting path into String")),
+ }
+
+ // Copy generated temp files to 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()?)
+}
+
+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)
+}