#!/bin/sh

#  from ckmame:runtest,v 1.22 2005/12/27 09:41:51 dillo Exp
#
#  runtest -- run regression tests
#  Copyright (C) 2002-2007 Dieter Baron and Thomas Klausner
#
#  This file is part of libzip, a library to manipulate ZIP archives.
#  The authors can be contacted at <libzip@nih.at>
#
#  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.
#  3. The names of the authors may not be used to endorse or promote
#     products derived from this software without specific prior
#     written permission.
# 
#  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.

# runtest TESTNAME
#
# files: 
#   TESTNAME.test: test scenario
#
# test scenario:
#    Lines beginning with # are comments.
#
#    The following commands are recognized; return and args must
#    appear exactly once, the others are optional.
#
#	args ARGS
#	    run program with command line arguments ARGS
#	
#	description TEXT
#	    description of what test is for
#
#	file TEST IN OUT
#	    copy file IN as TEST, compare against OUT after program run.
#
#	file-del TEST IN
#	    copy file IN as TEST, check that it is removed by program.
#
#	file-new TEST OUT
#	    check that file TEST is created by program and compare
#	    against OUT.
#
#	mkdir MODE NAME
#	    create directory NAME with permissions MODE.
#
#	program PRG
#	    run PRG.
#
#	return RET
#	    RET is the expected exit code
#
#	stderr TEXT
#	    program is expected to produce the error message TEXT.  If
#	    multiple stderr commands are used, the messages are
#	    expected in the order given.
#
#	stdout TEXT
#	    program is expected to print TEXT to stdout.  If multiple
#	    stdout commands are used, the messages are expected in
#	    the order given. 
#   
# exit status
#	runtest uses the following exit codes:
#	    0: test passed
#	    1: test failed
#	    2: other error
#	   77: test was skipped
# 
# environment variables:
#   VERBOSE: if set, be more verbose (e. g., output diffs)
#   NOCLEANUP: if set, don't delete directory test is run in

die() {
	echo "$0: $*" >&2;
	cleanup;
	exit 2;
}

fail() {
	if [ ! -z "${VERBOSE}" ]
	then
	    echo "${TEST} -- FAILED: $*";
	fi;
	cleanup;
	exit 1;
}

skip() {
	if [ ! -z "${VERBOSE}" ]
	then
		echo "${TEST} -- skipped: $*";
	fi;
	cleanup;
	exit 77;
}

succeed() {
	if [ ! -z "${VERBOSE}" ]
	then
		echo "${TEST} -- passed";
	fi
	cleanup;
	exit 0;
}

cleanup() {
	cd ..;
	if [ -z "${NOCLEANUP}" ]
	then
		chmod -R u+rw ${DIR};
		rm -r ${DIR};
	fi
}

check_in_out_exists() {
    if [ ! -f "$2" ]
    then
	fail "missing output file: '$2'"
    elif [ ! -f "$1" ]
    then
	die "cannot find input file '$1'"
    fi
}

checkdb() {
    check_in_out_exists "$1" "$2"
    out=`../dbdump "$2" | sort | diff ${DIFF_FLAGS} "$1" -`
    if [ $? -ne 0 ]
    then
	if [ $VERBOSE ]
	then
	    echo "$out"
	fi
	fail "$3"
    fi

}

checkfile() {
    check_in_out_exists "$1" "$2"
    out=`diff ${DIFF_FLAGS} "$1" "$2"`
    if [ $? -ne 0 ]
    then
	if [ $VERBOSE ]
	then
	    echo "$out"
	fi
	fail "$3"
    fi
}

checkzip() {
    check_in_out_exists "$1" "$2"
    # quiet CRC errors
    ${ZIPCMP} -t ${ZIPCMP_FLAGS} "$1" "$2" 2>/dev/null
    if [ $? -ne 0 ]
    then
	fail "$3"
    fi
}

test_empty() {
    if [ ! -z "$1" ]
    then
	die "directive $2 appeared twice in test file"
    fi
}

test_set() {
    if [ -z "$1" ]
    then
	die "required directive $2 missing in test file"
    fi
}

copy_file() {
    src="${srcdir}/$1"

    if [ ! -f "$src" ]
    then
	die "source file '$src' does not exist"
    fi
    dir=`dirname "$2"`
    if [ ! -d "$dir" ]
    then
	mkdir -p "$dir"
    fi
    cp "$src" "$2"
}

# GNU sort behaves differently on locales other than C, breaking tests
LC_ALL=C
export LC_ALL

#testdir=`dirname $1`
TEST=`echo $1 | sed -e s',.*/,,' -e 's/\.test$//'`
shift

DIR=${TEST}.d$$
if [ -z "${srcdir}" ]
then
    srcdir=..
else
    # XXX: fix for absolute srcdir?
    srcdir=../${srcdir}
fi

if [ -z "${ZIPCMP}" ]
then
    ZIPCMP=zipcmp
else
    if expr "${ZIPCMP}" : '[^/].*/' > /dev/null
    then
	ZIPCMP="../${ZIPCMP}"
    fi
fi

if [ -z "${VERBOSE}" ]
then
    DIFF_FLAGS=''
    ZIPCMP_FLAGS='-q'
else
    DIFF_FLAGS='-u'
    ZIPCMP_FLAGS='-v'
fi

# XXX: set up trap to cleanup

mkdir ${DIR} || ( die "cannot create test directory ${DIR}" )
cd ${DIR} || ( die "cannot cd to test directory ${DIR}" )

{

RET=''
ARGS=''
FILES=''
FILES_SHOULD=''
DESCR=''

touch stderr stdout

while read cmd arg
do
  case $cmd in
  \#*) ;;
  args)
    test_empty "${ARGS}" args
    ARGS="$arg";;
  description)
    test_empty "${DESCR}" description
    DESCR="$arg";;
  file)
    set $arg
    copy_file "$2" "$1"
    FILES="${FILES} ${srcdir}/$3!$1";;
  file-del)
    set $arg
    copy_file "$2" "$1";;
  file-new)
    set $arg
    FILES="${FILES} ${srcdir}/$2!$1";;
  mkdir)
    set $arg
    mkdir "$2" && chmod "$1" "$2";;
  program)
    PROGRAM=../"$arg";;
  return)
    test_empty "${RET}" return
    RET="$arg";;
  stderr)
    echo "${PROGRAM}: $arg" >> stderr;;
  stdout)
    echo "$arg" >> stdout;;
  *)
    die "unknown directive '$cmd'"
  esac
done

test_set "${RET}" return
test_set "${ARGS}" args

if [ -z "${PROGRAM}" ]
then
    die no program to run given
fi

if [ ! -z "${SETUP_ONLY}" ]
then
    echo ${DIR}
    exit 0
fi

if [ ! -z "${VERBOSE}" ]
then
	echo "${TEST}: ${DESCR}"
	echo "running: ${PROGRAM} ${ARGS}"
fi

${PROGRAM} ${ARGS} > gotout 2> goterr
ret=$?

if [ $ret -ne ${RET} ]
then
    if [ ! -z "${VERBOSE}" ]
    then
	cat gotout
	cat goterr
    fi
    fail "unexpected exit status: got $ret, expected ${RET}"
fi

FILES_SHOULD="${FILES_SHOULD} stderr stdout gotout goterr"

checkfile stderr goterr "unexpected error output"
checkfile stdout gotout "unexpected output"

if [ ! -z "${FILES}" ]
then
    for fs in ${FILES}
    do
        set -- `echo ${fs} | tr '!' ' '`
	FILES_SHOULD="${FILES_SHOULD} $2"
	case "$2" in
	*.db)
	    checkdb "$1" "$2" "database $2 wrong";;
	*.zip)
	    checkzip "$1" "$2" "zip file $2 wrong";;
	*)
	    checkfile "$1" "$2" "file $2 wrong";;
	esac 
    done
fi

# check that no additional files exist
echo gotfiles shouldfiles ${FILES_SHOULD} | tr ' ' '\012' | sort > shouldfiles
touch gotfiles
find . -type f -print | sed 's!^./!!' | sort > gotfiles

checkfile shouldfiles gotfiles "unexpected/missing files"

succeed

} < ${srcdir}/${TEST}.test
