Skip to content

Commit 6355f0a

Browse files
author
bors-servo
authored
Auto merge of #1180 - snewt:feat/quickchecking-cargo-features, r=fitzgen
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
2 parents d4829d3 + 2778fe5 commit 6355f0a

File tree

5 files changed

+110
-12
lines changed

5 files changed

+110
-12
lines changed

CONTRIBUTING.md

+9
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ out to us in a GitHub issue, or stop by
2121
- [Test Expectations and `libclang` Versions](#test-expectations-and-libclang-versions)
2222
- [Integration Tests](#integration-tests)
2323
- [Fuzzing `bindgen` with `csmith`](#fuzzing-bindgen-with-csmith)
24+
- [Property tests for `bindgen` with `quickchecking`](#property-tests-for-bindgen-with-quickchecking)
2425
- [Code Overview](#code-overview)
2526
- [Pull Requests and Code Reviews](#pull-requests-and-code-reviews)
2627
- [Generating Graphviz Dot Files](#generating-graphviz-dot-files)
@@ -224,6 +225,14 @@ uncover hidden bugs is by running `csmith` to generate random headers to test
224225

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

228+
### Property tests for `bindgen` with `quickchecking`
229+
230+
The `tests/quickchecking` crate generates property tests for `bindgen`.
231+
From the crate's directory you can run the tests with `cargo run`. For details
232+
on additional configuration including how to preserve / inspect the generated
233+
property tests, see
234+
[./tests/quickchecking/README.md](./tests/quickchecking/README.md).
235+
227236
## Code Overview
228237

229238
`bindgen` takes C and C++ header files as input and generates corresponding Rust

tests/quickchecking/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,15 @@ lazy_static = "1.0"
1818
quickcheck = "0.4"
1919
rand = "0.3"
2020
tempdir = "0.3"
21+
22+
[features]
23+
# No features by default.
24+
default = []
25+
26+
# Enable the generation of code that allows for zero sized arrays as struct
27+
# fields. Until issues #684 and #1153 are resolved this can result in failing tests.
28+
zero-sized-arrays = []
29+
30+
# Enable the generation of code that allows for long double types as struct
31+
# fields. Until issue #550 is resolved this can result in failing tests.
32+
long-doubles = []

tests/quickchecking/README.md

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Property tests for `bindgen` with `quickchecking`
2+
3+
`quickchecking` generates random C headers to test `bindgen`
4+
using the [`quickcheck`][quickcheck] property testing crate. When testing
5+
`bindgen` with `quickchecking`, the generated header files are passed to
6+
`bindgen`'s `csmith-fuzzing/predicate.py` script. If that script fails,
7+
`quickchecking` panics, and you can report an issue containing the test case!
8+
9+
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
10+
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
11+
12+
13+
- [Prerequisites](#prerequisites)
14+
- [Running](#running)
15+
16+
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
17+
18+
## Prerequisites
19+
20+
Requires `python3` to be in `$PATH`.
21+
22+
Many systems have `python3` by default but if your OS doesn't, its package
23+
manager may make it available:
24+
25+
```
26+
$ sudo apt install python3
27+
$ brew install python3
28+
$ # Etc...
29+
```
30+
31+
## Running
32+
33+
Run `quickchecking` binary to generate and test fuzzed C headers with
34+
`cargo run`. Additional configuration is exposed through the binary's CLI.
35+
36+
```
37+
$ cargo run --bin=quickchecking -- -h
38+
```
39+
[quickcheck]: https://github.com/BurntSushi/quickcheck

tests/quickchecking/src/bin.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
//! An application to run property tests for `bindgen` with _fuzzed_ C headers
2-
//! using `quickcheck`
1+
//! An application to run property tests for `bindgen` with _fuzzed_ C headers
2+
//! using `quickcheck`
33
//!
44
//! ## Usage
55
//!
@@ -77,7 +77,7 @@ fn main() {
7777
"Sets the range quickcheck uses during generation. \
7878
Corresponds to things like arbitrary usize and \
7979
arbitrary vector length. This number doesn't have \
80-
to grow much for that execution time to increase \
80+
to grow much for execution time to increase \
8181
significantly.",
8282
)
8383
.takes_value(true)

tests/quickchecking/src/fuzzers.rs

+47-9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub struct BasicTypeDeclarationC {
4545
pub type_qualifier: TypeQualifierC,
4646
/// The declaration's pointer level, i.e. `***`.
4747
pub pointer_level: PointerLevelC,
48+
/// The declaration's array dimension, i.e. [][][].
49+
pub array_dimension: ArrayDimensionC,
4850
/// The declaration's identifier, i.e. ident_N.
4951
pub ident_id: String,
5052
}
@@ -55,6 +57,8 @@ pub struct BasicTypeDeclarationC {
5557
pub struct StructDeclarationC {
5658
/// The declaration's fields.
5759
pub fields: DeclarationListC,
60+
/// The declaration's array dimension, i.e. [][][].
61+
pub array_dimension: ArrayDimensionC,
5862
/// The declaration's identifier, i.e. struct_N.
5963
pub ident_id: String,
6064
}
@@ -65,6 +69,8 @@ pub struct StructDeclarationC {
6569
pub struct UnionDeclarationC {
6670
/// The declaration's fields.
6771
pub fields: DeclarationListC,
72+
/// The declaration's array dimension, i.e. [][][].
73+
pub array_dimension: ArrayDimensionC,
6874
/// The declaration's identifier, i.e. union_N.
6975
pub ident_id: String,
7076
}
@@ -147,7 +153,7 @@ pub struct DeclarationListC {
147153

148154
/// HeaderC is used in generation of C headers to represent a collection of
149155
/// declarations.
150-
#[derive(Debug, Clone)]
156+
#[derive(Clone)]
151157
pub struct HeaderC {
152158
/// The header's declarations.
153159
pub def: DeclarationListC,
@@ -256,7 +262,8 @@ impl Arbitrary for BaseTypeC {
256262
"unsigned long long int",
257263
"float",
258264
"double",
259-
// "long double",
265+
#[cfg(feature = "long-doubles")]
266+
"long double",
260267
"void*",
261268
];
262269
BaseTypeC {
@@ -315,10 +322,17 @@ impl Arbitrary for ArrayDimensionC {
315322
// Keep these small, clang complains when they get too big.
316323
let dimensions = g.gen_range(0, 5);
317324
let mut def = String::new();
318-
// Don't allow size 0 dimension until #684 and #1153 are closed.
319-
// 16 is an arbitrary "not too big" number for capping array size.
325+
326+
let lower_bound;
327+
if cfg!(feature = "zero-sized-arrays") {
328+
lower_bound = 0;
329+
} else {
330+
lower_bound = 1;
331+
}
332+
320333
for _ in 1..dimensions {
321-
def += &format!("[{}]", g.gen_range(1, 16));
334+
// 16 is an arbitrary "not too big" number for capping array size.
335+
def += &format!("[{}]", g.gen_range(lower_bound, 16));
322336
}
323337
ArrayDimensionC { def }
324338
}
@@ -347,6 +361,7 @@ impl Arbitrary for BasicTypeDeclarationC {
347361
type_qualifier: Arbitrary::arbitrary(g),
348362
type_name: Arbitrary::arbitrary(g),
349363
pointer_level: Arbitrary::arbitrary(g),
364+
array_dimension: Arbitrary::arbitrary(g),
350365
ident_id: format!("{}", usize::arbitrary(g)),
351366
}
352367
}
@@ -357,11 +372,12 @@ impl fmt::Display for BasicTypeDeclarationC {
357372
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
358373
write!(
359374
f,
360-
"{} {} {} ident_{};",
375+
"{} {} {} ident_{}{};",
361376
self.type_qualifier,
362377
self.type_name,
363378
self.pointer_level,
364-
self.ident_id
379+
self.ident_id,
380+
self.array_dimension
365381
)
366382
}
367383
}
@@ -398,14 +414,21 @@ impl Arbitrary for StructDeclarationC {
398414
StructDeclarationC {
399415
fields,
400416
ident_id: format!("{}", usize::arbitrary(g)),
417+
array_dimension: Arbitrary::arbitrary(g),
401418
}
402419
}
403420
}
404421

405422
/// Enables to string and format for StructDeclarationC types.
406423
impl fmt::Display for StructDeclarationC {
407424
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
408-
write!(f, "struct {{ {} }} struct_{};", self.fields, self.ident_id)
425+
write!(
426+
f,
427+
"struct {{ {} }} struct_{}{};",
428+
self.fields,
429+
self.ident_id,
430+
self.array_dimension
431+
)
409432
}
410433
}
411434

@@ -441,14 +464,21 @@ impl Arbitrary for UnionDeclarationC {
441464
UnionDeclarationC {
442465
fields,
443466
ident_id: format!("{}", usize::arbitrary(g)),
467+
array_dimension: Arbitrary::arbitrary(g),
444468
}
445469
}
446470
}
447471

448472
/// Enables to string and format for UnionDeclarationC types.
449473
impl fmt::Display for UnionDeclarationC {
450474
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
451-
write!(f, "union {{ {} }} union_{};", self.fields, self.ident_id)
475+
write!(
476+
f,
477+
"union {{ {} }} union_{}{};",
478+
self.fields,
479+
self.ident_id,
480+
self.array_dimension
481+
)
452482
}
453483
}
454484

@@ -597,3 +627,11 @@ impl fmt::Display for HeaderC {
597627
write!(f, "{}", display)
598628
}
599629
}
630+
631+
/// Use Display trait for Debug so that any failing property tests report
632+
/// generated C code rather than the data structures that contain it.
633+
impl fmt::Debug for HeaderC {
634+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
635+
write!(f, "{}", self)
636+
}
637+
}

0 commit comments

Comments
 (0)