t/examples: skip missing command dependencies
[barcat.git] / t / examples.t
1 #!/usr/bin/env perl
2 use 5.014;
3 use warnings;
4 use re '/ms';
5 use IPC::Run 'run';
6
7 use Test::More;
8 { # silence fail diagnostics because of single caller
9         no warnings 'redefine';
10         sub Test::Builder::_ok_debug {}
11 }
12
13 my %CMDARGS = (
14         ping => '-c 1',
15         'cat \Khttpd/' => '/var/log/apache2/',
16 );
17
18 my $filename = 'barcat';
19 open my $input, '<', $filename
20         or die "Cannot read documentation from $filename script\n";
21
22 local $/ = "\n\n";
23 while (readline $input) {
24 SKIP: {
25         # find scriptlets in the appropriate section
26         /^=head1 EXAMPLES/ ... /^=head1/ or next;
27         /^\h/ or next;  # indented code snippet
28         /\A\h*>/ and next;  # psql prompt
29         chomp;
30         my $cmd = $_;
31         my $ref = "$filename line $.";
32
33         # store curl downloads
34         s{\bcurl (\S*)(?<param>[^|]*)}{
35                 my $url = $1;
36                 my @params = split ' ', $+{param};
37                 my $ext = (
38                         $cmd =~ /\bxml/     ? 'xml'  :
39                         $cmd =~ / jq /      ? 'json' :
40                         $cmd =~ /[=.]csv\b/ ? 'csv'  :
41                                               'txt'
42                 );
43                 my ($domain, $path) = $url =~ m{//([^/]+) .*/ ([^/]*) \z}x;
44                 $path =~ s/\.$ext\z//;
45                 my $cache = join '.', $path =~ tr/./_/r, $domain, $ext;
46                 $cache = "sample/data/$cache";
47                 SKIP: {
48                         -e $cache and skip($url, 1);
49                         ok(defined runres(['curl', '-sS', $url, '-o', $cache, @params]), $url)
50                                 or diag("download at $ref: $@");
51                 }
52                 "cat $cache"
53         }e;
54
55         # compose an identifier from significant parts
56         do {
57                 s/^\h+//;             # indentation
58                 s/\\\n\s*//g;         # line continuations
59                 s/^[(\h]+//;          # subshell
60                 s/^echo\ .*?\|\s*//;  # preceding input
61                 s/'(\S+)[^']*'/$1/g;  # quoted arguments
62                 s/\h*\|.*//;          # subsequent pipes
63                 s/^cat\ (?:\S+\/)?//; # local file
64         } for my $name = $cmd;
65
66         # prepare shell command to execute
67         while (my ($subcmd, $args) = each %CMDARGS) {
68                 $subcmd .= " \\K", $args .= ' ' unless $subcmd =~ m/\\K/;
69                 $cmd =~ s/\b$subcmd/$args/;
70         }
71
72         for my $param ($cmd =~ m{^[(\h]* (\w\S*)}gx) {
73                 $param eq 'cat' or
74                 runres(['which', $param])
75                         or diag("dependency $param missing at $ref\n$cmd"), skip($name, 1);
76         }
77
78         # run and report unexpected results
79         my $output = runres($cmd);
80         ok(!!$output, $name)
81                 or diag("command at $ref\n$cmd\n" . ($@ || 'empty output'));
82         defined $output or next;
83
84         # record output for review
85         my $numprefix = sprintf '%02d', Test::More->builder->current_test;
86         if (open my $record, '>', "sample/out/t$numprefix-$name.txt") {
87                 print {$record} $output;
88         }
89 }}
90
91 sub runres {
92         my ($cmd) = @_;
93         ref $cmd eq 'ARRAY'
94                 or $cmd = [bash => -c => "set -o pipefail\n$cmd"];
95         eval {
96                 run($cmd, \undef, \my $output, \my $error);
97                 die("error message:\n".($error =~ s/^/    /gr)."\n") if $error;
98                 $? == 0 or die "exit status ", $? >> 8, "\n";
99                 return $output;
100         };
101 }
102
103 done_testing();