Skip to content

Commit

Permalink
Auto merge of #1180 - snewt:feat/quickchecking-cargo-features, r=fitzgen
Browse files Browse the repository at this point in the history
Enable Cargo features for quickchecking crate

Logic to enable/disable special casing (due to known issues #550, #684, and #1153) has been exposed as features in the `quickchecking` crate's Cargo.toml file and corresponding `cfg` attributes in the source.

In addition to adding Cargo features, this PR represents the following:
- Documentation in `bindgen`'s CONTRIBUTING.md that points to a new README.md located in the `quickchecking` crate's directory.
- The Debug trait was implemented for the `HeaderC` type. This enables failing property tests to be reported as C source code rather than a Rust data structure.
- The ArrayDimensionC type is now used in header generation for union, struct, and basic declarations.

Thanks for taking a look and for any feedback!

Closes #1169

r? @fitzgen
  • Loading branch information
bors-servo authored Dec 9, 2017
2 parents d4829d3 + 2778fe5 commit 6355f0a
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 12 deletions.
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ out to us in a GitHub issue, or stop by
- [Test Expectations and `libclang` Versions](#test-expectations-and-libclang-versions)
- [Integration Tests](#integration-tests)
- [Fuzzing `bindgen` with `csmith`](#fuzzing-bindgen-with-csmith)
- [Property tests for `bindgen` with `quickchecking`](#property-tests-for-bindgen-with-quickchecking)
- [Code Overview](#code-overview)
- [Pull Requests and Code Reviews](#pull-requests-and-code-reviews)
- [Generating Graphviz Dot Files](#generating-graphviz-dot-files)
Expand Down Expand Up @@ -224,6 +225,14 @@ uncover hidden bugs is by running `csmith` to generate random headers to test

See [./csmith-fuzzing/README.md](./csmith-fuzzing/README.md) for details.

### Property tests for `bindgen` with `quickchecking`

The `tests/quickchecking` crate generates property tests for `bindgen`.
From the crate's directory you can run the tests with `cargo run`. For details
on additional configuration including how to preserve / inspect the generated
property tests, see
[./tests/quickchecking/README.md](./tests/quickchecking/README.md).

## Code Overview

`bindgen` takes C and C++ header files as input and generates corresponding Rust
Expand Down
12 changes: 12 additions & 0 deletions tests/quickchecking/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@ lazy_static = "1.0"
quickcheck = "0.4"
rand = "0.3"
tempdir = "0.3"

[features]
# No features by default.
default = []

# Enable the generation of code that allows for zero sized arrays as struct
# fields. Until issues #684 and #1153 are resolved this can result in failing tests.
zero-sized-arrays = []

# Enable the generation of code that allows for long double types as struct
# fields. Until issue #550 is resolved this can result in failing tests.
long-doubles = []
39 changes: 39 additions & 0 deletions tests/quickchecking/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Property tests for `bindgen` with `quickchecking`

`quickchecking` generates random C headers to test `bindgen`
using the [`quickcheck`][quickcheck] property testing crate. When testing
`bindgen` with `quickchecking`, the generated header files are passed to
`bindgen`'s `csmith-fuzzing/predicate.py` script. If that script fails,
`quickchecking` panics, and you can report an issue containing the test case!

<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->


- [Prerequisites](#prerequisites)
- [Running](#running)

<!-- END doctoc generated TOC please keep comment here to allow auto update -->

## Prerequisites

Requires `python3` to be in `$PATH`.

Many systems have `python3` by default but if your OS doesn't, its package
manager may make it available:

```
$ sudo apt install python3
$ brew install python3
$ # Etc...
```

## Running

Run `quickchecking` binary to generate and test fuzzed C headers with
`cargo run`. Additional configuration is exposed through the binary's CLI.

```
$ cargo run --bin=quickchecking -- -h
```
[quickcheck]: https://github.com/BurntSushi/quickcheck
6 changes: 3 additions & 3 deletions tests/quickchecking/src/bin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//! An application to run property tests for `bindgen` with _fuzzed_ C headers
//! using `quickcheck`
//! An application to run property tests for `bindgen` with _fuzzed_ C headers
//! using `quickcheck`
//!
//! ## Usage
//!
Expand Down Expand Up @@ -77,7 +77,7 @@ fn main() {
"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 that execution time to increase \
to grow much for execution time to increase \
significantly.",
)
.takes_value(true)
Expand Down
56 changes: 47 additions & 9 deletions tests/quickchecking/src/fuzzers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ pub struct BasicTypeDeclarationC {
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,
}
Expand All @@ -55,6 +57,8 @@ pub struct BasicTypeDeclarationC {
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,
}
Expand All @@ -65,6 +69,8 @@ pub struct StructDeclarationC {
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,
}
Expand Down Expand Up @@ -147,7 +153,7 @@ pub struct DeclarationListC {

/// HeaderC is used in generation of C headers to represent a collection of
/// declarations.
#[derive(Debug, Clone)]
#[derive(Clone)]
pub struct HeaderC {
/// The header's declarations.
pub def: DeclarationListC,
Expand Down Expand Up @@ -256,7 +262,8 @@ impl Arbitrary for BaseTypeC {
"unsigned long long int",
"float",
"double",
// "long double",
#[cfg(feature = "long-doubles")]
"long double",
"void*",
];
BaseTypeC {
Expand Down Expand Up @@ -315,10 +322,17 @@ impl Arbitrary for 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.

let lower_bound;
if cfg!(feature = "zero-sized-arrays") {
lower_bound = 0;
} else {
lower_bound = 1;
}

for _ in 1..dimensions {
def += &format!("[{}]", g.gen_range(1, 16));
// 16 is an arbitrary "not too big" number for capping array size.
def += &format!("[{}]", g.gen_range(lower_bound, 16));
}
ArrayDimensionC { def }
}
Expand Down Expand Up @@ -347,6 +361,7 @@ impl Arbitrary for 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)),
}
}
Expand All @@ -357,11 +372,12 @@ impl fmt::Display for BasicTypeDeclarationC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{} {} {} ident_{};",
"{} {} {} ident_{}{};",
self.type_qualifier,
self.type_name,
self.pointer_level,
self.ident_id
self.ident_id,
self.array_dimension
)
}
}
Expand Down Expand Up @@ -398,14 +414,21 @@ impl Arbitrary for StructDeclarationC {
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)
write!(
f,
"struct {{ {} }} struct_{}{};",
self.fields,
self.ident_id,
self.array_dimension
)
}
}

Expand Down Expand Up @@ -441,14 +464,21 @@ impl Arbitrary for UnionDeclarationC {
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)
write!(
f,
"union {{ {} }} union_{}{};",
self.fields,
self.ident_id,
self.array_dimension
)
}
}

Expand Down Expand Up @@ -597,3 +627,11 @@ impl fmt::Display for HeaderC {
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)
}
}

0 comments on commit 6355f0a

Please sign in to comment.