From 32c82229d93471c25eaaaed5e76704893162a125 Mon Sep 17 00:00:00 2001 From: Leah Neukirchen Date: Fri, 25 Jun 2021 21:24:47 +0200 Subject: add partial test suite --- Makefile | 3 + t/1000-basic.t | 84 +++++++++++++++++++ t/1500-long.t | 85 +++++++++++++++++++ t/2000-expr.t | 252 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ t/dirgen.pl | 14 ++++ t/lib.sh | 15 ++++ t/tap3 | 112 +++++++++++++++++++++++++ t/treegen | 30 +++++++ 8 files changed, 595 insertions(+) create mode 100755 t/1000-basic.t create mode 100755 t/1500-long.t create mode 100755 t/2000-expr.t create mode 100644 t/dirgen.pl create mode 100644 t/lib.sh create mode 100755 t/tap3 create mode 100755 t/treegen diff --git a/Makefile b/Makefile index 5ef6fcd..ce6c7b7 100644 --- a/Makefile +++ b/Makefile @@ -14,6 +14,9 @@ all: $(ALL) clean: FRC rm -f lr +check: FRC all + prove -v >> +. +a +b +c +EOF + +check 'no arguments, recurse' <<'EOF' +treegen +lr +<<< +f:a/b/c +>>> +. +a +a/b +a/b/c +EOF + +check 'simple argument' <<'EOF' +treegen +lr a +<<< +f:a/b/c +>>> +a +a/b +a/b/c +EOF + +check 'simple arguments' <<'EOF' +treegen +lr a d +<<< +f:a/b/c +f:d/e/f +>>> +a +a/b +a/b/c +d +d/e +d/e/f +EOF + +check 'breadth first' <<'EOF' +treegen +lr -B +<<< +f:a/b/c +f:a/d +>>> +. +a +a/b +a/d +a/b/c +EOF + +check 'unsorted' <<'EOF' +treegen +lr -U | sort +<<< +f:a/b/c +f:a/d +>>> +. +a +a/b +a/b/c +a/d +EOF diff --git a/t/1500-long.t b/t/1500-long.t new file mode 100755 index 0000000..cf6da52 --- /dev/null +++ b/t/1500-long.t @@ -0,0 +1,85 @@ +#!/bin/sh +. ./t/lib.sh + +plan 8 + +check 'long output' <<'EOF' +treegen +lr -l +<<< +f:a +f:b +f:c +>>> /drwx------ \d .* \d+-\d+-\d+ \d+:\d+ \.\// +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ a/ +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ b/ +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ c/ +EOF + +check 'no arguments, recurse' <<'EOF' +treegen +lr -l +<<< +f:a/b/c +>>> /drwx------ \d .* \d+-\d+-\d+ \d+:\d+ \.\// +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a/ +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a\/b/ +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ a\/b\/c/ +EOF + +check 'simple argument' <<'EOF' +treegen +lr -l a +<<< +f:a/b/c +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a/ +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a\/b/ +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ a\/b\/c/ +EOF + +check 'simple arguments' <<'EOF' +treegen +lr -l a d +<<< +f:a/b/c +f:d/e/f +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a/ +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a\/b/ +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ a\/b\/c/ +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ d/ +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ d\/e/ +>>> /-rw-r--r-- \d .* \d+-\d+-\d+ \d+:\d+ d\/e\/f/ +EOF + +# NB: two spaces for inode count due to -U +check 'unsorted' <<'EOF' +treegen +lr -l -U +<<< +f:a/b/c +f:a/d +>>> /drwxrwxr-x \d .* \d+-\d+-\d+ \d+:\d+ a/ +EOF + +check 'fifo' <<'EOF' +mkfifo fifo +lr -l +>>> /prw-rw-r-- .* fifo\|/ +EOF + +check 'symlink' <<'EOF' +treegen +lr -l +<<< +f:a/b +l:b:a/c +>>> /lrw.* .* a\/c -> b$/ +EOF + +check 'executable' <<'EOF' +treegen +lr -l +<<< +f:a/b:0:711 +>>> /-rwx--x--x .* a\/b\*$/ +EOF diff --git a/t/2000-expr.t b/t/2000-expr.t new file mode 100755 index 0000000..a564baa --- /dev/null +++ b/t/2000-expr.t @@ -0,0 +1,252 @@ +#!/bin/sh +. ./t/lib.sh + +plan 24 + +check 'parse error detection' <<'EOF' +lr -t 'xyzzy' +>>>2 /parse error: unknown expression/ +>>>= 2 +EOF + +check 'number too big detection' <<'EOF' +lr -t 'size > 99999999999999999999999999999999999999999' +>>>2 /parse error: number too big/ +>>>= 2 +EOF + +check 'unterminated string' <<'EOF' +lr -t 'name == "runaway' +>>>2 /parse error: unterminated string/ +>>>= 2 +EOF + +check 'invalid regex' <<'EOF' +lr -t 'name =~ "[runaway"' +>>>2 /parse error: invalid regex/ +>>>= 2 +EOF + +check 'name' <<'EOF' +treegen +lr -t 'name == "a"' +<<< +f:a +f:b +f:c +>>> +a +EOF + +check 'name, no match' <<'EOF' +treegen +lr -t 'name == "q"' +<<< +f:a +f:b +f:c +>>> +EOF + +check 'name, inner quotes' <<'EOF' +treegen +lr -t 'name == "quo""te"' +<<< +f:quo"te +>>> +quo"te +EOF + +check 'name, environment variable' <<'EOF' +treegen +export NAME=b +lr -t 'name == $NAME' +<<< +f:a +f:b +f:c +>>> +b +EOF + +check 'name =~' <<'EOF' +treegen +lr -t 'name =~ "a|b"' +<<< +f:a +f:b +f:c +>>> +a +b +EOF + +check 'name =~~' <<'EOF' +treegen +lr -t 'name =~~ "a|b"' +<<< +f:A +f:B +f:C +>>> +A +B +EOF + +check 'name ~~' <<'EOF' +treegen +lr -t 'name ~~ "*.c"' +<<< +f:bar.c +f:foo.c +f:foo.h +>>> +bar.c +foo.c +EOF + +check 'name ~~~' <<'EOF' +treegen +lr -t 'name ~~~ "*.c"' +<<< +f:bar.c +f:foo.C +f:foo.h +>>> +bar.c +foo.C +EOF + +check 'type = f' <<'EOF' +treegen +lr -t 'type = f' +<<< +f:a +f:b/c +>>> +a +b/c +EOF + +check 'type != f' <<'EOF' +treegen +lr -t 'type != f' +<<< +f:a +f:b/c +>>> +. +b +EOF + +check 'type = l' <<'EOF' +treegen +lr -t 'type = l' +<<< +f:a +l:a:b +>>> +b +EOF + +check 'parentheses' <<'EOF' +treegen +lr -t '(((type = f)))' +<<< +f:a +f:b/c +>>> +a +b/c +EOF + +check 'logic: or' <<'EOF' +treegen +lr -t 'name == "a" || name == "c"' +<<< +f:a +f:b +f:c +>>> +a +c +EOF + +check 'logic: negation' <<'EOF' +treegen +lr -t 'type = f && !(name == "a" || name == "c")' +<<< +f:a +f:b +f:c +>>> +b +EOF + +check 'logic: and' <<'EOF' +treegen +lr -t 'name ~~ "*a*" && name ~~ "*c*"' +<<< +f:abc +f:ade +f:ebc +>>> +abc +EOF + +check 'size ==' <<'EOF' +treegen +lr -t 'size == 42' +<<< +f:a +f:b:42 +f:c +>>> +b +EOF + +check 'size =' <<'EOF' +treegen +lr -t 'size = 42' +<<< +f:a +f:b:42 +f:c +>>> +b +EOF + +check 'size comparison, I' <<'EOF' +treegen +lr -t 'size > 40 && size < 60' +<<< +f:a +f:b:42 +f:c:90 +>>> +b +EOF + +check 'size comparison, II' <<'EOF' +treegen +lr -t 'size > 1M && size < 1G' +<<< +f:a +f:b:2097152 +f:c:90 +>>> +b +EOF + +check 'size comparison, III' <<'EOF' +treegen +lr -t 'size >= 4k' +<<< +f:a:4095 +f:b:4096 +f:c:4097 +>>> +b +c +EOF + diff --git a/t/dirgen.pl b/t/dirgen.pl new file mode 100644 index 0000000..0431a37 --- /dev/null +++ b/t/dirgen.pl @@ -0,0 +1,14 @@ +#!/usr/bin/env perl -w +use v5.16; + +# [fdl] +while (<>) { + if (/^f:(.*?)(?::(\d+))?$/) { + my $size = $2 || 0; + print "creating file $1 of size $size"; + } elsif (/^d:(.*)$/) { + print "creating dir $1"; + } elsif (/^l:(.*?):(.*)$/) { + print "symlinking $1 -> $2"; + } +} diff --git a/t/lib.sh b/t/lib.sh new file mode 100644 index 0000000..30a16e1 --- /dev/null +++ b/t/lib.sh @@ -0,0 +1,15 @@ +export "PATH=$PWD:$PWD/t:$PATH" + +umask 002 + +plan() { + printf '1..%d\n' "$1" +} + +check() { + export LLVM_PROFILE_FILE=/tmp/cov/lr.$(date +%s%N) + TESTDIR=$(mktemp -d) + cd $TESTDIR + tap3 "$@" + rm -r "$TESTDIR" +} diff --git a/t/tap3 b/t/tap3 new file mode 100755 index 0000000..bfb9bc6 --- /dev/null +++ b/t/tap3 @@ -0,0 +1,112 @@ +#!/usr/bin/env perl +# tap3 [DESC] - check output/error/status of a command against a specification +# +# A tiny variant of shelltestrunner (format v1), just takes one test +# case and outputs a TAP line. +# +# Input format: +# +# CMD +# <<< +# INPUT +# >>> +# OUTPUT +# >>> /OUTPUT REGEX/ +# >>>2 +# STDERR +# >>>2 /STDERR REGEX/ +# >>>= STATUS +# >>>= !STATUS +# +# All but CMD are optional and can be put in any order, +# Regex variants can be repeated, all patterns must match. +# By default, STATUS is set to 0 and STDERR assumed empty. +# +# To the extent possible under law, the creator of this work has waived +# all copyright and related or neighboring rights to this work. +# http://creativecommons.org/publicdomain/zero/1.0/ + +use strict; +use warnings; +use Symbol 'gensym'; +use IPC::Open3; + +my $cmd = ""; +my ($input, $output, @output_rx, $stderr, @stderr_rx, $status, $status_not); +my $ignored = ""; + +my $var = \$cmd; +while () { + if (/^#!? /) { next; } + if (/^<<<$/) { $var = \$input; $input = ""; next; } + if (/^>>>$/) { $var = \$output; $output = ""; next; } + if (/^>>>2$/) { $var = \$stderr; $stderr = ""; next; } + if (/^>>>\s*\/(.*)\/$/) { push @output_rx, $1; next; } + if (/^>>>2\s*\/(.*)\/$/) { push @stderr_rx, $1; next; } + if (/^>>>=\s+(\d+)$/) { $var = \$ignored; $status = $1; next; } + if (/^>>>=\s+!(\d+)$/) { $var = \$ignored; $status_not = $1; next; } + $$var .= $_; +} + +chomp($cmd); +die "No command to check given\n" if !$cmd; + +my ($wtr, $rdr); +my $err = gensym; +my $pid = open3($wtr, $rdr, $err, "/bin/sh", "-c", $cmd); + +my $desc = shift || $cmd; +$desc =~ s/\n.*//g; + +print $wtr $input if (defined($input)); +close $wtr; +my $real_output = do { local $/; <$rdr>; }; +my $real_stderr = do { local $/; <$err>; }; +waitpid($pid, 0); +my $real_status = $? >> 8; + +my $r = 0; + +sub not_ok { + print "not ok - $desc\n" if (!$r); + $r = 1; + $_[0] =~ s/^/# /mg; + print $_[0]; +} + +if (defined($output) && $real_output ne $output) { + not_ok("wrong output:\n$real_output"); +} +for my $rx (@output_rx) { + if ($real_output !~ $rx) { + not_ok("output doesn't match /$rx/:\n$real_output\n"); + } +} +if (defined($stderr) && $real_stderr ne $stderr) { + not_ok("wrong stderr:\n$real_stderr"); +} +for my $rx (@stderr_rx) { + if ($real_stderr !~ $rx) { + not_ok("stderr doesn't match /$rx/:\n$real_stderr\n"); + } +} +if (!defined($stderr) && !@stderr_rx && + !defined($status) && !defined($status_not) && + $real_stderr) { + not_ok("output to stderr:\n$real_stderr\n"); +} +if (defined($status) && $real_status != $status) { + not_ok("wrong status: $real_status (expected $status)\n"); +} +if (defined($status_not) && $real_status == $status_not) { + not_ok("wrong status: $real_status (expected anything else)\n"); +} +if (!defined($status) && !defined($status_not) && + !defined($stderr) && !@stderr_rx && + $real_status != 0) { + not_ok("wrong status: $real_status (command failed)\n"); +} + +print "ok - $desc\n" if (!$r); + +exit $r; diff --git a/t/treegen b/t/treegen new file mode 100755 index 0000000..1b7f0c9 --- /dev/null +++ b/t/treegen @@ -0,0 +1,30 @@ +#!/usr/bin/perl -w +use v5.16; + +use File::Spec; +use File::Path qw(make_path); + +# [fdl] +while (<>) { + if (/^f:(.*?)(?::(\d+))?(?::(\d+))?$/) { + my $path = $1; + my ($_volume, $dir, $_base) = File::Spec->splitpath($path); + my $size = $2 || 0; + my $mode = $3 ? oct($3) : 0644; + + make_path($dir) if $dir; + open(my $out, ">", "$path") or die "can't create $path"; + chmod($mode, $out); + truncate($out, $size); + close($out); + } elsif (/^d:(.*)$/) { + make_path($1, {mode => 0755}); + } elsif (/^l:(.*?):(.*)$/) { + my $target = $1; + my $linkname = $2; + my ($_volume, $dir, $_base) = File::Spec->splitpath($linkname); + + make_path($dir) if $dir; + symlink $target, $linkname; + } +} -- cgit 1.4.1