installed_progs.t: Python checks stdout too, 150 ok
[sunny256-utils.git] / tests / installed_progs.t
blob208d2377e4b4500d5421672c5e263d0a1b0ec6e4
1 #!/usr/bin/env perl
3 #=======================================================================
4 # installed_progs.t
5 # File ID: 3d0e23bc-400e-11e4-a184-c80aa9e67bbd
7 # Check that some necessary programs are installed.
9 # Character set: UTF-8
10 # ©opyleft 2014– Øyvind A. Holm <sunny@sunbase.org>
11 # License: GNU General Public License version 2 or later, see end of
12 # file for legal stuff.
13 #=======================================================================
15 use strict;
16 use warnings;
18 use Test::More;
19 if ($^O ne "linux") {
20 plan skip_all => "We're not on GNU/Linux";
21 } else {
22 plan "no_plan";
25 use Getopt::Long;
27 local $| = 1;
29 our $CMDB = "";
30 our $CMD = "../$CMDB";
32 our %Opt = (
34 'all' => 0,
35 'gui' => 0,
36 'help' => 0,
37 'other' => 0,
38 'quiet' => 0,
39 'todo' => 0,
40 'verbose' => 0,
41 'version' => 0,
45 our $progname = $0;
46 $progname =~ s/^.*\/(.*?)$/$1/;
47 our $VERSION = '0.0.0';
49 my %descriptions = ();
51 Getopt::Long::Configure('bundling');
52 GetOptions(
54 'all|a' => \$Opt{'all'},
55 'gui|g' => \$Opt{'gui'},
56 'help|h' => \$Opt{'help'},
57 'other|o' => \$Opt{'other'},
58 'quiet|q+' => \$Opt{'quiet'},
59 'todo|t' => \$Opt{'todo'},
60 'verbose|v+' => \$Opt{'verbose'},
61 'version' => \$Opt{'version'},
63 ) || die("$progname: Option error. Use -h for help.\n");
65 $Opt{'verbose'} -= $Opt{'quiet'};
66 $Opt{'help'} && usage(0);
67 if ($Opt{'version'}) {
68 print_version();
69 exit(0);
72 exit(main());
74 sub main {
75 # {{{
76 my $Retval = 0;
78 my $Lh = "[0-9a-fA-F]";
79 my $Templ = "$Lh\{8}-$Lh\{4}-$Lh\{4}-$Lh\{4}-$Lh\{12}";
80 my $v1_templ = "$Lh\{8}-$Lh\{4}-1$Lh\{3}-$Lh\{4}-$Lh\{12}";
81 my $v1rand_templ = "$Lh\{8}-$Lh\{4}-1$Lh\{3}-$Lh\{4}-$Lh\[37bf]$Lh\{10}";
82 my $v4_templ = "$Lh\{8}-$Lh\{4}-4$Lh\{3}-[89ab]$Lh\{3}-$Lh\{12}";
84 diag(sprintf('========== Executing %s v%s ==========',
85 $progname, $VERSION));
87 if ($Opt{'todo'} && !$Opt{'all'}) {
88 goto todo_section;
91 =pod
93 testcmd("$CMD command", # {{{
94 <<'END',
95 [expected stdout]
96 END
97 '',
99 'description',
102 # }}}
104 =cut
106 diag("Checking coreutils...");
107 coreutils(qw{
108 arch base32 base64 basename cat chcon chgrp chmod chown chroot cksum
109 comm cp csplit cut date dd df dir dircolors dirname du echo env expand
110 expr factor false fmt fold groups head hostid id install join link ln
111 logname ls md5sum mkdir mkfifo mknod mktemp mv nice nl nohup nproc
112 numfmt od paste pathchk pinky pr printenv printf ptx pwd readlink
113 realpath rm rmdir runcon seq sha1sum sha224sum sha256sum sha384sum
114 sha512sum shred shuf sleep sort split stat stdbuf stty sum sync tac
115 tail tee timeout touch tr true truncate tsort tty uname unexpand uniq
116 unlink uptime users vdir wc who whoami yes
119 diag("Checking important software...");
120 installed('autoconf --version', '/GNU Autoconf/', 'stdout');
121 installed('bash --version', '/^GNU bash/', 'stdout');
122 installed('bc --version', '/^bc \d.*Free Software Foundation/s', 'stdout');
123 installed('cmake --version', '/^cmake version \d/', 'stdout');
124 installed('cmark --version', '/^cmark \d+\.\d+\.\d+/', 'stdout');
125 installed('cronolog --version', '/^cronolog version \d/', 'stderr');
126 installed('ctags --version', '/^(Exuberant|Universal) Ctags \d/', 'stdout');
127 installed('curl --version', '/^curl /', 'stdout');
128 installed('dict --version', '/^dict \d/', 'stdout');
129 installed('echo ABC ZZZ aabel abbel abc bbbe © Å Æ Ø å æ ø → 🤘 | fmt -1 | sort', '/^ABC\nZZZ\naabel\nabbel\nabc\nbbbe\n©\nÅ\nÆ\nØ\nå\næ\nø\n\n🤘\n$/', 'stdout', 'Use C sorting order');
130 installed('find --version', '/GNU findutils/', 'stdout');
131 installed('gadu --version', '/git-annex-utils \d/', 'stdout');
132 installed('gcc --version', '/^gcc /', 'stdout');
133 installed('git --version', '/^git version 2\./', 'stdout');
134 installed('git-annex version', '/^git-annex version: /', 'stdout');
135 installed('gnuplot --version', '/^gnuplot /', 'stdout');
136 installed('gpg --version', '/^gpg.+GnuPG\b/', 'stdout');
137 installed('grep --version', '/GNU grep/', 'stdout');
138 installed('gzip --version', '/^gzip \d/', 'stdout');
139 installed('indent --version', '/GNU indent 2\.2\./', 'stdout');
140 installed('lilypond --version', '/^GNU LilyPond 2/', 'stdout');
141 installed('make --version', '/GNU Make/', 'stdout');
142 installed('mc --version', '/GNU Midnight Commander/', 'stdout');
143 installed('mysql --version', '/^$/', 'stdout', 'MySQL is not installed');
144 installed('ncdu -v', '/^ncdu \d/', 'stdout');
145 installed('perl --version', '/This is perl( |, v)5/', 'stdout');
146 installed('pinfo --version', '/^Przemek\'s Info Viewer /', 'stdout');
147 installed('pv --version', '/^pv \d/', 'stdout');
148 installed('python --version', '/Python (2|3)/', 'both');
149 installed('python3 --version', '/^Python 3/', 'both');
150 installed('recode --version', '/^Free recode \d/', 'stdout');
151 installed('rsync --version', '/^rsync\s+version \d/', 'stdout');
152 installed('screen --version', '/^Screen version \d/', 'stdout');
153 installed('script --version', '/^script .+\butil-linux\b/', 'stdout');
154 installed('sqlite3 --version', '/^3\.[2-4]/', 'stdout');
155 installed('ssh -V', '/OpenSSH/', 'stderr');
156 installed('sshfs --version', '/SSHFS version \d/', 'stdout');
157 installed('tar --version', '/GNU tar\b/', 'stdout');
158 installed('tree --version', '/^tree v\d\./', 'stdout');
159 installed('unzip -v', '/^UnZip \d.*Info-ZIP/', 'stdout');
160 installed('uprecords -v', '/^uprecords \d/', 'stdout');
161 installed('uuid -d ac89d100-5809-11e0-b3ff-00023faf1383', '/2011-03-27 00:32:19\.377792\.0 UTC/', 'stdout', 'OSSP uuid');
162 installed('vim --version', '/VIM - Vi IMproved [89]\../', 'stdout');
163 installed('wget --version', '/GNU Wget/', 'stdout');
164 installed('zip -v', '/This is Zip \d.*Info-ZIP/', 'stdout');
165 repeat_test('uuidgen -r', 100, "^$v4_templ\$");
166 repeat_test('uuidgen -t', 100, "^$v1_templ\$");
168 is(`echo "SELECT json('[\\"a\\", 4, true, { \\"abc\\" :\\"def\\"}]');" | sqlite3 2>/dev/null`,
169 "[\"a\",4,true,{\"abc\":\"def\"}]\n",
170 "sqlite3 has json support",
173 if ($Opt{'other'} || $Opt{'all'}) {
175 diag("Checking other software...");
176 installed('archivemount --version', '/^archivemount version \d/', 'stderr');
177 installed('arj', '/^ARJ\S*? v \d/', 'stdout');
178 installed('asciidoc --version', '/^asciidoc \d/', 'stdout');
179 installed('bison --version', '/^bison\b.+GNU Bison\b/', 'stdout');
180 installed('cdparanoia --version', '/^cdparanoia III/', 'stderr');
181 installed('cpio --version', '/GNU cpio/', 'stdout');
182 installed('dblatex --version', '/^dblatex version \d/', 'stdout');
183 installed('dot -V', '/graphviz version \d/', 'stderr');
184 installed('echo "[{ }]" | json_reformat -m', '/^\[{}+]$/', 'stdout', 'json_reformat');
185 installed('exifprobe -V', '/Program: \'exifprobe\' version \d/', 'stdout');
186 installed('exiftool -ver', '/^\d+\.\d/', 'stdout');
187 installed('fdupes --version', '/^fdupes \d\./', 'stdout');
188 installed('flac --version', '/^flac /', 'stdout');
189 installed('flex --version', '/^flex \d/', 'stdout');
190 installed('fontforge --version', '/^fontforge 20/', 'stdout');
191 installed('fossil version', '/^This is fossil version 2\.3 /', 'stdout');
192 installed('gettext --version', '/GNU gettext/', 'stdout');
193 installed('gpsbabel --version', '/GPSBabel Version \d/', 'stdout');
194 installed('groff --version', '/^GNU groff version \d/', 'stdout');
195 installed('htop --version', '/^htop \d/', 'stdout');
196 installed('iotop --version', '/^iotop \d/', 'stdout');
197 installed('lame --version', '/LAME .* version /', 'stdout');
198 installed('lftp --version', '/^LFTP .+Version \d/', 'stdout');
199 installed('lynx --version', '/^Lynx Version \d/', 'stdout');
200 installed('lzip --version', '/^Lzip \d/i', 'stdout');
201 installed('mftrace --version', '/^mftrace \d\./', 'stdout');
202 installed('mosh --version', '/^mosh \d/', 'stdout');
203 installed('mutt -h', '/^Mutt \d/', 'stdout');
204 installed('ncftp -v', '/Program version:\s+NcFTP /', 'stderr');
205 installed('nmap --version', '/Nmap version /', 'stdout');
206 installed('nodejs --version', '/^v\d+\.\d+\.\d+$/', 'stdout');
207 installed('npm --version', '/^\d+\.\d+\.\d+$/', 'stdout');
208 installed('pandoc --version', '/^pandoc \d\./', 'stdout');
209 installed('pip --version', '/^pip \d/', 'stdout');
210 installed('pip3 --version', '/^pip \d/', 'stdout');
211 installed('psql --version', '/psql \(PostgreSQL\)/', 'stdout');
212 installed('pylint --version', '/^pylint \d/', 'stdout');
213 installed('quilt --version', '/^\d\./', 'stdout');
214 installed('rtorrent -h', '/BitTorrent client version /', 'stdout');
215 installed('rzip --version', '/^rzip version \d/', 'stdout');
216 installed('scriptreplay --help', '/-m, --maxdelay/', 'stdout', 'scriptreplay has -m/--maxdelay');
217 installed('strace -V', '/^strace -- version (\d+|UNKNOWN)/', 'stdout');
218 installed('svn --version', '/svn, version /', 'stdout');
219 installed('task --version', '/^2\.6\.0$/', 'stdout');
220 installed('texi2html --version', '/^\d\./', 'stdout');
221 installed('tig --version', '/^tig version /', 'stdout');
222 installed('tmux -V', '/^tmux \d\./', 'stdout');
223 installed('top -v', '/procps(-ng)?( version)? \d/', 'stdout');
224 installed('trickle -V', '/^trickle: version \d/', 'stderr');
225 installed('unrar --version', '/UNRAR \d/', 'stdout');
226 installed('uuencode --version', '/^uuencode \(GNU sharutils\)/', 'stdout');
227 installed('whois --version', '/^Version \d/', 'stdout');
228 installed('wiggle --version', '/^wiggle v?1\.0/', 'stderr');
229 installed('xmllint --version', '/^xmllint: using libxml version /', 'stderr');
230 installed('xmlto --version', '/^xmlto version \d/', 'stdout');
231 installed('xz --version', '/^xz \(XZ Utils\) \d/s', 'stdout');
232 installed('youtube-dl --version', '/^20\d\d\.\d\d\.\d\d/', 'stdout');
236 if ($Opt{'gui'} || $Opt{'all'}) {
238 diag("Checking graphical software...");
239 installed('abiword --version', '/^\d\.\d+\.\d+/', 'stdout');
240 installed('bash -c "type -p gnome-system-monitor"', '/bin\/gnome-system-monitor$/', 'stdout');
241 installed('celestia --help', '/Usage:.*\bcelestia\b.+OPTION/s', 'stdout');
242 installed('firefox --version', '/Mozilla Firefox \d+\.\d+/', 'stdout');
243 installed('geeqie --version', '/^Geeqie \d\./', 'stderr');
244 installed('gnucash --version', '/GnuCash \d\./', 'stdout');
245 installed('gnumeric --version', '/^gnumeric version /', 'stdout');
246 installed('gource --help', '/Gource v\d/', 'stdout');
247 installed('inkscape -V', '/^Inkscape \d/', 'stdout');
248 installed('mplayer -V', '/^MPlayer2 /', 'stdout');
249 installed('okular --version', '/okular:? \d/i', 'stdout');
250 installed('qemu-system-i386 --version', '/QEMU emulator version \d/', 'stdout');
251 installed('shutter -v', '/^\d+\.\d+(\.\d+)? Rev\.\d+/', 'stdout');
252 installed('ufraw --version', '/^ufraw \d/', 'stderr');
253 installed('vlc --version', '/^VLC version \d/', 'stdout');
254 installed('wireshark --version', '/^wireshark \d/i', 'stdout');
255 installed('x264 --version', '/^x264 \d/', 'stdout');
256 installed('xdot --help', '/Usage:.*\bxdot\b/si', 'stdout');
257 installed('xtightvncviewer -h', '/^TightVNC Viewer /', 'stderr');
261 todo_section:
264 if ($Opt{'all'} || $Opt{'todo'}) {
265 diag('Running TODO tests...'); # {{{
267 TODO: {
269 local $TODO = '';
270 # Insert TODO tests here.
273 # TODO tests }}}
276 diag('Testing finished.');
277 return $Retval;
278 # }}}
279 } # main()
281 sub installed {
282 # {{{
283 my ($Cmd, $Exp, $Std, $Desc) = @_;
284 $Std =~ /^(both|stderr|stdout)$/ ||
285 BAIL_OUT("installed(): $Cmd: Invalid stream: '$Std'");
286 my $Txt = join('',
287 "\"$Cmd\"",
288 defined($Desc)
289 ? " - $Desc"
290 : ''
293 if ($Std eq 'stdout') {
294 like(`$Cmd 2>/dev/null`, $Exp, $Txt);
295 } elsif ($Std eq 'stderr') {
296 like(`$Cmd 2>&1 >/dev/null`, $Exp, $Txt);
297 } else {
298 like(`$Cmd 2>&1`, $Exp, $Txt);
300 return;
301 # }}}
302 } # installed()
304 sub coreutils {
305 # {{{
306 my @progs = @_;
307 my $retval = 0;
308 for my $curr (@progs) {
309 my $cmd = $curr;
310 if ($curr =~ /^(echo|false|kill|pwd|true|printf)$/) {
311 for my $d (qw{ /usr/local/bin /usr/bin /bin }) {
312 if (-x "$d/$curr") {
313 $cmd = "$d/$curr";
314 last;
318 installed("$cmd --version",
319 "/^$curr .*?\\b(coreutils|procps-ng)\\b/",
320 'stdout') || ($retval = 1);
322 return($retval);
323 # }}}
324 } # coreutils()
326 sub repeat_test {
327 # {{{
328 my ($cmd, $count, $regexp) = @_;
329 my $retval = 0;
330 my $erruuid = '';
332 for (my $t = $count; $t && ($retval < 10); $t--) {
333 my $uuid = `$cmd`;
334 $uuid =~ /$regexp/s || ($retval++, $erruuid .= $uuid);
337 is($erruuid, '', "$cmd: Repeat test $count times");
338 return($retval);
339 # }}}
340 } # repeat_test()
342 sub testcmd {
343 # {{{
344 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
345 defined($descriptions{$Desc}) &&
346 BAIL_OUT("testcmd(): '$Desc' description is used twice");
347 $descriptions{$Desc} = 1;
348 my $stderr_cmd = '';
349 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
350 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
351 my $TMP_STDERR = "$CMDB-stderr.tmp";
352 my $retval = 1;
354 if (defined($Exp_stderr)) {
355 $stderr_cmd = " 2>$TMP_STDERR";
357 $retval &= is(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
358 my $ret_val = $?;
359 if (defined($Exp_stderr)) {
360 $retval &= is(file_data($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
361 unlink($TMP_STDERR);
362 } else {
363 diag("Warning: stderr not defined for '$Txt'");
365 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
367 return $retval;
368 # }}}
369 } # testcmd()
371 sub likecmd {
372 # {{{
373 my ($Cmd, $Exp_stdout, $Exp_stderr, $Exp_retval, $Desc) = @_;
374 defined($descriptions{$Desc}) &&
375 BAIL_OUT("likecmd(): '$Desc' description is used twice");
376 $descriptions{$Desc} = 1;
377 my $stderr_cmd = '';
378 my $cmd_outp_str = $Opt{'verbose'} >= 1 ? "\"$Cmd\" - " : '';
379 my $Txt = join('', $cmd_outp_str, defined($Desc) ? $Desc : '');
380 my $TMP_STDERR = "$CMDB-stderr.tmp";
381 my $retval = 1;
383 if (defined($Exp_stderr)) {
384 $stderr_cmd = " 2>$TMP_STDERR";
386 $retval &= like(`$Cmd$stderr_cmd`, $Exp_stdout, "$Txt (stdout)");
387 my $ret_val = $?;
388 if (defined($Exp_stderr)) {
389 $retval &= like(file_data($TMP_STDERR), $Exp_stderr, "$Txt (stderr)");
390 unlink($TMP_STDERR);
391 } else {
392 diag("Warning: stderr not defined for '$Txt'");
394 $retval &= is($ret_val >> 8, $Exp_retval, "$Txt (retval)");
396 return $retval;
397 # }}}
398 } # likecmd()
400 sub file_data {
401 # Return file content as a string {{{
402 my $File = shift;
403 my $Txt;
405 open(my $fp, '<', $File) or return undef;
406 local $/ = undef;
407 $Txt = <$fp>;
408 close($fp);
409 return $Txt;
410 # }}}
411 } # file_data()
413 sub print_version {
414 # Print program version {{{
415 print("$progname $VERSION\n");
416 return;
417 # }}}
418 } # print_version()
420 sub usage {
421 # Send the help message to stdout {{{
422 my $Retval = shift;
424 if ($Opt{'verbose'}) {
425 print("\n");
426 print_version();
428 print(<<"END");
430 Usage: $progname [options]
432 Check for missing necessary programs needed by some scripts.
434 Options:
436 -a, --all
437 Run all tests, also TODOs.
438 -g, --gui
439 Also check for programs that need a graphical environment.
440 -h, --help
441 Show this help.
442 -o/--other
443 Check for other software, programs that aren't essential for a
444 wonderful life.
445 -q, --quiet
446 Be more quiet. Can be repeated to increase silence.
447 -t, --todo
448 Run only the TODO tests.
449 -v, --verbose
450 Increase level of verbosity. Can be repeated.
451 --version
452 Print version information.
455 exit($Retval);
456 # }}}
457 } # usage()
459 sub msg {
460 # Print a status message to stderr based on verbosity level {{{
461 my ($verbose_level, $Txt) = @_;
463 $verbose_level > $Opt{'verbose'} && return;
464 print(STDERR "$progname: $Txt\n");
465 return;
466 # }}}
467 } # msg()
469 __END__
471 # This program is free software; you can redistribute it and/or modify
472 # it under the terms of the GNU General Public License as published by
473 # the Free Software Foundation; either version 2 of the License, or (at
474 # your option) any later version.
476 # This program is distributed in the hope that it will be useful, but
477 # WITHOUT ANY WARRANTY; without even the implied warranty of
478 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
479 # See the GNU General Public License for more details.
481 # You should have received a copy of the GNU General Public License
482 # along with this program.
483 # If not, see L<http://www.gnu.org/licenses/>.
485 # vim: set fenc=UTF-8 ft=perl fdm=marker ts=4 sw=4 sts=4 et fo+=w :