1//! An application to run property tests for `bindgen` with _fuzzed_ C headers
2//! using `quickcheck`
3//!
4//! ## Usage
5//!
6//! Print help
7//! ```bash
8//! $ cargo run --bin=quickchecking -- -h
9//! ```
10//!
11//! Run with default values
12//! ```bash
13//! $ cargo run --bin=quickchecking
14//! ```
15//!
16#![deny(missing_docs)]
17extern crate clap;
18extern crate quickchecking;
19
20use clap::{App, Arg};
21use std::path::Path;
22
23// Validate CLI argument input for generation range.
24fn validate_generate_range(v: String) -> Result<(), String> {
25    match v.parse::<usize>() {
26        Ok(_) => Ok(()),
27        Err(_) => Err(String::from(
28            "Generate range could not be converted to a usize.",
29        )),
30    }
31}
32
33// Validate CLI argument input for tests count.
34fn validate_tests_count(v: String) -> Result<(), String> {
35    match v.parse::<usize>() {
36        Ok(_) => Ok(()),
37        Err(_) => Err(String::from(
38            "Tests count could not be converted to a usize.",
39        )),
40    }
41}
42
43// Validate CLI argument input for fuzzed headers output path.
44fn validate_path(v: String) -> Result<(), String> {
45    match Path::new(&v).is_dir() {
46        true => Ok(()),
47        false => Err(String::from("Provided directory path does not exist.")),
48    }
49}
50
51fn main() {
52    let matches = App::new("quickchecking")
53        .version("0.2.0")
54        .about(
55            "Bindgen property tests with quickcheck. \
56             Generate random valid C code and pass it to the \
57             csmith/predicate.py script",
58        )
59        .arg(
60            Arg::with_name("path")
61                .short("p")
62                .long("path")
63                .value_name("PATH")
64                .help(
65                    "Optional. Preserve generated headers for inspection, \
66                     provide directory path for header output. [default: None] ",
67                )
68                .takes_value(true)
69                .validator(validate_path),
70        )
71        .arg(
72            Arg::with_name("range")
73                .short("r")
74                .long("range")
75                .value_name("RANGE")
76                .help(
77                    "Sets the range quickcheck uses during generation. \
78                     Corresponds to things like arbitrary usize and \
79                     arbitrary vector length. This number doesn't have \
80                     to grow much for execution time to increase \
81                     significantly.",
82                )
83                .takes_value(true)
84                .default_value("32")
85                .validator(validate_generate_range),
86        )
87        .arg(
88            Arg::with_name("count")
89                .short("c")
90                .long("count")
91                .value_name("COUNT")
92                .help(
93                    "Count / number of tests to run. Running a fuzzed \
94                     header through the predicate.py script can take a \
95                     long time, especially if the generation range is \
96                     large. Increase this number if you're willing to \
97                     wait a while.",
98                )
99                .takes_value(true)
100                .default_value("2")
101                .validator(validate_tests_count),
102        )
103        .get_matches();
104
105    let output_path: Option<&str> = matches.value_of("path");
106    let generate_range: usize =
107        matches.value_of("range").unwrap().parse::<usize>().unwrap();
108    let tests: usize =
109        matches.value_of("count").unwrap().parse::<usize>().unwrap();
110
111    quickchecking::test_bindgen(generate_range, tests, output_path)
112}
113