| File: | bin/fuzz-harness-generator |
| Coverage: | 74.8% |
| line | stmt | bran | cond | sub | time | code |
|---|---|---|---|---|---|---|
| 1 | #!/usr/bin/env perl | |||||
| 2 | ||||||
| 3 | 6 6 6 | 10365 4 96 | use strict; | |||
| 4 | 6 6 6 | 8 4 104 | use warnings; | |||
| 5 | ||||||
| 6 | 6 6 6 | 1689 6 119 | use App::Test::Generator; | |||
| 7 | 6 6 6 | 12 6 8 | use autodie qw(:all); | |||
| 8 | 6 6 6 | 11999 5 168 | use File::Temp; | |||
| 9 | 6 6 6 | 1857 27155 10 | use Getopt::Long qw(GetOptions); | |||
| 10 | 6 6 6 | 1544 108660 1481 | use Pod::Usage; | |||
| 11 | ||||||
| 12 - 60 | =head1 NAME fuzz-harness-generator - Generate fuzzing + corpus-based test harnesses from test schemas =head1 SYNOPSIS fuzz-harness-generator [-r] [-o output_file] input_file fuzz-harness-generator --dry-run input.yaml =head1 DESCRIPTION This tool generates a C<t/fuzz.t> test file that fuzzes and validates a target module's function or method, using both randomized fuzz cases and static corpus cases (Perl or YAML). =head1 OPTIONS =over 4 =item B<--help> Show this help. =item B<--input> The input configuration file =item B<--output> The (optional) output file. =item B<--dry-run> Validate the input configuration and schema extraction without writing any output files or running tests. =item B<--run> Call C<prove> on the output file. C<fuzz-harness-generator -r t/conf/data_text_append.conf> will, therefore, dynamically create and run tests on the C<append> method of L<Data::Text> =item B<--version> Prints the version of L<App::Test::Generator> =back =cut | |||||
| 61 | ||||||
| 62 | 6 | 414133 | my $infile; | |||
| 63 | my $outfile; | |||||
| 64 | 6 | 0 | my $help; | |||
| 65 | 6 | 0 | my $run; | |||
| 66 | 6 | 0 | my $verbose; | |||
| 67 | 6 | 0 | my $version; | |||
| 68 | 6 | 0 | my $dry_run; | |||
| 69 | ||||||
| 70 | 6 | 10 | Getopt::Long::Configure('bundling'); | |||
| 71 | ||||||
| 72 | 6 | 85 | GetOptions( | |||
| 73 | 'help|h' => \$help, | |||||
| 74 | 'input|i=s' => \$infile, | |||||
| 75 | 'dry-run|n' => \$dry_run, | |||||
| 76 | 'output|o=s' => \$outfile, | |||||
| 77 | 'run|r' => \$run, | |||||
| 78 | 'verbose|v' => \$verbose, | |||||
| 79 | 'version|V' => \$version, | |||||
| 80 | ) or pod2usage(2); | |||||
| 81 | ||||||
| 82 | 6 | 2651 | pod2usage(-exitval => 0, -verbose => 1) if($help); | |||
| 83 | ||||||
| 84 | 5 | 7 | if($version) { | |||
| 85 | 1 | 6 | print $App::Test::Generator::VERSION, "\n"; | |||
| 86 | 1 | 23 | exit 0; | |||
| 87 | } | |||||
| 88 | ||||||
| 89 | 4 | 10 | if($infile && @ARGV) { | |||
| 90 | 0 | 0 | pod2usage('Specify input file either as argument or via --input, not both'); | |||
| 91 | } | |||||
| 92 | ||||||
| 93 | 4 | 7 | $infile ||= shift @ARGV or pod2usage('No config file given'); | |||
| 94 | ||||||
| 95 | 4 | 7 | if($dry_run && $run) { | |||
| 96 | 0 | 0 | pod2usage('--dry-run cannot be used with --run'); | |||
| 97 | } | |||||
| 98 | ||||||
| 99 | 4 | 7 | if ($dry_run && $outfile) { | |||
| 100 | 1 | 6 | warn '--dry-run specified; --output will be ignored'; | |||
| 101 | } | |||||
| 102 | ||||||
| 103 | 4 | 161 | if($verbose) { | |||
| 104 | 0 | 0 | $ENV{'TEST_VERBOSE'} = 1; | |||
| 105 | } | |||||
| 106 | ||||||
| 107 | 4 | 11 | if($run && !$outfile) { | |||
| 108 | 0 | 0 | my $fh; | |||
| 109 | 0 | 0 | ($fh, $outfile) = File::Temp::tempfile(); | |||
| 110 | 0 | 0 | close $fh; | |||
| 111 | ||||||
| 112 | 0 | 0 | App::Test::Generator::generate($infile, $outfile); | |||
| 113 | ||||||
| 114 | 0 | 0 | exit system('prove', '-l', $outfile) >> 8; | |||
| 115 | } | |||||
| 116 | ||||||
| 117 | 4 | 11 | if ($dry_run) { | |||
| 118 | 3 | 10 | my ($fh, $tmp) = File::Temp::tempfile(); | |||
| 119 | 3 | 675 | close $fh; | |||
| 120 | ||||||
| 121 | eval { | |||||
| 122 | 3 | 5 | App::Test::Generator::generate($infile, $tmp); | |||
| 123 | 2 | 76 | 1; | |||
| 124 | 3 | 1162 | } or do { | |||
| 125 | 1 | 342 | die "Dry-run failed for $infile: $@"; | |||
| 126 | }; | |||||
| 127 | ||||||
| 128 | 2 | 5 | unlink $tmp; | |||
| 129 | 2 | 671 | print "Dry-run OK: $infile parsed and validated successfully\n"; | |||
| 130 | 2 | 64 | exit 0; | |||
| 131 | } elsif($outfile && -e $outfile && !$run) { | |||||
| 132 | 0 | 0 | warn "Overwriting existing file: $outfile"; | |||
| 133 | } | |||||
| 134 | ||||||
| 135 | 1 | 7 | App::Test::Generator::generate($infile, $outfile); | |||
| 136 | ||||||
| 137 | 1 | 36 | if($outfile) { | |||
| 138 | 1 | 1 | chmod 0755, $outfile if($outfile =~ /\.(pl|cgi)$/); | |||
| 139 | 1 | 2 | if($run) { | |||
| 140 | 0 | 0 | system("prove -l $outfile"); | |||
| 141 | } | |||||
| 142 | } | |||||
| 143 | ||||||
| 144 | 1 | 25 | exit 0; | |||