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)]
17
18 use clap::{Arg, ArgAction, Command};
19 use std::path::PathBuf;
20
21 // Parse CLI argument input for generation range.
parse_generate_range(v: &str) -> Result<usize, String>22 fn parse_generate_range(v: &str) -> Result<usize, String> {
23 match v.parse::<usize>() {
24 Ok(v) => Ok(v),
25 Err(_) => Err(String::from(
26 "Generate range could not be converted to a usize.",
27 )),
28 }
29 }
30
31 // Parse CLI argument input for tests count.
parse_tests_count(v: &str) -> Result<u64, String>32 fn parse_tests_count(v: &str) -> Result<u64, String> {
33 match v.parse::<u64>() {
34 Ok(v) => Ok(v),
35 Err(_) => Err(String::from(
36 "Tests count could not be converted to a usize.",
37 )),
38 }
39 }
40
41 // Parse CLI argument input for fuzzed headers output path.
parse_path(v: &str) -> Result<PathBuf, String>42 fn parse_path(v: &str) -> Result<PathBuf, String> {
43 let path = PathBuf::from(v);
44 match path.is_dir() {
45 true => Ok(path),
46 false => Err(String::from("Provided directory path does not exist.")),
47 }
48 }
49
main()50 fn main() {
51 let matches = Command::new("quickchecking")
52 .version("0.2.0")
53 .about(
54 "Bindgen property tests with quickcheck. \
55 Generate random valid C code and pass it to the \
56 csmith/predicate.py script",
57 )
58 .arg(
59 Arg::new("path")
60 .short('p')
61 .long("path")
62 .value_name("PATH")
63 .help(
64 "Optional. Preserve generated headers for inspection, \
65 provide directory path for header output. [default: None] ",
66 )
67 .action(ArgAction::Set)
68 .value_parser(parse_path),
69 )
70 .arg(
71 Arg::new("range")
72 .short('r')
73 .long("range")
74 .value_name("RANGE")
75 .help(
76 "Sets the range quickcheck uses during generation. \
77 Corresponds to things like arbitrary usize and \
78 arbitrary vector length. This number doesn't have \
79 to grow much for execution time to increase \
80 significantly.",
81 )
82 .action(ArgAction::Set)
83 .default_value("32")
84 .value_parser(parse_generate_range),
85 )
86 .arg(
87 Arg::new("count")
88 .short('c')
89 .long("count")
90 .value_name("COUNT")
91 .help(
92 "Count / number of tests to run. Running a fuzzed \
93 header through the predicate.py script can take a \
94 long time, especially if the generation range is \
95 large. Increase this number if you're willing to \
96 wait a while.",
97 )
98 .action(ArgAction::Set)
99 .default_value("2")
100 .value_parser(parse_tests_count),
101 )
102 .get_matches();
103
104 let output_path = matches.get_one::<PathBuf>("path").map(PathBuf::as_path);
105 let generate_range = *matches.get_one::<usize>("range").unwrap();
106 let tests = *matches.get_one::<u64>("count").unwrap();
107
108 quickchecking::test_bindgen(generate_range, tests, output_path)
109 }
110