summaryrefslogtreecommitdiff
path: root/tests/quickchecking
diff options
context:
space:
mode:
authorShea Newton <sheanewt@gmail.com>2017-11-28 19:30:38 -0800
committerShea Newton <sheanewt@gmail.com>2017-11-29 19:59:35 -0800
commit2aa9b1d2344ce2007b5bba2a4c9141c837f99676 (patch)
tree1c705395db767f2cca0cd47e8370e036b37f2758 /tests/quickchecking
parentc981808952bf1305d08828eb4ce8c8f7d15ba7c2 (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
Diffstat (limited to 'tests/quickchecking')
-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
4 files changed, 715 insertions, 0 deletions
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)
+}