Blame test/run

rpm-build 0a0c83
#!/usr/bin/perl -w -U
rpm-build 0a0c83
rpm-build 0a0c83
# Copyright (c) 2007, 2008 Andreas Gruenbacher.
rpm-build 0a0c83
# All rights reserved.
rpm-build 0a0c83
#
rpm-build 0a0c83
# Redistribution and use in source and binary forms, with or without
rpm-build 0a0c83
# modification, are permitted provided that the following conditions
rpm-build 0a0c83
# are met:
rpm-build 0a0c83
# 1. Redistributions of source code must retain the above copyright
rpm-build 0a0c83
#    notice, this list of conditions, and the following disclaimer,
rpm-build 0a0c83
#    without modification, immediately at the beginning of the file.
rpm-build 0a0c83
# 2. The name of the author may not be used to endorse or promote products
rpm-build 0a0c83
#    derived from this software without specific prior written permission.
rpm-build 0a0c83
#
rpm-build 0a0c83
# Alternatively, this software may be distributed under the terms of the
rpm-build 0a0c83
# GNU Public License ("GPL").
rpm-build 0a0c83
#
rpm-build 0a0c83
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
rpm-build 0a0c83
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
rpm-build 0a0c83
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
rpm-build 0a0c83
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
rpm-build 0a0c83
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
rpm-build 0a0c83
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
rpm-build 0a0c83
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
rpm-build 0a0c83
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
rpm-build 0a0c83
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
rpm-build 0a0c83
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
rpm-build 0a0c83
# SUCH DAMAGE.
rpm-build 0a0c83
rpm-build 0a0c83
#
rpm-build 0a0c83
# Possible improvements:
rpm-build 0a0c83
#
rpm-build 0a0c83
# - distinguish stdout and stderr output
rpm-build 0a0c83
# - add environment variable like assignments
rpm-build 0a0c83
# - run up to a specific line
rpm-build 0a0c83
# - resume at a specific line
rpm-build 0a0c83
#
rpm-build 0a0c83
rpm-build 0a0c83
use strict;
rpm-build 0a0c83
use Cwd qw(abs_path);
rpm-build 0a0c83
use FileHandle;
rpm-build 0a0c83
use File::Basename qw(basename dirname);
rpm-build 0a0c83
use File::Path qw(rmtree);
rpm-build 0a0c83
use Getopt::Std;
rpm-build 0a0c83
use POSIX qw(isatty setuid getcwd);
rpm-build 0a0c83
use vars qw($opt_l $opt_v $opt_t);
rpm-build 0a0c83
rpm-build 0a0c83
no warnings qw(taint);
rpm-build 0a0c83
rpm-build 0a0c83
$opt_l = ~0;  # a really huge number
rpm-build 0a0c83
getopts('l:vt:');
rpm-build 0a0c83
rpm-build 0a0c83
my ($OK, $FAILED) = ("ok", "failed");
rpm-build 0a0c83
if (isatty(fileno(STDOUT))) {
rpm-build 0a0c83
	$OK = "\033[32m" . $OK . "\033[m";
rpm-build 0a0c83
	$FAILED = "\033[31m\033[1m" . $FAILED . "\033[m";
rpm-build 0a0c83
}
rpm-build 0a0c83
rpm-build 0a0c83
# Export this dir to tests so they can access data files if needed.
rpm-build 0a0c83
$ENV{"TESTDIR"} = abs_path(dirname($0));
rpm-build 0a0c83
# Add the current dir to PATH so we can find sort-getfattr-output and such.
rpm-build 0a0c83
$ENV{"PATH"} = $ENV{"TESTDIR"} . ":$ENV{PATH}";
rpm-build 0a0c83
# Add the parent dir to PATH so we can find the compiled tools.
rpm-build 0a0c83
$ENV{"PATH"} = dirname(abs_path(dirname($0))) . ":$ENV{PATH}";
rpm-build 0a0c83
$ENV{"TUSER"} = getpwuid($>);
rpm-build 0a0c83
if (!defined($ENV{"TUSER"})) {
rpm-build 0a0c83
	# If the uid isn't found in the private passwd file, just use the
rpm-build 0a0c83
	# uid directly.
rpm-build 0a0c83
	$ENV{"TUSER"} = $>;
rpm-build 0a0c83
}
rpm-build 0a0c83
$ENV{"TGROUP"} = getgrgid($));
rpm-build 0a0c83
if (!defined($ENV{"TGROUP"})) {
rpm-build 0a0c83
	# If the groupid isn't found in the private group file, just use the
rpm-build 0a0c83
	# gid directly.
rpm-build 0a0c83
	my @groups = split(/ /, $();
rpm-build 0a0c83
	$ENV{"TGROUP"} = $groups[0];
rpm-build 0a0c83
}
rpm-build 0a0c83
rpm-build 0a0c83
open(TEST_FILE, $ARGV[0]);
rpm-build 0a0c83
rpm-build 0a0c83
# Create a tempdir to run in for parallel test execution.
rpm-build 0a0c83
my $tmpdir = $ARGV[0] . ".dir";
rpm-build 0a0c83
rmtree($tmpdir);
rpm-build 0a0c83
if (!mkdir($tmpdir)) {
rpm-build 0a0c83
	$tmpdir = getcwd() . "/" . basename($ARGV[0]) . ".dir";
rpm-build 0a0c83
	rmtree($tmpdir);
rpm-build 0a0c83
	mkdir($tmpdir) or die "could not create $tmpdir";
rpm-build 0a0c83
}
rpm-build 0a0c83
my $pretmpdir = getcwd();
rpm-build 0a0c83
chdir($tmpdir) or die "could not enter $tmpdir";
rpm-build 0a0c83
rpm-build 0a0c83
sub exec_test($$);
rpm-build 0a0c83
sub process_test($$$$);
rpm-build 0a0c83
rpm-build 0a0c83
my ($prog, $in, $out) = ([], [], []);
rpm-build 0a0c83
my $prog_line = 0;
rpm-build 0a0c83
my ($tests, $failed) = (0,0);
rpm-build 0a0c83
my $lineno;
rpm-build 0a0c83
my $width = ($ENV{COLUMNS} || 80) >> 1;
rpm-build 0a0c83
rpm-build 0a0c83
for (;;) {
rpm-build 0a0c83
  my $line = <TEST_FILE>; $lineno++;
rpm-build 0a0c83
  if (defined $line) {
rpm-build 0a0c83
    # Substitute %VAR and %{VAR} with environment variables.
rpm-build 0a0c83
    $line =~ s[%(\w+)][$ENV{$1}]eg;
rpm-build 0a0c83
    $line =~ s[%\{(\w+)\}][$ENV{$1}]eg;
rpm-build 0a0c83
  }
rpm-build 0a0c83
  if (defined $line) {
rpm-build 0a0c83
    if ($line =~ s/^\s*< ?//) {
rpm-build 0a0c83
      push @$in, $line;
rpm-build 0a0c83
    } elsif ($line =~ s/^\s*> ?//) {
rpm-build 0a0c83
      push @$out, $line;
rpm-build 0a0c83
    } else {
rpm-build 0a0c83
      process_test($prog, $prog_line, $in, $out);
rpm-build 0a0c83
      last if $prog_line >= $opt_l;
rpm-build 0a0c83
rpm-build 0a0c83
      $prog = [];
rpm-build 0a0c83
      $prog_line = 0;
rpm-build 0a0c83
    }
rpm-build 0a0c83
    if ($line =~ s/^\s*\$ ?//) {
rpm-build 0a0c83
      $prog = [ map { s/\\(.)/$1/g; $_ } split /(?
rpm-build 0a0c83
      $prog_line = $lineno;
rpm-build 0a0c83
      $in = [];
rpm-build 0a0c83
      $out = [];
rpm-build 0a0c83
    }
rpm-build 0a0c83
  } else {
rpm-build 0a0c83
    process_test($prog, $prog_line, $in, $out);
rpm-build 0a0c83
    last;
rpm-build 0a0c83
  }
rpm-build 0a0c83
}
rpm-build 0a0c83
rpm-build 0a0c83
my $status = sprintf("%d commands (%d passed, %d failed)",
rpm-build 0a0c83
	$tests, $tests-$failed, $failed);
rpm-build 0a0c83
if (isatty(fileno(STDOUT))) {
rpm-build 0a0c83
	if ($failed) {
rpm-build 0a0c83
		$status = "\033[31m\033[1m" . $status . "\033[m";
rpm-build 0a0c83
	} else {
rpm-build 0a0c83
		$status = "\033[32m" . $status . "\033[m";
rpm-build 0a0c83
	}
rpm-build 0a0c83
}
rpm-build 0a0c83
print $status, "\n";
rpm-build 0a0c83
rpm-build 0a0c83
# Now clean up the testdir.
rpm-build 0a0c83
chdir($pretmpdir);
rpm-build 0a0c83
rmtree($tmpdir);
rpm-build 0a0c83
rpm-build 0a0c83
exit $failed ? 1 : 0;
rpm-build 0a0c83
rpm-build 0a0c83
rpm-build 0a0c83
sub process_test($$$$) {
rpm-build 0a0c83
  my ($prog, $prog_line, $in, $out) = @_;
rpm-build 0a0c83
rpm-build 0a0c83
  return unless @$prog;
rpm-build 0a0c83
rpm-build 0a0c83
       my $p = [ @$prog ];
rpm-build 0a0c83
       print "[$prog_line] \$ ", join(' ',
rpm-build 0a0c83
             map { s/\s/\\$&/;; $_ } @$p), " -- ";
rpm-build 0a0c83
       my $result = exec_test($prog, $in);
rpm-build 0a0c83
       my @good = ();
rpm-build 0a0c83
       my $nmax = (@$out > @$result) ? @$out : @$result;
rpm-build 0a0c83
       for (my $n=0; $n < $nmax; $n++) {
rpm-build 0a0c83
	   my $use_re;
rpm-build 0a0c83
	   if (defined $out->[$n] && $out->[$n] =~ /^~ /) {
rpm-build 0a0c83
		$use_re = 1;
rpm-build 0a0c83
		$out->[$n] =~ s/^~ //g;
rpm-build 0a0c83
	   }
rpm-build 0a0c83
rpm-build 0a0c83
           if (!defined($out->[$n]) || !defined($result->[$n]) ||
rpm-build 0a0c83
               (!$use_re && $result->[$n] ne $out->[$n]) ||
rpm-build 0a0c83
               ( $use_re && $result->[$n] !~ /^$out->[$n]/)) {
rpm-build 0a0c83
               push @good, ($use_re ? '!~' : '!=');
rpm-build 0a0c83
	   }
rpm-build 0a0c83
	   else {
rpm-build 0a0c83
               push @good, ($use_re ? '=~' : '==');
rpm-build 0a0c83
           }
rpm-build 0a0c83
       }
rpm-build 0a0c83
       my $good = !(grep /!/, @good);
rpm-build 0a0c83
       $tests++;
rpm-build 0a0c83
       $failed++ unless $good;
rpm-build 0a0c83
       print $good ? $OK : $FAILED, "\n";
rpm-build 0a0c83
       if (!$good || $opt_v) {
rpm-build 0a0c83
         for (my $n=0; $n < $nmax; $n++) {
rpm-build 0a0c83
	   my $l = defined($out->[$n]) ? $out->[$n] : "~";
rpm-build 0a0c83
	   chomp $l;
rpm-build 0a0c83
	   my $r = defined($result->[$n]) ? $result->[$n] : "~";
rpm-build 0a0c83
	   chomp $r;
rpm-build 0a0c83
	   print sprintf("%-" . ($width-3) . "s %s %s\n",
rpm-build 0a0c83
			 $r, $good[$n], $l);
rpm-build 0a0c83
         }
rpm-build 0a0c83
       }
rpm-build 0a0c83
}
rpm-build 0a0c83
rpm-build 0a0c83
rpm-build 0a0c83
sub su($) {
rpm-build 0a0c83
  my ($user) = @_;
rpm-build 0a0c83
rpm-build 0a0c83
  $user ||= "root";
rpm-build 0a0c83
rpm-build 0a0c83
  my ($login, $pass, $uid, $gid) = getpwnam($user)
rpm-build 0a0c83
    or return [ "su: user $user does not exist\n" ];
rpm-build 0a0c83
  my @groups = ();
rpm-build 0a0c83
  my $fh = new FileHandle("/etc/group")
rpm-build 0a0c83
    or return [ "opening /etc/group: $!\n" ];
rpm-build 0a0c83
  while (<$fh>) {
rpm-build 0a0c83
    chomp;
rpm-build 0a0c83
    my ($group, $passwd, $gid, $users) = split /:/;
rpm-build 0a0c83
    foreach my $u (split /,/, $users) {
rpm-build 0a0c83
      push @groups, $gid
rpm-build 0a0c83
	if ($user eq $u);
rpm-build 0a0c83
    }
rpm-build 0a0c83
  }
rpm-build 0a0c83
  $fh->close;
rpm-build 0a0c83
rpm-build 0a0c83
  my $groups = join(" ", ($gid, $gid, @groups));
rpm-build 0a0c83
  #print STDERR "[[$groups]]\n";
rpm-build 0a0c83
  $! = 0;  # reset errno
rpm-build 0a0c83
  $> = 0;
rpm-build 0a0c83
  $( = $gid;
rpm-build 0a0c83
  $) = $groups;
rpm-build 0a0c83
  if ($!) {
rpm-build 0a0c83
    return [ "su: $!\n" ];
rpm-build 0a0c83
  }
rpm-build 0a0c83
  if ($uid != 0) {
rpm-build 0a0c83
    $> = $uid;
rpm-build 0a0c83
    #$< = $uid;
rpm-build 0a0c83
    if ($!) {
rpm-build 0a0c83
      return [ "su: $prog->[1]: $!\n" ];
rpm-build 0a0c83
    }
rpm-build 0a0c83
  }
rpm-build 0a0c83
  #print STDERR "[($>,$<)($(,$))]";
rpm-build 0a0c83
  return [];
rpm-build 0a0c83
}
rpm-build 0a0c83
rpm-build 0a0c83
rpm-build 0a0c83
sub sg($) {
rpm-build 0a0c83
  my ($group) = @_;
rpm-build 0a0c83
rpm-build 0a0c83
  my $gid = getgrnam($group)
rpm-build 0a0c83
    or return [ "sg: group $group does not exist\n" ];
rpm-build 0a0c83
  my %groups = map { $_ eq $gid ? () : ($_ => 1) } (split /\s/, $));
rpm-build 0a0c83
  
rpm-build 0a0c83
  #print STDERR "<<", join("/", keys %groups), ">>\n";
rpm-build 0a0c83
  my $groups = join(" ", ($gid, $gid, keys %groups));
rpm-build 0a0c83
  #print STDERR "[[$groups]]\n";
rpm-build 0a0c83
  $! = 0;  # reset errno
rpm-build 0a0c83
  if ($> != 0) {
rpm-build 0a0c83
	  my $uid = $>;
rpm-build 0a0c83
	  $> = 0;
rpm-build 0a0c83
	  $( = $gid;
rpm-build 0a0c83
	  $) = $groups;
rpm-build 0a0c83
	  $> = $uid;
rpm-build 0a0c83
  } else {
rpm-build 0a0c83
	  $( = $gid;
rpm-build 0a0c83
	  $) = $groups;
rpm-build 0a0c83
  }
rpm-build 0a0c83
  if ($!) {
rpm-build 0a0c83
    return [ "sg: $!\n" ];
rpm-build 0a0c83
  }
rpm-build 0a0c83
  print STDERR "[($>,$<)($(,$))]";
rpm-build 0a0c83
  return [];
rpm-build 0a0c83
}
rpm-build 0a0c83
rpm-build 0a0c83
rpm-build 0a0c83
sub exec_test($$) {
rpm-build 0a0c83
  my ($prog, $in) = @_;
rpm-build 0a0c83
  local (*IN, *IN_DUP, *IN2, *OUT_DUP, *OUT, *OUT2);
rpm-build 0a0c83
  my $needs_shell = (join('', @$prog) =~ /[][|<>"'`\$\*\?]/);
rpm-build 0a0c83
rpm-build 0a0c83
  if ($prog->[0] eq "umask") {
rpm-build 0a0c83
    umask oct $prog->[1];
rpm-build 0a0c83
    return [];
rpm-build 0a0c83
  } elsif ($prog->[0] eq "cd") {
rpm-build 0a0c83
    if (!chdir $prog->[1]) {
rpm-build 0a0c83
      return [ "chdir: $prog->[1]: $!\n" ];
rpm-build 0a0c83
    }
rpm-build 0a0c83
    $ENV{PWD} = getcwd;
rpm-build 0a0c83
    return [];
rpm-build 0a0c83
  } elsif ($prog->[0] eq "su") {
rpm-build 0a0c83
    return su($prog->[1]);
rpm-build 0a0c83
  } elsif ($prog->[0] eq "sg") {
rpm-build 0a0c83
    return sg($prog->[1]);
rpm-build 0a0c83
  } elsif ($prog->[0] eq "require_root") {
rpm-build 0a0c83
    my $ret = su("root");
rpm-build 0a0c83
    if ($! != 0) {
rpm-build 0a0c83
      print $ret->[0];
rpm-build 0a0c83
      warn "required root failed; skipping test";
rpm-build 0a0c83
      exit 77;
rpm-build 0a0c83
    }
rpm-build 0a0c83
    return [];
rpm-build 0a0c83
  } elsif ($prog->[0] eq "export") {
rpm-build 0a0c83
    my ($name, $value) = split /=/, $prog->[1];
rpm-build 0a0c83
    # FIXME: need to evaluate $value, so that things like this will work:
rpm-build 0a0c83
    # export dir=$PWD/dir
rpm-build 0a0c83
    $ENV{$name} = $value;
rpm-build 0a0c83
    return [];
rpm-build 0a0c83
  } elsif ($prog->[0] eq "unset") {
rpm-build 0a0c83
    delete $ENV{$prog->[1]};
rpm-build 0a0c83
    return [];
rpm-build 0a0c83
  }
rpm-build 0a0c83
rpm-build 0a0c83
  pipe *IN2, *OUT
rpm-build 0a0c83
    or die "Can't create pipe for reading: $!";
rpm-build 0a0c83
  open *IN_DUP, "<&STDIN"
rpm-build 0a0c83
    or *IN_DUP = undef;
rpm-build 0a0c83
  open *STDIN, "<&IN2"
rpm-build 0a0c83
    or die "Can't duplicate pipe for reading: $!";
rpm-build 0a0c83
  close *IN2;
rpm-build 0a0c83
rpm-build 0a0c83
  open *OUT_DUP, ">&STDOUT"
rpm-build 0a0c83
    or die "Can't duplicate STDOUT: $!";
rpm-build 0a0c83
  pipe *IN, *OUT2
rpm-build 0a0c83
    or die "Can't create pipe for writing: $!";
rpm-build 0a0c83
  open *STDOUT, ">&OUT2"
rpm-build 0a0c83
    or die "Can't duplicate pipe for writing: $!";
rpm-build 0a0c83
  close *OUT2;
rpm-build 0a0c83
rpm-build 0a0c83
  *STDOUT->autoflush();
rpm-build 0a0c83
  *OUT->autoflush();
rpm-build 0a0c83
rpm-build 0a0c83
  if (fork()) {
rpm-build 0a0c83
    # Server
rpm-build 0a0c83
    if (*IN_DUP) {
rpm-build 0a0c83
      open *STDIN, "<&IN_DUP"
rpm-build 0a0c83
        or die "Can't duplicate STDIN: $!";
rpm-build 0a0c83
      close *IN_DUP
rpm-build 0a0c83
        or die "Can't close STDIN duplicate: $!";
rpm-build 0a0c83
    }
rpm-build 0a0c83
    open *STDOUT, ">&OUT_DUP"
rpm-build 0a0c83
      or die "Can't duplicate STDOUT: $!";
rpm-build 0a0c83
    close *OUT_DUP
rpm-build 0a0c83
      or die "Can't close STDOUT duplicate: $!";
rpm-build 0a0c83
rpm-build 0a0c83
    foreach my $line (@$in) {
rpm-build 0a0c83
      #print "> $line";
rpm-build 0a0c83
      print OUT $line;
rpm-build 0a0c83
    }
rpm-build 0a0c83
    close *OUT
rpm-build 0a0c83
      or die "Can't close pipe for writing: $!";
rpm-build 0a0c83
rpm-build 0a0c83
    my $result = [];
rpm-build 0a0c83
    while (<IN>) {
rpm-build 0a0c83
      #print "< $_";
rpm-build 0a0c83
      # remove libtool 'lt-' prefixes on prog name output
rpm-build 0a0c83
      s#^lt-##g;
rpm-build 0a0c83
      if ($needs_shell) {
rpm-build 0a0c83
	s#^/bin/sh: line \d+: ##;
rpm-build 0a0c83
      }
rpm-build 0a0c83
      push @$result, $_;
rpm-build 0a0c83
    }
rpm-build 0a0c83
    return $result;
rpm-build 0a0c83
  } else {
rpm-build 0a0c83
    # Client
rpm-build 0a0c83
    $< = $>;
rpm-build 0a0c83
    close IN
rpm-build 0a0c83
      or die "Can't close read end for input pipe: $!";
rpm-build 0a0c83
    close OUT
rpm-build 0a0c83
      or die "Can't close write end for output pipe: $!";
rpm-build 0a0c83
    close OUT_DUP
rpm-build 0a0c83
      or die "Can't close STDOUT duplicate: $!";
rpm-build 0a0c83
    local *ERR_DUP;
rpm-build 0a0c83
    open ERR_DUP, ">&STDERR"
rpm-build 0a0c83
      or die "Can't duplicate STDERR: $!";
rpm-build 0a0c83
    open STDERR, ">&STDOUT"
rpm-build 0a0c83
      or die "Can't join STDOUT and STDERR: $!";
rpm-build 0a0c83
rpm-build 0a0c83
    if ($needs_shell) {
rpm-build 0a0c83
      exec ('/bin/sh', '-c', join(" ", @$prog));
rpm-build 0a0c83
    } else {
rpm-build 0a0c83
      exec @$prog;
rpm-build 0a0c83
    }
rpm-build 0a0c83
    print STDERR $prog->[0], ": $!\n";
rpm-build 0a0c83
    exit;
rpm-build 0a0c83
  }
rpm-build 0a0c83
}
rpm-build 0a0c83