Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable Cargo features for quickchecking crate #1180

Merged
merged 2 commits into from
Dec 9, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to only show the -h or --help command invocation, and leave out the command's output. As we add more options/flags the output will change and it is likely we will forget to update this code block.

```
[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)
}
}