Convert sbin/mdconfig/tests from prove format tests to ATF format tests

As a side effect...
1. The tests now checks for the root user before continuing with kyua, which is
   more visible than the test being skipped with the TAP protocol
2. The tests work with devices that aren't /dev/md0 by caching the device
   attached during the test to a file, and later use the cached information to
   detach the device in the cleanup routine
3. The tests no longer require perl to run

MFC after: 1 week
PR: 191191
Sponsored by: EMC / Isilon Storage Division
This commit is contained in:
ngie 2014-11-27 06:04:00 +00:00
parent 364f8a339b
commit 244d05e746
6 changed files with 288 additions and 613 deletions

View File

@ -38,6 +38,10 @@
# xargs -n1 | sort | uniq -d;
# done
# 20141126: convert sbin/mdconfig/tests to ATF format tests
OLD_FILES+=usr/tests/sbin/mdconfig/legacy_test
OLD_FILES+=usr/tests/sbin/mdconfig/mdconfig.test
OLD_FILES+=usr/tests/sbin/mdconfig/run.pl
# 20141126: remove xform_ipip decapsulation fallback
OLD_FILES+=usr/include/netipsec/ipip_var.h
# 20141109: faith/faithd removal

View File

@ -2,12 +2,9 @@
TESTSDIR= ${TESTSBASE}/sbin/mdconfig
TAP_TESTS_SH= legacy_test
TAP_TESTS_SH_SED_legacy_test= 's,__PERL__,${TAP_PERL_INTERPRETER},g'
TEST_METADATA.legacy_test+= required_programs="${TAP_PERL_INTERPRETER}"
ATF_TESTS_SH= mdconfig_test
FILESDIR= ${TESTSDIR}
FILES= mdconfig.test
FILES+= run.pl
TEST_METADATA.mdconfig_test+= required_user="root"
.include <bsd.test.mk>

View File

@ -1,47 +0,0 @@
#!/bin/sh
#
# Copyright (c) 2012 Edward Tomasz Napierała <trasz@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
# This is a wrapper script to run mdconfig.test.
echo "1..1"
if [ `whoami` != "root" ]; then
echo "ok 1 # skip You need to be root to run this test."
exit 0
fi
TESTDIR=$(dirname $(realpath $0))
__PERL__ -w -U $TESTDIR/run.pl $TESTDIR/mdconfig.test > /dev/null
if [ $? -eq 0 ]; then
echo "ok 1"
else
echo "not ok 1"
fi

View File

@ -1,231 +0,0 @@
# Copyright (c) 2012 Edward Tomasz Napierała <trasz@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
# This is a test for mdconfig(8) functionality. Run it as root:
#
# /usr/src/tools/regression/mdconfig/run /usr/src/tools/regression/mdconfig/mdconfig.test
#
# WARNING: Creates files in unsafe way.
$ whoami
> root
$ umask 022
$ truncate -s 1gb xxx
$ mdconfig -l
$ mdconfig -af xxx
> md0
# This awk thing is to strip the file path.
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 vnode 1024M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 2097152 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Check different valid syntax variations: implicit -a.
$ mdconfig xxx
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 vnode 1024M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 2097152 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Explicit -t vnode.
$ mdconfig -a -t vnode -f xxx
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 vnode 1024M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 2097152 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Size for vnodes - smaller than the actual file.
$ mdconfig -a -t vnode -f xxx -s 128m
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 vnode 128M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 134217728 # mediasize in bytes (128M)
> 262144 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Size for vnodes - larger than the actual file.
$ mdconfig -a -t vnode -f xxx -s 128g
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 vnode 128G
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 137438953472 # mediasize in bytes (128G)
> 268435456 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Sector size for vnodes.
$ mdconfig -a -t vnode -f xxx -S 2048
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 vnode 1024M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 2048 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 524288 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Malloc type.
$ mdconfig -a -t malloc -s 1g
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 malloc 1024M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 2097152 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Swap type.
$ mdconfig -a -t swap -s 1g
> md0
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 swap 1024M
$ diskinfo -v /dev/md0 | expand
> /dev/md0
> 512 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 2097152 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 0
# Attaching with a specific unit number.
$ mdconfig -as 1g -u 42
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md42 swap 1024M
$ diskinfo -v /dev/md42 | expand
> /dev/md42
> 512 # sectorsize
> 1073741824 # mediasize in bytes (1.0G)
> 2097152 # mediasize in sectors
> 0 # stripesize
> 0 # stripeoffset
>
$ mdconfig -du 42
# Querying.
$ mdconfig -as 1g
> md0
$ mdconfig -as 2g -u 42
$ mdconfig -lv | awk '{ print $1, $2, $3 }'
> md0 swap 1024M
> md42 swap 2048M
$ mdconfig -lvu 0 | awk '{ print $1, $2, $3 }'
> md0 swap 1024M
$ mdconfig -lvu 42 | awk '{ print $1, $2, $3 }'
> md42 swap 2048M
$ mdconfig -lvu 24 | awk '{ print $1, $2, $3 }'
$ mdconfig -du 42
$ mdconfig -du 0
$ rm xxx

View File

@ -0,0 +1,281 @@
# Copyright (c) 2012 Edward Tomasz Napierała <trasz@FreeBSD.org>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $FreeBSD$
#
check_diskinfo()
{
local md=$1
local mediasize_in_bytes=$2
local mediasize_in_sectors=$3
local sectorsize=${4:-512}
local stripesize=${5:-0}
local stripeoffset=${6:-0}
atf_check -s exit:0 \
-o match:"/dev/$md *$sectorsize *$mediasize_in_bytes *$mediasize_in_sectors *$stripesize *$stripeoffset" \
-x "diskinfo /dev/$md | expand"
}
cleanup_common()
{
if [ -f mdconfig.out ]; then
mdconfig -d -u $(sed -e 's/md//' mdconfig.out)
fi
}
atf_test_case attach_vnode_non_explicit_type cleanup
attach_vnode_non_explicit_type_head()
{
atf_set "descr" "Tests out -a / -f without -t"
}
attach_vnode_non_explicit_type_body()
{
local md
local size_in_mb=1024
atf_check -s exit:0 -x "truncate -s ${size_in_mb}m xxx"
atf_check -s exit:0 -o save:mdconfig.out -x 'mdconfig -af xxx'
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "1073741824" "2097152"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md vnode ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_vnode_non_explicit_type_cleanup()
{
cleanup_common
}
atf_test_case attach_vnode_implicit_a_f cleanup
attach_vnode_implicit_a_f_head()
{
atf_set "descr" "Tests out implied -a / -f without -t"
}
attach_vnode_implicit_a_f_body()
{
local md
local size_in_mb=1024
atf_check -s exit:0 -x "truncate -s ${size_in_mb}m xxx"
atf_check -s exit:0 -o save:mdconfig.out -x 'mdconfig xxx'
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "1073741824" "2097152"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md vnode ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_vnode_implicit_a_f_cleanup()
{
cleanup_common
}
atf_test_case attach_vnode_explicit_type cleanup
attach_vnode_explicit_type_head()
{
atf_set "descr" "Tests out implied -a / -f with -t vnode"
}
attach_vnode_explicit_type_body()
{
local md
local size_in_mb=1024
atf_check -s exit:0 -x "truncate -s ${size_in_mb}m xxx"
atf_check -s exit:0 -o save:mdconfig.out -x 'mdconfig -af xxx -t vnode'
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "1073741824" "2097152"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md vnode ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_vnode_explicit_type_cleanup()
{
[ -f mdconfig.out ] && mdconfig -d -u $(sed -e 's/md//' mdconfig.out)
rm -f mdconfig.out xxx
}
atf_test_case attach_vnode_smaller_than_file cleanup
attach_vnode_smaller_than_file_head()
{
atf_set "descr" "Tests mdconfig -s with size less than the file size"
}
attach_vnode_smaller_than_file_body()
{
local md
local size_in_mb=128
atf_check -s exit:0 -x "truncate -s 1024m xxx"
atf_check -s exit:0 -o save:mdconfig.out \
-x "mdconfig -af xxx -s ${size_in_mb}m"
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "134217728" "262144"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md vnode ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_vnode_smaller_than_file_cleanup()
{
cleanup_common
}
atf_test_case attach_vnode_larger_than_file cleanup
attach_vnode_larger_than_file_head()
{
atf_set "descr" "Tests mdconfig -s with size greater than the file size"
}
attach_vnode_larger_than_file_body()
{
local md
local size_in_gb=128
atf_check -s exit:0 -x "truncate -s 1024m xxx"
atf_check -s exit:0 -o save:mdconfig.out \
-x "mdconfig -af xxx -s ${size_in_gb}g"
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "137438953472" "268435456"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md vnode ${size_in_gb}G$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_vnode_larger_than_file_cleanup()
{
cleanup_common
}
atf_test_case attach_vnode_sector_size cleanup
attach_vnode_sector_size_head()
{
atf_set "descr" "Tests mdconfig -s with size greater than the file size"
}
attach_vnode_sector_size_body()
{
local md
local size_in_mb=1024
atf_check -s exit:0 -x "truncate -s ${size_in_mb}m xxx"
atf_check -s exit:0 -o save:mdconfig.out \
-x "mdconfig -af xxx -S 2048"
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "1073741824" "524288" "2048"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md vnode ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_vnode_sector_size_cleanup()
{
cleanup_common
}
atf_test_case attach_malloc cleanup
attach_malloc_head()
{
atf_set "descr" "Tests mdconfig with -t malloc"
}
attach_malloc_body()
{
local md
local size_in_mb=1024
atf_check -s exit:0 -o save:mdconfig.out \
-x 'mdconfig -a -t malloc -s 1g'
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "1073741824" "2097152"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md malloc ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_malloc_cleanup()
{
cleanup_common
}
atf_test_case attach_swap cleanup
attach_swap_head()
{
atf_set "descr" "Tests mdconfig with -t swap"
}
attach_swap_body()
{
local md
local size_in_mb=1024
atf_check -s exit:0 -o save:mdconfig.out \
-x 'mdconfig -a -t swap -s 1g'
md=$(cat mdconfig.out)
atf_check -s exit:0 -o match:'^md[0-9]+$' -x "echo $md"
check_diskinfo "$md" "1073741824" "2097152"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md swap ${size_in_mb}M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_swap_cleanup()
{
cleanup_common
}
atf_test_case attach_with_specific_unit_number cleanup
attach_with_specific_unit_number_head()
{
atf_set "descr" "Tests mdconfig with a unit specified by -u"
}
attach_with_specific_unit_number_body()
{
local md_unit=99
local size_in_mb=10
local md="md${md_unit}"
echo "$md" > mdconfig.out
atf_check -s exit:0 -o empty \
-x "mdconfig -a -t malloc -s ${size_in_mb}m -u $md_unit"
check_diskinfo "$md" "10485760" "20480"
# This awk strips the file path.
atf_check -s exit:0 -o match:"^$md malloc "$size_in_mb"M$" \
-x "mdconfig -lv | awk '\$1 == \"$md\" { print \$1, \$2, \$3 }'"
}
attach_with_specific_unit_number_cleanup()
{
cleanup_common
}
atf_init_test_cases()
{
atf_add_test_case attach_vnode_non_explicit_type
atf_add_test_case attach_vnode_explicit_type
atf_add_test_case attach_vnode_smaller_than_file
atf_add_test_case attach_vnode_larger_than_file
atf_add_test_case attach_vnode_sector_size
atf_add_test_case attach_malloc
atf_add_test_case attach_swap
atf_add_test_case attach_with_specific_unit_number
}

View File

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