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