• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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