#! /bin/sh

#
# unpackfs-test                                                (jh,05.02.2006)
#

#
#   Copyright (C) 2004, 2005, 2006  Jochen Hepp <jochen.hepp@gmx.de>
#
#   This file is part of unpackfs.
#
#   unpackfs is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   unpackfs is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
#

script="${0##*/}"
cmd="$PWD/../src/${script%-test}"
fsname="${cmd##*/}"
tests=


# --- print_usage ---

print_usage () {
	cat <<-EOF
Usage: $script [OPTIONS]

Options:
       --dir=DIR         directory with subdirectory ref for output references
       --cmd=CMD         command unpackfs
       --config=FILE     configuration file for magic unpacking
       -h  --help        display this help and exit
EOF
}


# --- log_wait_until ---

log_wait_until () { # msg test
	local msg="$1"
	local test="$2"
	local msg_wait
	local r
	local i

	if eval "$test"; then
		r='ok'
	else
		r=''
		msg_wait="$(awk -v n="$msg.." 'BEGIN { \
		            r=""; l=length(n)+8; \
		            while (l<40) { l++; r=r "." }
		            print r " waiting" }')"
		printf '%s\r' "$msg..$msg_wait"

		for i in 1 2 3 4 5 6 7 8 9; do
			sleep 1
			if eval "$test"; then
				r='ok'
				break
			fi
			printf '%s\r' "$msg $i$msg_wait"
		done
	fi

	if [ "$r" ]; then
		true
	else
		false
	fi
}


# --- make_tmpdir ---

make_tmpdir () {
	local dir

	dir="$(mktemp -d -t $script.XXXXXX)"
	if [ -z "$dir" ]; then
		echo "$script: can't create temporary directory" >&2
		exit 1
	fi
	echo "$dir"
}


# --- init ---

init () { # [DIR] [TYPE=file,perm,time,content] ...
          # DIR:=(mount|root|cache|tmp)
          # see create () for TYPE=file,perm,time,content
          # global: mountdir, rootdir, cachedir, tmpsubdir
	local dir
	local mounted

	mkdir -p "$mountdir" "$rootdir" "$cachedir" "$tmpsubdir"
	dir="$rootdir"
	mounted=''

	while [ $# -gt 0 ]; do
		case "$1" in
			root:)
				dir="$rootdir"
				;;
			cache:)
				dir="$cachedir"
				;;
			tmp:)
				dir="$tmpsubdir"
				;;
			mount:)
				if [ ! "$mounted" ]; then
					mount_unpackfs
					mounted='yes'
				fi
				dir="$mountdir"
				;;
			*)
				create "$dir" "$1"
				;;
		esac
		shift
	done
}


# --- mount_unpackfs ---

mount_unpackfs () { # [mountdir], [rootdir], [cachedir], [config]
                    # global: mountdir, rootdir, cachedir, config
                    # global: cmd, err, out
	local mdir="$1"
	local rdir="$2"
	local cdir="$3"
	local conf="$4"
	local mnt
	local r

	if [ -z "$mdir" ]; then
		mdir="$mountdir"
	fi
	if [ -z "$rdir" ]; then
		rdir="$rootdir"
	fi
	if [ $# -lt 3 ]; then
		cdir="$cachedir"
	fi
	if [ -z "$conf" ]; then
		conf="$config"
	fi

	for mnt in /proc/mounts /etc/mtab; do
		if grep -q "^$fsname $mdir fuse" "$mnt"; then
			echo "$script: $fsname is already mounted in $mnt" >>"$err"
		fi
	done

	if [ -z "$cdir" ]; then
		[ -x "$cmd" ] && \
		"$cmd" "$mdir" -u "$rdir" -c "$conf" >>"$out" >>"$err"
	else
		[ -x "$cmd" ] && \
		"$cmd" "$mdir" -u "$rdir" -t "$cdir" -c "$conf" >>"$out" >>"$err"
	fi
	r=$?

	for mnt in /proc/mounts /etc/mtab; do
		if ! log_wait_until 'mounting unpackfs' \
		     "grep -q \"^$fsname $mdir fuse\" \"$mnt\"" >&3; then
			echo "$script: $fsname is not mounted in "$mnt"" >>"$err"
		fi
	done

	return $r
}


# --- unmount_unpackfs ---

unmount_unpackfs () { # [mountdir]   global: mountdir, err, out
	local mdir="$1"
	local mnt

	if [ -z "$mdir" ]; then
		mdir="$mountdir"
	fi

	fusermount -u "$mdir" >>"$out" 2>>"$err"

	for mnt in /proc/mounts /etc/mtab; do
		if ! log_wait_until 'unmounting unpackfs' \
		     "! grep -q \"^$fsname $mdir fuse\" \"$mnt\"" >&3; then
			fusermount -u -z "$mdir" 2>/dev/null 1>&2
			echo "$script: $fsname is still mounted in $mnt" >>"$err"
		fi
	done

	if [ -d "$mdir" ]; then
		if ! log_wait_until 'removing mountpoint' \
		     "test ! -d \"$mdir\" || rmdir \"$mdir\" 2>/dev/null" >&3;
		then
			echo "$script: unable to remove mountpoint" >>"$err"
			echo "$script: aborting further tests - maybe try again" >>"$err"
			echo 'Aborting further tests - maybe try again' \
			     >"$tmpdir/stop-running"
		fi
	fi
}


# --- create ---

create () { # dir TYPE=file,perm,time,content
            #     TYPE:=file|link|dir|block|char|uchar|fifo|
            #           pack|stat|readdir|touch
            #     content is mayor_minor when TYPE is block, char or uchar
            #     content is file[_file_...] when TYPE is pack
	local dir="$1"
	local arg="$2"
	local type; local file; local perm; local time; local content
	local dir_file
	local d

	arg="$(echo "$arg" | sed 's/ /_/g; s/[=,]/ /g')"
	set -- $arg
	type="$1"
	file="$(echo "$2" | sed 's/_/ /g')"
	perm="$3"
	time="${4:-0}"
	content="$(echo "$5" | sed 's/_/ /g')"

	dir_file="$dir/$file"
	d="${dir_file%/*}"
	if [ ! -d "$d" ]; then
		mkdir -p "$d"
	fi

	case "$type" in
		file)
			echo "$content" >"$dir_file" 2>>"$err"
			chmod "$perm" "$dir_file" 2>>"$err"
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		link)
			ln -s "$content" "$dir_file" 2>>"$err"
			;;
		dir)
			mkdir -p -m "$perm" "$dir_file" 2>>"$err"
			;;
		block)
			mknod -m "$perm" "$dir_file" b $content 2>>"$err"
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		char)
			mknod -m "$perm" "$dir_file" c $content 2>>"$err"
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		uchar)
			mknod -m "$perm" "$dir_file" u $content 2>>"$err"
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		fifo)
			mkfifo -m "$perm" "$dir_file" 2>>"$err"
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		pack)
			d="$PWD"
			cd "$tmpsubdir"
			case "$file" in
				*.tar.gz) tar -czf "$dir_file" $content ;;
				*.gz)     gzip -c >"$dir_file" $content ;;
				*.tar)    tar -cf  "$dir_file" $content ;;
				*)
					echo "$script: $file: can't create this packer type" >&2
					exit 1
					;;
			esac
			cd "$d"
			chmod "$perm" "$dir_file" 2>>"$err"
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		stat)
			stat "$dir_file" >/dev/null 2>>"$err"
			;;
		readdir)
			ls -1 "$dir_file" >/dev/null 2>>"$err"
			;;
		touch)
			touch -d $((20000101+$time)) "$dir_file" 2>>"$err"
			;;
		*)
			echo "$script: $type: can't create this type" >&2
			exit 1
			;;
	esac
}


# --- listdirs ---

listdirs () { # dirs   global: tmpdir
	local file

	(
		cd "$tmpdir" && \
		find "$@" -noleaf | \
		LC_ALL=C sort | \
		while read file; do
			ls -1 -d -g --no-group --time-style=+%Y.%m.%d "$file"
		done | \
		sed 's/[[:blank:]]\+/ /g; s/^\([^ ]\+\) [0-9]\+ /\1 /;
		     s/^\([^ ]\+ [^ ]\+ [^ ]\+ \(root\|cache\)\)-long/\1/;
		     /\(.gz\|.tar\)$/ {
		        s/^\([^ ]\+\) [1-9][0-9]* /\1 not_null /;
		        s/^\([^ ]\+ [^ ]\+\) [^ ]\+ /\1 /;
		     }
		     s/^\([dl][^ ]\+\) [^ ]\+ [^ ]\+ /\1 /'
	)
}


# --- clean ---

clean () { # global: mountdir, config, rootdir, cachedir, tmpsubdir
	if [ -d "$mountdir" ]; then
		rmdir "$mountdir"
	fi
	if [ -f "$tmpdir/config" ]; then
		rm "$tmpdir/config"
	fi
	if [ -d "$tmpdir/config" ]; then
		rmdir "$tmpdir/config"
	fi
	if [ -d "$rootdir" ]; then
		rm -rf "$rootdir"
	fi
	if [ -d "$cachedir" ]; then
		rm -rf "$cachedir"
	fi
	if [ -d "$tmpsubdir" ]; then
		rm -rf "$tmpsubdir"
	fi
}


# --- diff_sed2nd ---

diff_sed2nd () { # opt old new   global: tmpdir, fsname
	sed "s%$fsname%unpackfs%; s%$tmpdir%/\$tmpdir%g" "$3" | \
	diff "$1" "$2" -
}


# --- diff_content ---

diff_content () {
	echo "$2" | \
	diff "$1" - "$3"
}


# --- diff_listmount ---

diff_listmount () {
	listdirs "${mountdir##*/}" | \
	diff "$1" "$2" -
}


# --- diff_listdirs ---

diff_listdirs () {
	listdirs "${rootdir##*/}" "${cachedir##*/}" | \
	diff "$1" "$2" -
}


# --- pipe_result_unmount ---

pipe_result_unmount () {
	local r=$?
	unmount_unpackfs
	return $r
}


# --- tests ---

tests="$tests error_missing_mountpoint_s"
error_missing_mountpoint_s () {
	[ -x "$cmd" ] && ! "$cmd" >"$out" 2>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_missing_mountpoint" "$err" >"$diff"
}

tests="$tests error_unknown_option_s"
error_unknown_option_s () {
	[ -x "$cmd" ] && ! "$cmd" --unknown >"$out" 2>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_unknown_option" "$err" >"$diff"
}

tests="$tests error_opt_o_allow_s"
error_opt_o_allow_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$config" \
	         -o allow_root,allow_other >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u "$ref/error_opt_o_allow" "$err" >"$diff"
}

tests="$tests error_opt_u_missing_arg_s"
error_opt_u_missing_arg_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u -t "$cachedir" -c "$config" >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && sed "s%$fsname%unpackfs%; 1 s/u\$/X/" "$err" | \
	diff -u "$ref/error_opt_x_missing_arg" - >"$diff"
}

tests="$tests error_opt_t_missing_arg_s"
error_opt_t_missing_arg_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$rootdir" -t -c "$config" >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && sed "s%$fsname%unpackfs%; 1 s/t\$/X/" "$err" | \
	diff -u "$ref/error_opt_x_missing_arg" - >"$diff"
}

tests="$tests error_opt_c_missing_arg_s"
error_opt_c_missing_arg_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && sed "s%$fsname%unpackfs%; 1 s/c\$/X/" "$err" | \
	diff -u "$ref/error_opt_x_missing_arg" - >"$diff"
}

tests="$tests error_opt_u_no_dir_s"
error_opt_u_no_dir_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$tmpdir/nowhere" -t "$cachedir" -c "$config" \
	         >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u "$ref/error_no_dir" "$err" >"$diff"
}

tests="$tests error_opt_t_no_dir_s"
error_opt_t_no_dir_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$rootdir" -t "$tmpdir/nowhere" -c "$config" \
	         >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u "$ref/error_no_dir" "$err" >"$diff"
}

tests="$tests error_opt_c_no_config_s"
error_opt_c_no_config_s () {
	init
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/nowhere" \
	         >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u "$ref/error_no_config" "$err" >"$diff"
}

tests="$tests error_conf_is_dir_s"
error_conf_is_dir_s () {
	init
	mkdir "$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	! "$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	         >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u "$ref/error_conf_is_dir" "$err" - >"$diff"
}

tests="$tests error_conf_none_too_few_args_s"
error_conf_none_too_few_args_s () {
	init
	echo 'testpacker -' >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_too_few_args" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_none_too_many_args_s"
error_conf_none_too_many_args_s () {
	init
	echo 'testpacker none "magic" .n too_much' >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_too_many_args" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_unknown_packer_s"
error_conf_unknown_packer_s () {
	init
	echo 'testpacker special "magic"' >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_unknown_packer" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_too_few_args_s"
error_conf_too_few_args_s () {
	init
	echo "testpacker - \"magic\" .ext|.e \"$0\"" \
	     >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_too_few_args" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_packer_no_cmd_s"
error_conf_packer_no_cmd_s () {
	init
	echo 'testpacker - "magic" .ext|.e "" $source' \
	     >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_packer_no_cmd" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_packer_not_found_s"
error_conf_packer_not_found_s () {
	init
	echo 'testpacker - "magic" .ext|.e "/bin-nowhere/testpacker" $source' \
	     >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_packer_not_found" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_packer_no_src_s"
error_conf_packer_no_src_s () {
	init
	echo "testpacker - \"magic\" .ext|.e \"$0\" no_source" \
	     >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_packer_no_src" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests error_conf_packer_no_ext_s"
error_conf_packer_no_ext_s () {
	init
	echo "testpacker - \"magic\" '' \"$0\" \$source" \
	     >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u "$ref/error_conf_packer_no_ext" "$err" >"$diff"
	pipe_result_unmount
}

tests="$tests conf_comment_in_line_s"
conf_comment_in_line_s () {
	init
	echo 'testpacker none "magic" # comment' >"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ]
	pipe_result_unmount
}

tests="$tests fusermount_s"
fusermount_s () {
	fusermount -v >"$out" 2>"$err" && \
	sed '1 s/ .*$//' "$out" | \
	diff -u "$ref/fusermount" - >"$diff" && [ ! -s "$err" ]
}

tests="$tests show_root"
show_root () {
	init root: file=test,644,0,content \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/mount_test" >"$diff"
	pipe_result_unmount
}

tests="$tests show_cache"
show_cache () {
	init cache: file=test,644,0,content \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/mount_test" >"$diff"
	pipe_result_unmount
}

tests="$tests show_root_prefer"
show_root_prefer () {
	init root:  file=test,644,0,content \
	     cache: file=test,644,1,other \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/mount_test" >"$diff"
	pipe_result_unmount
}

tests="$tests show_both"
show_both () {
	init root:  file=test,644,0,content \
	     cache: file=other,644,1,other \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/mount_test_other" >"$diff"
	pipe_result_unmount
}

tests="$tests open_root"
open_root () {
	init root: file=test,644,0,content \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_content -u content "$mountdir/test" >"$diff"
	pipe_result_unmount
}

tests="$tests open_cache"
open_cache () {
	init cache: file=test,644,0,content \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_content -u content "$mountdir/test" >"$diff"
	pipe_result_unmount
}

tests="$tests open_root_prefer"
open_root_prefer () {
	init root:  file=test,644,0,content \
	     cache: file=test,644,1,other \
	     mount:
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_content -u content "$mountdir/test" >"$diff"
	pipe_result_unmount
}

tests="$tests new_root"
new_root () {
	init mount: file=test,644,0,content
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/root_test" >"$diff"
	pipe_result_unmount
}

tests="$tests new_cache"
new_cache () {
	init cache: dir=dir,755 \
	     mount: file=dir/test,644,0,content
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/cache_dir_test" >"$diff"
	pipe_result_unmount
}

tests="$tests new_autocache_s"
new_autocache_s () {
	local tmpdir="$TMPDIR"
	local tmpdir_set=
	local r
	if set | grep -q '^TMPDIR='; then tmpdir_set=yes; fi
	init
	export TMPDIR="$tmpsubdir"
	mount_unpackfs "$mountdir" "$rootdir" '' "$config"
	r=$?
	if [ "$tmpdir_set" ]; then	TMPDIR="$tmp"; else unset TMPDIR; fi
	export TMPDIR
	ls "$mountdir" >/dev/null 2>>"$err" && \
	[ $r = 0 ] && \
	tmpdir="$(ls -1 "$tmpsubdir" 2>>"$err")" && \
	[ ! -s "$err" ] && [ "$tmpdir" ] && [ "${tmpdir#$fsname}" != "$tmpdir" ]
	pipe_result_unmount
}

tests="$tests magic_root_gz"
magic_root_gz () {
	init tmp:   file=test,644,0,content \
	     root:  pack=test.gz,644,0,test \
	     mount: stat=test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_tar"
magic_root_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=test.tar,644,0,test_other \
	     mount: stat=test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_tar_gz"
magic_root_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=test.tar.gz,644,0,test_other \
	     mount: stat=test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_autocache_tar_gz_s"
magic_autocache_tar_gz_s () {
	init tmp:  file=test,644,0,content \
	           file=other,644,0,other \
	     root: pack=test.tar.gz,644,0,test_other
	mount_unpackfs "$mountdir" "$rootdir" '' "$config" && \
	stat "$mountdir/test" >/dev/null 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/magic_autocache_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_dir_gz"
magic_root_dir_gz () {
	init tmp:   file=test,644,0,content \
	     root:  pack=dir/subdir/subsubdir/a/b/c/test.gz,644,0,test \
	     mount: stat=dir/subdir/subsubdir/a/b/c/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_dir_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_dir_tar"
magic_root_dir_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=dir/subdir/subsubdir/a/b/c/test.tar,644,0,test_other \
	     mount: stat=dir/subdir/subsubdir/a/b/c/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_dir_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_dir_tar_gz"
magic_root_dir_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=dir/subdir/subsubdir/a/b/c/test.tar.gz,644,0,test_other \
	     mount: stat=dir/subdir/subsubdir/a/b/c/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_dir_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_autocache_dir_tar_gz_s"
magic_autocache_dir_tar_gz_s () {
	init tmp:  file=test,644,0,content \
	           file=other,644,0,other \
	     root: pack=dir/subdir/subsubdir/a/b/c/test.tar.gz,644,0,test_other
	mount_unpackfs "$mountdir" "$rootdir" '' "$config" && \
	stat "$mountdir/dir/subdir/subsubdir/a/b/c/test" >/dev/null 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/magic_autocache_dir_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_cache_gz"
magic_cache_gz () {
	init tmp:   file=test,644,0,content \
	     cache: pack=dir/test.gz,644,0,test \
	     mount: stat=dir/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_cache_tar"
magic_cache_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/test.tar,644,0,test_other \
	     mount: stat=dir/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_cache_tar_gz"
magic_cache_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/test.tar.gz,644,0,test_other \
	     mount: stat=dir/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_cache_dir_gz"
magic_cache_dir_gz () {
	init tmp:   file=test,644,0,content \
	     cache: pack=dir/subdir/subsubdir/a/b/c/test.gz,644,0,test \
	     mount: stat=dir/subdir/subsubdir/a/b/c/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_dir_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_cache_dir_tar"
magic_cache_dir_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/subdir/subsubdir/a/b/c/test.tar,644,0,test_other \
	     mount: stat=dir/subdir/subsubdir/a/b/c/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_dir_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_cache_dir_tar_gz"
magic_cache_dir_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/subdir/subsubdir/a/b/c/test.tar.gz,644,0,test_other \
	     mount: stat=dir/subdir/subsubdir/a/b/c/test
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_dir_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_root_gz"
dir_magic_root_gz () {
	init tmp:   file=test,644,0,content \
	     root:  pack=test.gz,644,0,test \
	     mount: readdir=.
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_root_tar"
dir_magic_root_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=test.tar,644,0,test_other \
	     mount: readdir=.
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_root_tar_gz"
dir_magic_root_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=test.tar.gz,644,0,test_other \
	     mount: readdir=.
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_autocache_tar_gz_s"
dir_magic_autocache_tar_gz_s () {
	init tmp:  file=test,644,0,content \
	           file=other,644,0,other \
	     root: pack=test.tar.gz,644,0,test_other
	mount_unpackfs "$mountdir" "$rootdir" '' "$config" && \
	ls "$mountdir" >/dev/null 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/magic_autocache_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_root_dir_gz"
dir_magic_root_dir_gz () {
	init tmp:   file=test,644,0,content \
	     root:  pack=dir/subdir/subsubdir/a/b/c/test.gz,644,0,test \
	     mount: readdir=dir/subdir/subsubdir/a/b/c
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_dir_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_root_dir_tar"
dir_magic_root_dir_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=dir/subdir/subsubdir/a/b/c/test.tar,644,0,test_other \
	     mount: readdir=dir/subdir/subsubdir/a/b/c
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_dir_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_root_dir_tar_gz"
dir_magic_root_dir_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=dir/subdir/subsubdir/a/b/c/test.tar.gz,644,0,test_other \
	     mount: readdir=dir/subdir/subsubdir/a/b/c
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_dir_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_autocache_dir_tar_gz_s"
dir_magic_autocache_dir_tar_gz_s () {
	init tmp:  file=test,644,0,content \
	           file=other,644,0,other \
	     root: pack=dir/subdir/subsubdir/a/b/c/test.tar.gz,644,0,test_other
	mount_unpackfs "$mountdir" "$rootdir" '' "$config" && \
	ls "$mountdir/dir/subdir/subsubdir/a/b/c" >/dev/null 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listmount -u "$ref/magic_autocache_dir_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_cache_gz"
dir_magic_cache_gz () {
	init tmp:   file=test,644,0,content \
	     cache: pack=dir/test.gz,644,0,test \
	     mount: readdir=dir
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_cache_tar"
dir_magic_cache_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/test.tar,644,0,test_other \
	     mount: readdir=dir
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_cache_tar_gz"
dir_magic_cache_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/test.tar.gz,644,0,test_other \
	     mount: readdir=dir
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_cache_dir_gz"
dir_magic_cache_dir_gz () {
	init tmp:   file=test,644,0,content \
	     cache: pack=dir/subdir/subsubdir/a/b/c/test.gz,644,0,test \
	     mount: readdir=dir/subdir/subsubdir/a/b/c
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_dir_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_cache_dir_tar"
dir_magic_cache_dir_tar () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/subdir/subsubdir/a/b/c/test.tar,644,0,test_other \
	     mount: readdir=dir/subdir/subsubdir/a/b/c
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_dir_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests dir_magic_cache_dir_tar_gz"
dir_magic_cache_dir_tar_gz () {
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     cache: pack=dir/subdir/subsubdir/a/b/c/test.tar.gz,644,0,test_other \
	     mount: readdir=dir/subdir/subsubdir/a/b/c
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_cache_dir_tar_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_2nd_gz"
magic_root_2nd_gz () {
	local a; local b;
	init tmp:   file=test,644,0,content \
	     root:  pack=test.gz,644,0,test
	a='^\([^ ]\+ \+\(-\|file\|dir\) \+"[^"]*" \+[^ ]*';
	b='[^ ]*\) \+"[^"]*"'
	sed "s%$a\.gz$b%\1 /bin/false%" "$config" >"$tmpdir/config" 2>>"$err" && \
	cat "$config" >>"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	stat "$mountdir/test" >/dev/null 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_gz" >"$diff"
	pipe_result_unmount
}

tests="$tests magic_root_2nd_tar"
magic_root_2nd_tar () {
	local a; local b;
	init tmp:   file=test,644,0,content \
	            file=other,644,0,other \
	     root:  pack=test.tar,644,0,test_other
	a='^\([^ ]\+ \+\(-\|file\|dir\) \+"[^"]*" \+[^ ]*';
	b='[^ ]*\) \+"[^"]*"'
	sed "s%$a\.tar$b%\1 /bin/false%" "$config" >"$tmpdir/config" 2>>"$err" && \
	cat "$config" >>"$tmpdir/config" 2>>"$err" && \
	[ -x "$cmd" ] && \
	"$cmd" "$mountdir" -u "$rootdir" -t "$cachedir" -c "$tmpdir/config" \
	       >"$out" 2>>"$err" && \
	stat "$mountdir/test" >/dev/null 2>>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ] && \
	diff_listdirs -u "$ref/magic_root_tar" >"$diff"
	pipe_result_unmount
}

tests="$tests version_s"
version_s () {
	"$cmd" -V >"$out" 2>"$err" && \
	sed "s%$fsname%unpackfs%; 1 s/ .*\$//" "$out" | \
	diff -u "$ref/version" - >"$diff" && [ ! -s "$err" ]
}

tests="$tests help_s"
help_s () {
	"$cmd" -h >"$out" 2>"$err" && \
	diff_sed2nd -u "$ref/help" "$out" >"$diff" && [ ! -s "$err" ]
}


# --- error_report ---

error_report () { # name file
	local name="$1"
	local file="$2"

	if [ -f "$file" ]; then
		if [ -s "$file" ]; then
			echo "$name:"
			cat "$file"
		else
			echo "$name: (none)"
		fi
	fi
}


# --- ccmalloc ---

ccmalloc () { # file
	local file="$1"
	local r

	log_wait_until 'generating ccmalloc report' "test -s \"$file\"" >&3

	if [ ! -s "$file" ]; then
		r='none'
	else
		r="$(awk -F'|' '
		     BEGIN { r="ok" }
		     ($2 == "      bytes" || $2 == "allocations" ) && $3 != $4
		        { r="failed" }
		     END { print r }' "$file")"
	fi

	if [ "$r" != 'ok' ]; then
		true
	else
		false
	fi
}


# --- main ---

result='ok'
count=0

ref='ref'
config='../src/config'
tmpdir="$(make_tmpdir)"
mountdir="$tmpdir/mount"
tmpsubdir="$tmpdir/tmp"
out="$tmpdir/stdout"
err="$tmpdir/stderr"
content="$tmpdir/content"
diff="$tmpdir/diff"
errorlog="$tmpdir/error.log"

while [ $# -gt 0 ]; do
	case "$1" in
		--dir=*)
			ref="${1#--dir=}/ref"
			;;
		--cmd=*)
			cmd="${1#--cmd=}"
			fsname="${cmd##*/}"
			;;
		--config=*)
			config="${1#--config=}"
			;;
		-h|--help)
			print_usage;
			exit 0
			;;
		*)
			echo "$script: unrecognized option \`-$1'"
			echo "Try \`$script --help' for more information."
	esac
	shift
done

if [ -e 'error.log' ]; then
	rm -f error.log
fi

echo "testsuite ${script%-test}   [X] = extended test"
echo "----------------------------------------"

exec 3>&1

for func in $tests; do
	if [ "${func%_s}" != "$func" ]; then
		func_modes='normal'
	else
		func_modes='normal extended'
	fi

	for mode in $func_modes; do
		if [ "$mode" = 'extended' ]; then
			rootdir="$tmpdir/root-long"
			cachedir="$tmpdir/cache"
			ext='.[X]'
		else
			rootdir="$tmpdir/root"
			cachedir="$tmpdir/cache"
			ext=''
		fi
 
		printf '%s\r' \
		"$(awk -v n="${func%_s}$ext" 'BEGIN { \
		   l=length(n)+8; \
		   while (l<40) { l++; n=n "." }
		   print n " running" }')"

		if "$func" && [ ! -e "$diff" -o ! -s "$diff" ]; then
			rc='ok'
			if [ -f ccmalloc.log ]; then
				if ! ccmalloc ccmalloc.log; then
					{
						echo "test: $func$ext"
						error_report 'ccmalloc' ccmalloc.log
						echo
					} >>"$errorlog"
					rc='leaked'
					count=$(($count+1))
					result='failed'
				fi
				rm -f ccmalloc.log
			fi
		else
			rc='failed'
			count=$(($count+1))
			result='failed'
			{
				echo "test: $func$ext"
				error_report 'stdout' "$out"
				error_report 'stderr' "$err"
				error_report 'diff'   "$diff"
				if [ -f ccmalloc.log ]; then
					if ! ccmalloc ccmalloc.log; then
						error_report 'ccmalloc' ccmalloc.log
					fi
					rm -f ccmalloc.log
				fi
				echo
			} >>"$errorlog"
		fi
		rm -f "$out" "$err" "$diff"
		clean

		awk -v n="${func%_s}$ext" -v r="$rc" 'BEGIN { \
			l=length(n)+1+length(r); \
			while (l<40) { l++; n=n "." };
			print n " " r }'

		if [ -e "$tmpdir/stop-running" ]; then
			break;
		fi
	done

	if [ -e "$tmpdir/stop-running" ]; then
		cat "$tmpdir/stop-running"
		break
	fi
done

exec 1>&3-

echo "----------------------------------------"

if [ -e "$errorlog" ]; then
	mv "$errorlog" error.log
fi
rm -rf "$tmpdir"

if [ "$result" = 'ok' ]; then
	echo "all tests passed."
	exit 0
else
	if [ "$test" = 1 ]; then
		echo '1 test failed.'
	else
		echo "$count tests failed."
	fi
	echo "see file 'error.log' for details."
	exit 1
fi


# --- end ---

