summaryrefslogtreecommitdiff
path: root/bindgen-tests/tests/quickchecking/src
diff options
context:
space:
mode:
Diffstat (limited to 'bindgen-tests/tests/quickchecking/src')
-rw-r--r--bindgen-tests/tests/quickchecking/src/bin.rs112
-rw-r--r--bindgen-tests/tests/quickchecking/src/fuzzers.rs635
-rw-r--r--bindgen-tests/tests/quickchecking/src/lib.rs133
3 files changed, 880 insertions, 0 deletions
diff --git a/bindgen-tests/tests/quickchecking/src/bin.rs b/bindgen-tests/tests/quickchecking/src/bin.rs
new file mode 100644
index 00000000..f2b52e82
--- /dev/null
+++ b/bindgen-tests/tests/quickchecking/src/bin.rs
@@ -0,0 +1,112 @@
+//! An application to run property tests for `bindgen` with _fuzzed_ C headers
+//! using `quickcheck`
+//!
+//! ## Usage
+//!
+//! Print help
+//! ```bash
+//! $ cargo run --bin=quickchecking -- -h
+//! ```
+//!
+//! Run with default values
+//! ```bash
+//! $ cargo run --bin=quickchecking
+//! ```
+//!
+#![deny(missing_docs)]
+extern crate clap;
+extern crate quickchecking;
+
+use clap::{App, Arg};
+use std::path::Path;
+
+// Validate CLI argument input for generation range.
+fn validate_generate_range(v: String) -> Result<(), String> {
+ match v.parse::<usize>() {
+ Ok(_) => Ok(()),
+ Err(_) => Err(String::from(
+ "Generate range could not be converted to a usize.",
+ )),
+ }
+}
+
+// Validate CLI argument input for tests count.
+fn validate_tests_count(v: String) -> Result<(), String> {
+ match v.parse::<usize>() {
+ Ok(_) => Ok(()),
+ Err(_) => Err(String::from(
+ "Tests count could not be converted to a usize.",
+ )),
+ }
+}
+
+// Validate CLI argument input for fuzzed headers output path.
+fn validate_path(v: String) -> Result<(), String> {
+ match Path::new(&v).is_dir() {
+ true => Ok(()),
+ false => Err(String::from("Provided directory path does not exist.")),
+ }
+}
+
+fn main() {
+ let matches = App::new("quickchecking")
+ .version("0.2.0")
+ .about(
+ "Bindgen property tests with quickcheck. \
+ Generate random valid C code and pass it to the \
+ csmith/predicate.py script",
+ )
+ .arg(
+ Arg::with_name("path")
+ .short("p")
+ .long("path")
+ .value_name("PATH")
+ .help(
+ "Optional. Preserve generated headers for inspection, \
+ provide directory path for header output. [default: None] ",
+ )
+ .takes_value(true)
+ .validator(validate_path),
+ )
+ .arg(
+ Arg::with_name("range")
+ .short("r")
+ .long("range")
+ .value_name("RANGE")
+ .help(
+ "Sets the range quickcheck uses during generation. \
+ Corresponds to things like arbitrary usize and \
+ arbitrary vector length. This number doesn't have \
+ to grow much for execution time to increase \
+ significantly.",
+ )
+ .takes_value(true)
+ .default_value("32")
+ .validator(validate_generate_range),
+ )
+ .arg(
+ Arg::with_name("count")
+ .short("c")
+ .long("count")
+ .value_name("COUNT")
+ .help(
+ "Count / number of tests to run. Running a fuzzed \
+ header through the predicate.py script can take a \
+ long time, especially if the generation range is \
+ large. Increase this number if you're willing to \
+ wait a while.",
+ )
+ .takes_value(true)
+ .default_value("2")
+ .validator(validate_tests_count),
+ )
+ .get_matches();
+
+ let output_path: Option<&str> = matches.value_of("path");
+ let generate_range: usize =
+ matches.value_of("range").unwrap().parse::<usize>().unwrap();
+ let tests: usize =
+ matches.value_of("count").unwrap().parse::<usize>().unwrap();
+
+ quickchecking::test_bindgen(generate_range, tests, output_path)
+}
diff --git a/bindgen-tests/tests/quickchecking/src/fuzzers.rs b/bindgen-tests/tests/quickchecking/src/fuzzers.rs
new file mode 100644
index 00000000..b609dd5a
--- /dev/null
+++ b/bindgen-tests/tests/quickchecking/src/fuzzers.rs
@@ -0,0 +1,635 @@
+use quickcheck::{Arbitrary, Gen, StdGen};
+use rand::thread_rng;
+use std::fmt;
+
+/// 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 array dimension, i.e. [][][].
+ pub array_dimension: ArrayDimensionC,
+ /// 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 array dimension, i.e. [][][].
+ pub array_dimension: ArrayDimensionC,
+ /// 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 array dimension, i.e. [][][].
+ pub array_dimension: ArrayDimensionC,
+ /// 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(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",
+ #[cfg(feature = "long-doubles")]
+ "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();
+
+ let lower_bound;
+ if cfg!(feature = "zero-sized-arrays") {
+ lower_bound = 0;
+ } else {
+ lower_bound = 1;
+ }
+
+ for _ in 1..dimensions {
+ // 16 is an arbitrary "not too big" number for capping array size.
+ def += &format!("[{}]", g.gen_range(lower_bound, 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),
+ array_dimension: 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,
+ self.array_dimension
+ )
+ }
+}
+
+/// 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)),
+ array_dimension: Arbitrary::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, self.array_dimension
+ )
+ }
+}
+
+/// 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)),
+ array_dimension: Arbitrary::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, self.array_dimension
+ )
+ }
+}
+
+/// 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)
+ }
+}
+
+/// Use Display trait for Debug so that any failing property tests report
+/// generated C code rather than the data structures that contain it.
+impl fmt::Debug for HeaderC {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self)
+ }
+}
diff --git a/bindgen-tests/tests/quickchecking/src/lib.rs b/bindgen-tests/tests/quickchecking/src/lib.rs
new file mode 100644
index 00000000..b09d1c49
--- /dev/null
+++ b/bindgen-tests/tests/quickchecking/src/lib.rs
@@ -0,0 +1,133 @@
+//! A library to generate __fuzzed__ C headers for use with `quickcheck`
+//!
+//! ## Example
+//!
+//! ```rust
+//! extern crate quickcheck;
+//! extern crate quickchecking;
+//! extern crate rand;
+//! use quickcheck::{Arbitrary, Gen, StdGen};
+//! use quickchecking::fuzzers;
+//! use rand::thread_rng;
+//!
+//! fn main() {
+//! let generate_range: usize = 10; // Determines things like the length of
+//! // arbitrary vectors generated.
+//! let header = fuzzers::HeaderC::arbitrary(
+//! &mut StdGen::new(thread_rng(), generate_range));
+//! println!("{}", header);
+//! }
+//! ```
+//!
+#![deny(missing_docs)]
+#[macro_use]
+extern crate lazy_static;
+extern crate quickcheck;
+extern crate rand;
+extern crate tempdir;
+
+use quickcheck::{QuickCheck, StdGen, TestResult};
+use rand::thread_rng;
+use std::error::Error;
+use std::fs::File;
+use std::io::Write;
+use std::path::PathBuf;
+use std::process::{Command, Output};
+use std::sync::Mutex;
+use tempdir::TempDir;
+
+/// Contains definitions of and impls for types used to fuzz C declarations.
+pub mod fuzzers;
+
+// Global singleton, manages context across tests. For now that context is
+// only the output_path for inspecting fuzzed headers (if specified).
+struct Context {
+ output_path: Option<String>,
+}
+
+// Initialize global context.
+lazy_static! {
+ static ref CONTEXT: Mutex<Context> =
+ Mutex::new(Context { output_path: None });
+}
+
+// Passes fuzzed header to the `csmith-fuzzing/predicate.py` script, returns
+// output of the associated command.
+fn run_predicate_script(
+ header: fuzzers::HeaderC,
+) -> Result<Output, Box<dyn Error>> {
+ let dir = TempDir::new("bindgen_prop")?;
+ let header_path = dir.path().join("prop_test.h");
+
+ let mut header_file = File::create(&header_path)?;
+ header_file.write_all(header.to_string().as_bytes())?;
+ header_file.sync_all()?;
+
+ let header_path_string;
+ match header_path.into_os_string().into_string() {
+ Ok(s) => header_path_string = s,
+ Err(_) => return Err(From::from("error converting path into String")),
+ }
+
+ let mut predicate_script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+ predicate_script_path.push("../../csmith-fuzzing/predicate.py");
+
+ let predicate_script_path_string;
+ match predicate_script_path.into_os_string().into_string() {
+ Ok(s) => predicate_script_path_string = s,
+ Err(_) => return Err(From::from("error converting path into String")),
+ }
+
+ // Copy generated temp files to output_path directory for inspection.
+ // If `None`, output path not specified, don't copy.
+ match CONTEXT.lock().unwrap().output_path {
+ Some(ref path) => {
+ Command::new("cp")
+ .arg("-a")
+ .arg(&dir.path().to_str().unwrap())
+ .arg(&path)
+ .output()?;
+ }
+ None => {}
+ }
+
+ Ok(Command::new(&predicate_script_path_string)
+ .arg(&header_path_string)
+ .output()?)
+}
+
+// Generatable property. Pass generated headers off to run through the
+// `csmith-fuzzing/predicate.py` script. Success is measured by the success
+// status of that command.
+fn bindgen_prop(header: fuzzers::HeaderC) -> TestResult {
+ match run_predicate_script(header) {
+ Ok(o) => return TestResult::from_bool(o.status.success()),
+ Err(e) => {
+ println!("{:?}", e);
+ return TestResult::from_bool(false);
+ }
+ }
+}
+
+/// Instantiate a Quickcheck object and use it to run property tests using
+/// fuzzed C headers generated with types defined in the `fuzzers` module.
+/// Success/Failure is dictated by the result of passing the fuzzed headers
+/// to the `csmith-fuzzing/predicate.py` script.
+pub fn test_bindgen(
+ generate_range: usize,
+ tests: usize,
+ output_path: Option<&str>,
+) {
+ match output_path {
+ Some(path) => {
+ CONTEXT.lock().unwrap().output_path =
+ Some(String::from(PathBuf::from(path).to_str().unwrap()));
+ }
+ None => {} // Path not specified, don't provide output.
+ }
+
+ QuickCheck::new()
+ .tests(tests)
+ .gen(StdGen::new(thread_rng(), generate_range))
+ .quickcheck(bindgen_prop as fn(fuzzers::HeaderC) -> TestResult)
+}