#! /bin/sh

#
# customize-test                                                (jh,07.11.2005)
#

#
#   Copyright (C) 2004, 2005  Jochen Hepp <jochen.hepp@gmx.de>
#
#   This file is part of customize.
#
#   customize 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 in version 2 of the License.
#
#   customize 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/../${script%-test}"
tests=


# --- 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 () { # [cmpall] [DIR] [TYPE=file,perm,time,content] ...
          # DIR:=work|common[-old|-fallback]|special[-old|-fallback]
          # see create () for TYPE=file,perm,time,content
          # global: workdir, customdir
	local dir
	local subdir
	local d

	mkdir -p "$workdir"
	dir="$workdir"
	subdir=''

	if [ "$1" = 'cmpall' ]; then
		shift
		subdir="$workdir"
	fi

	make_custom_dirs "$subdir" common testhost

	while [ $# -gt 0 ]; do
		case "$1" in
			work:)
				dir="$workdir"
				;;
			common:|common-old:|common-fallback:)
				d="${1%:}"
				make_custom_dirs "$subdir" "$d"
				dir="$customdir/$d$subdir"
				;;
			special:|special-old:|special-fallback:)
				d="${1%:}"
				d="testhost${d#special}"
				make_custom_dirs "$subdir" "$d"
				dir="$customdir/$d$subdir"
				;;
			*)
				create "$dir" "$1"
				;;
		esac
		shift
	done
}


# --- make_custom_dirs ---

make_custom_dirs () { # subdir dir, ...   global: workdir, customdir
	local subdir="$1"
	local dir
	local uplink

	shift
	uplink=''

	if [ -z "$subdir" ]; then
		# install links for customize to fake working at '/' instead of $workdir
		subdir="${workdir%/*}"
		uplink="`echo \"$subdir\" | sed 's%/[^/]\+%../%g; s%/$%%'`"
	fi

	while [ $# -gt 0 ]; do
		dir="$customdir/$1$subdir"
		if [ ! -d "$dir" ]; then
			mkdir -p "$dir"
			if [ "$uplink" ]; then
				ln -s "$uplink" "$customdir/$1$workdir"
			fi
		fi
		shift
	done
}


# --- create ---

create () { # dir TYPE=file,perm,time,content
            #     TYPE=file|link|dir|block|char|uchar|fifo
            #     content is mayor_minor when TYPE is block, char or uchar
	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"
			chmod "$perm" "$dir_file"
			touch -d $((20000101+$time)) "$dir_file"
			;;
		link)
			ln -s "$content" "$dir_file"
			;;
		dir)
			mkdir -p -m "$perm" "$dir_file"
			;;
		block)
			mknod -m "$perm" "$dir_file" b $content
			touch -d $((20000101+$time)) "$dir_file"
			;;
		char)
			mknod -m "$perm" "$dir_file" c $content
			touch -d $((20000101+$time)) "$dir_file"
			;;
		uchar)
			mknod -m "$perm" "$dir_file" u $content
			touch -d $((20000101+$time)) "$dir_file"
			;;
		fifo)
			mkfifo -m "$perm" "$dir_file"
			touch -d $((20000101+$time)) "$dir_file"
			;;
		*)
			echo "$script: $type: can't create this type" >&2
			exit 1
			;;
	esac
}


# --- listdirs ---

listdirs () { # global: tmpdir, workdir, customdir
	local subdir
	local dir
	local d

	subdir="${workdir#/}"
	subdir="${subdir%%/*}"
	for dir in common common-old common-fallback \
	           testhost testhost-old testhost-fallback; do
		d="$customdir/$dir/$subdir"
		if [ -d "$d" ]; then
			rm -r "$d"
		fi
	done

	(
		cd "$tmpdir" && \
		find custom work | \
		LC_ALL=C sort | \
		while read file; do
			ls -1 -d -g --no-group --time-style=+%Y.%m.%d "$file"
		done | \
		sed 's/[[:blank:]]\+[0-9]\+[[:blank:]]\+/ /;
		     /^[dl]/ s/[[:blank:]]\+[0-9]\+[[:blank:]]\+[0-9.]\+[[:blank:]]\+/ /;
		     /-old\// s/_[0-9]\{6,8\}\.old/_DATE.old/'
	)
}


# --- clean ---

clean () { # global: workdir, customdir
	if [ -d "$workdir" ]; then
		rm -rf "$workdir"
	fi
	if [ -d "$customdir" ]; then
		rm -rf "$customdir"
	fi
}


# --- diff_sed2nd ---

diff_sed2nd () { # global: tmpdir
	sed "s%$tmpdir\(/work\)\?%%g" <"$3" | \
	diff "$1" "$2" -
}


# --- diff_listdirs ---

diff_listdirs () {
	listdirs | \
	diff "$1" "$2" -
}


# --- tests ---

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

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

tests="$tests error_no_customize"
error_no_customize () {
	export CUSTOMIZE="$customdir" HOSTNAME=testhost
	[ -x "$cmd" ] && ! "$cmd" -d / >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u ref/error_no_customize "$err" >"$diff"
}

tests="$tests error_create_dir"
error_create_dir () {
	init work:   file=dir/test,644,0,content \
	     common: file=dir,644,0,content
	[ -x "$cmd" ] && ! "$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	sed -n "/^customize: / { s%$tmpdir\(/work\)\?%%g; p }" <"$err" | \
	diff -u ref/error_create_dir - >"$diff"
}

tests="$tests error_is_dir"
error_is_dir () {
	init work: dir=dir,755
	[ -x "$cmd" ] && ! "$cmd" -c "$workdir/dir" >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u ref/error_is_dir "$err" >"$diff"
}

tests="$tests error_not_found"
error_not_found () {
	init work:
	[ -x "$cmd" ] && ! "$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u ref/error_not_found "$err" >"$diff"
}

tests="$tests error_is_special_file"
error_is_special_file () {
	init work:    file=test,644,1,content \
	     special: file=test,644,0,content
	[ -x "$cmd" ] && ! "$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff -u ref/error_is_special_file "$err" >"$diff"
}

tests="$tests error_is_special_link"
error_is_special_link () {
	init work:    link=test,_,_,target \
	     special: link=test,_,_,target
	[ -x "$cmd" ] && ! "$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff -u ref/error_is_special_link "$err" >"$diff"
}

tests="$tests error_no_dest_dir"
error_no_dest_dir () {
	init common: file=dir/test,644,0,content
	[ -x "$cmd" ] && ! "$cmd" -r "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common_dir "$out" >"$diff" && \
	diff_sed2nd -u ref/error_no_dest_dir "$err" >>"$diff"
}

tests="$tests error_never_customized"
error_never_customized () {
	init work:
	[ -x "$cmd" ] && ! "$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u ref/error_never_customized "$err" >"$diff"
}

tests="$tests error_dir_not_found"
error_dir_not_found () {
	local result=1
	init cmpall
	export CUSTOMIZE="$customdir" HOSTNAME=otherhost
	"$cmd" --cmpall >"$out" 2>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u ref/error_dir_not_found "$err" >"$diff" && \
	result=0
	export CUSTOMIZE="$customdir" HOSTNAME=testhost
	return $result
}

tests="$tests error_tar_dir_not_found"
error_tar_dir_not_found () {
	local result=1
	init cmpall
	export CUSTOMIZE="$customdir" HOSTNAME=otherhost
	[ -x "$cmd" ] && ! "$cmd" --tar >"$out" 2>"$err" && \
	[ ! -s "$out" ] && \
	diff_sed2nd -u ref/error_dir_not_found "$err" >"$diff" && \
	result=0
	export CUSTOMIZE="$customdir" HOSTNAME=testhost
	return $result
}

tests="$tests error_tar_dir_exists"
error_tar_dir_exists () {
	init cmpall
	[ -x "$cmd" ] && ! "$cmd" --untar >"$out" 2>"$err" && \
	[ ! -s "$out" ] && diff_sed2nd -u ref/error_dir_exists "$err" >"$diff"
}

tests="$tests common_file"
common_file () {
	init work: file=test,644,0,content
	"$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests common_link"
common_link () {
	init work: link=test,_,_,target
	"$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_link.ls >>"$diff"
}

tests="$tests common_block"
common_block () {
	init work: block=test,644,0,1_0
	"$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_block.ls >>"$diff"
}

tests="$tests common_char"
common_char () {
	init work: char=test,644,0,5_1
	"$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_char.ls >>"$diff"
}

tests="$tests common_fifo"
common_fifo () {
	init work: fifo=test,644,0
	"$cmd" -c "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_fifo.ls >>"$diff"
}

tests="$tests common_long"
common_long () {
	init work: file=test,644,0,content
	"$cmd" --common "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests common_subdir"
common_subdir () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content
	"$cmd" -c "$workdir/$subdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_subdir_file.ls >>"$diff"
}

tests="$tests common_spaces"
common_spaces () {
	local subdir="dir with spaces/spaces in subdir"
	local name="file has spaces in its name"
	init work: file="$subdir/$name",644,0,content
	"$cmd" -c "$workdir/$subdir/$name" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_spaces "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_spaces.ls >>"$diff"
}

tests="$tests common_relative"
common_relative () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content
	( cd "$workdir/dir/subdir" && \
	  "$cmd" -c subsubdir/a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/common_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_subdir_file.ls >>"$diff"
}

tests="$tests common_updirs"
common_updirs () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content \
	           dir=dir/subdir/foo/bar,755
	( cd "$workdir/dir/subdir/foo/bar" && \
	  "$cmd" -c ../../subsubdir/a/b/../../a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/common_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_updirs_file.ls >>"$diff"
}

tests="$tests special_file"
special_file () {
	init work: file=test,644,0,content
	"$cmd" -s "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_file.ls >>"$diff"
}

tests="$tests special_link"
special_link () {
	init work: link=test,_,_,target
	"$cmd" -s "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_link.ls >>"$diff"
}

tests="$tests special_long"
special_long () {
	init work: file=test,644,0,content
	"$cmd" --special "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_file.ls >>"$diff"
}

tests="$tests special_subdir"
special_subdir () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content
	"$cmd" -s "$workdir/$subdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/special_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_subdir_file.ls >>"$diff"
}

tests="$tests special_spaces"
special_spaces () {
	local subdir="dir with spaces/spaces in subdir"
	local name="file has spaces in its name"
	init work: file="$subdir/$name",644,0,content
	"$cmd" -s "$workdir/$subdir/$name" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/special_spaces "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_spaces.ls >>"$diff"
}

tests="$tests special_relative"
special_relative () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content
	( cd "$workdir/dir/subdir" && \
	  "$cmd" -s subsubdir/a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/special_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_subdir_file.ls >>"$diff"
}

tests="$tests special_updirs"
special_updirs () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content \
	           dir=dir/subdir/foo/bar,755
	( cd "$workdir/dir/subdir/foo/bar" && \
	  "$cmd" -s ../../subsubdir/a/b/../../a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/special_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/special_updirs_file.ls >>"$diff"
}

tests="$tests diff_common_file"
diff_common_file () {
	init work:   file=test,644,0,content \
	     common: file=test,644,0,content
	"$cmd" -d "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests diff_common_link"
diff_common_link () {
	init work:   link=test,_,_,target \
	     common: link=test,_,_,target
	"$cmd" -d "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_link.ls >>"$diff"
}

tests="$tests diff_special_file"
diff_special_file () {
	init work:    file=test,644,1,other \
	     common:  file=test,644,0,content \
	     special: file=test,644,1,other
	"$cmd" -d "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_file.ls >>"$diff"
}

tests="$tests diff_special_link"
diff_special_link () {
	init work:    link=test,_,_,other \
	     common:  link=test,_,_,target \
	     special: link=test,_,_,other
	"$cmd" -d "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_link.ls >>"$diff"
}

tests="$tests diff_long"
diff_long () {
	init work:   file=test,644,0,content \
	     common: file=test,644,0,content
	"$cmd" --diff "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests diff_subdir"
diff_subdir () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work:   file=$subdir/test,644,0,content \
	     common: file=$subdir/test,644,0,content
	"$cmd" -d "$workdir/$subdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_subdir_file.ls >>"$diff"
}

tests="$tests diff_spaces"
diff_spaces () {
	local subdir="dir with spaces/spaces in subdir"
	local name="file has spaces in its name"
	init work:   file="$subdir/$name",644,0,content \
	     common: file="$subdir/$name",644,0,content
	"$cmd" -d "$workdir/$subdir/$name" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diff_spaces "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_spaces.ls >>"$diff"
}

tests="$tests diff_relative"
diff_relative () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work:   file=$subdir/test,644,0,content \
	     common: file=$subdir/test,644,0,content
	( cd "$workdir/dir/subdir" && \
	  "$cmd" -d subsubdir/a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/diff_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_subdir_file.ls >>"$diff"
}

tests="$tests diff_updirs"
diff_updirs () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work: file=$subdir/test,644,0,content \
	           dir=dir/subdir/foo/bar,755 \
	     common: file=$subdir/test,644,0,content
	( cd "$workdir/dir/subdir/foo/bar" && \
	  "$cmd" -d ../../subsubdir/a/b/../../a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/diff_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_updirs_file.ls >>"$diff"
}

tests="$tests diffshort"
diffshort () {
	init work:   file=test,644,0,content \
	     common: file=test,644,0,content
	"$cmd" -ds "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diffshort_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests diffshort_long"
diffshort_long () {
	init work:   file=test,644,0,content \
	     common: file=test,644,0,content
	"$cmd" --diffshort "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/diffshort_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests restore_common_file"
restore_common_file () {
	init common: file=test,644,0,content
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests restore_common_link"
restore_common_link () {
	init common: link=test,_,_,target
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_link.ls >>"$diff"
}

tests="$tests restore_common_block"
restore_common_block () {
	init common: block=test,644,0,1_0
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_block.ls >>"$diff"
}

tests="$tests restore_common_char"
restore_common_char () {
	init common: char=test,644,0,5_1
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_char.ls >>"$diff"
}

tests="$tests restore_common_fifo"
restore_common_fifo () {
	init common: fifo=test,644,0
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_fifo.ls >>"$diff"
}

tests="$tests restore_special_file"
restore_special_file () {
	init common:  file=test,644,0,content \
	     special: file=test,644,1,other
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_file.ls >>"$diff"
}

tests="$tests restore_special_link"
restore_special_link () {
	init common:  link=test,_,_,target \
	     special: link=test,_,_,other
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_link.ls >>"$diff"
}

tests="$tests restore_long"
restore_long () {
	init common: file=test,644,0,content
	"$cmd" --restore "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests restore_subdir"
restore_subdir () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work:   dir=$subdir,755 \
	     common: file=$subdir/test,644,0,content
	"$cmd" -r "$workdir/$subdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_subdir_file.ls >>"$diff"
}

tests="$tests restore_spaces"
restore_spaces () {
	local subdir="dir with spaces/spaces in subdir"
	local name="file has spaces in its name"
	init work:   dir="$subdir",755 \
	     common: file="$subdir/$name",644,0,content
	"$cmd" -r "$workdir/$subdir/$name" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_spaces "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_spaces.ls >>"$diff"
}

tests="$tests restore_relative"
restore_relative () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work:   dir=$subdir,755 \
	     common: file=$subdir/test,644,0,content
	( cd "$workdir/dir/subdir" && \
	  "$cmd" -r subsubdir/a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/restore_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_subdir_file.ls >>"$diff"
}

tests="$tests restore_updirs"
restore_updirs () {
	local subdir=dir/subdir/subsubdir/a/b/c
	init work:   dir=$subdir,755 \
	             dir=dir/subdir/foo/bar,755 \
	     common: file=$subdir/test,644,0,content
	( cd "$workdir/dir/subdir/foo/bar" && \
	  "$cmd" -r ../../subsubdir/a/b/../../a/b/c/test >"$out" 2>"$err" ) && \
	diff_sed2nd -u ref/restore_subdir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_updirs_file.ls - >>"$diff"
}

tests="$tests force_common"
force_common () {
	init work:   file=test,644,9,test \
	     common: file=test,644,0,content
	"$cmd" -rf "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests force_special"
force_special () {
	init work:    file=test,644,9,test \
	     common:  file=test,644,0,content \
	     special: file=test,644,1,other
	"$cmd" -rf "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_file.ls >>"$diff"
}

tests="$tests force_long"
force_long () {
	init work:   file=test,644,9,test \
	     common: file=test,644,0,content
	"$cmd" --force "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests force_file_to_link"
force_file_to_link () {
	init work:   link=test,_,_,other \
	             file=other,644,1,test \
	     common: file=test,644,0,content
	"$cmd" -rf "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_other_file.ls >>"$diff"
}

tests="$tests force_file_to_dead_link"
force_file_to_dead_link () {
	init work:   link=test,_,_,target \
	     common: file=test,644,0,content
	"$cmd" -rf "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_file.ls >>"$diff"
}

tests="$tests force_link_to_link"
force_link_to_link () {
	init work:   link=test,_,_,other \
	             file=other,644,1,test \
	     common: link=test,_,_,target
	"$cmd" -rf "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_other_link.ls >>"$diff"
}

tests="$tests force_link_to_dead_link"
force_link_to_dead_link () {
	init work:   link=test,_,_,nowhere \
	     common: link=test,_,_,target
	"$cmd" -rf "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/force_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_link.ls >>"$diff"
}

tests="$tests cmpall_long"
cmpall_long () {
	init cmpall \
	     work:    file=test,644,1,other \
	     common:  file=test,644,0,content \
	     special: file=test,644,1,other
	"$cmd" --cmpall >"$out" 2>"$err" && \
	[ ! -s "$out" ] && [ ! -s "$err" ]
}

tests="$tests cmpall_missing"
cmpall_missing () {
	init cmpall \
	     common: file=test,644,0,content
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_missing "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_is_file"
cmpall_is_file () {
	init cmpall \
	     work:   file=test,644,0,content \
	     common: link=test,_,_,target
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_is_file "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_newer"
cmpall_newer () {
	init cmpall \
	     work:   file=test,644,1,other \
	     common: file=test,644,0,content
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_newer "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_older"
cmpall_older () {
	init cmpall \
	     work:   file=test,644,0,other \
	     common: file=test,644,1,content
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_older "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_differs_file"
cmpall_differs_file () {
	init cmpall \
	     work:   file=test,644,0,other \
	     common: file=test,644,0,content
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_differs "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_is_okay_file"
cmpall_is_okay_file () {
	init cmpall \
	     work:            file=test,644,0,content \
	     common-fallback: file=test,644,0,content
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_is_okay "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_is_link"
cmpall_is_link () {
	init cmpall \
	     work:   link=test,_,_,target \
	     common: file=test,644,0,content
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_is_link "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_differs_link"
cmpall_differs_link () {
	init cmpall \
	     work:   link=test,_,_,other \
	     common: link=test,_,_,target
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_differs "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_dead_link"
cmpall_dead_link () {
	init cmpall \
	     work:   link=test,_,_,target \
	     common: link=test,_,_,target
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_dead_link "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_is_okay_link"
cmpall_is_okay_link () {
	init cmpall \
	     work:            file=target,644,0,content \
	                      link=test,_,_,target \
	     common-fallback: link=test,_,_,target
	"$cmd" --cmpall >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_is_okay "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests restore_common_fallback"
restore_common_fallback () {
	init common-fallback: file=test,644,1,content-fallback
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common_fallback "$out" >"$diff" && \
	[ ! -s "$err" ] && \
	diff_listdirs -u ref/common_fallback_file.ls >>"$diff"
}

tests="$tests restore_common_fallback_skip"
restore_common_fallback_skip () {
	init common:          file=test,644,0,content \
	     common-fallback: file=test,644,1,content-fallback
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_common "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_fallback_skip_file.ls >>"$diff"
}

tests="$tests restore_special_fallback"
restore_special_fallback () {
	init common:           file=test,644,0,content \
	     common-fallback:  file=test,644,1,content-fallback \
	     special-fallback: file=test,644,3,other-fallback
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_special_fallback "$out" >"$diff" && \
	[ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_fallback_file.ls >>"$diff"
}

tests="$tests restore_special_fallback_skip"
restore_special_fallback_skip () {
	init common:           file=test,644,0,content \
	     common-fallback:  file=test,644,1,content-fallback \
	     special:          file=test,644,2,other \
	     special-fallback: file=test,644,3,other-fallback
	"$cmd" -r "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/restore_special "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_special_fallback_skip_file.ls >>"$diff"
}

tests="$tests cmpall_common_fallback"
cmpall_common_fallback () {
	init cmpall \
	     work:            file=test,644,1,content-fallback \
	     common-fallback: file=test,644,1,content-fallback
	"$cmd" --cmpall "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_is_okay "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_common_fallback_skip"
cmpall_common_fallback_skip () {
	init cmpall \
	     work:            file=test,644,0,content \
	     common:          file=test,644,0,content \
	     common-fallback: file=test,644,1,content-fallback
	"$cmd" --cmpall "$workdir/test" >"$out" 2>"$err" && \
	[ ! -s "$out" ] >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_special_fallback"
cmpall_special_fallback () {
	init cmpall \
	     work:             file=test,644,3,other-fallback \
	     common:           file=test,644,0,content \
	     common-fallback:  file=test,644,1,content-fallback \
	     special-fallback: file=test,644,3,other-fallback
	"$cmd" --cmpall "$workdir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/cmpall_is_okay "$out" >"$diff" && [ ! -s "$err" ]
}

tests="$tests cmpall_special_fallback_skip"
cmpall_special_fallback_skip () {
	init cmpall \
	     work:             file=test,644,2,other \
	     common:           file=test,644,0,content \
	     common-fallback:  file=test,644,1,content-fallback \
	     special:          file=test,644,2,other \
	     special-fallback: file=test,644,3,other-fallback
	"$cmd" --cmpall "$workdir/test" >"$out" 2>"$err" && \
	[ ! -s "$out" ] >"$diff" && [ ! -s "$err" ]
}

tests="$tests common_save_old_file"
common_save_old_file () {
	init work:       file=dir/test,644,1,new \
	     common:     file=dir/test,644,0,older \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_dir_old "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_save_old_file.ls >>"$diff"
}

tests="$tests common_save_old_link"
common_save_old_link () {
	init work:       link=dir/test,_,_,target \
	     common:     link=dir/test,_,_,other \
	                 file=dir/other,644,0,content \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_dir_old "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_save_old_link.ls >>"$diff"
}

tests="$tests common_save_old_dead_link"
common_save_old_dead_link () {
	init work:       link=dir/test,_,_,target \
	     common:     link=dir/test,_,_,other \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_dir_old "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_save_old_dead_link.ls >>"$diff"
}

tests="$tests common_with_old_link"
common_with_old_link () {
	init work:       link=dir/test,_,_,target \
	     common:     link=dir/test,_,_,other \
	                 file=dir/other,644,0,content \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_dir_old "$out" >"$diff" && [ ! -s "$err" ] && \
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_dir "$out" >"$diff" && [ ! -s "$err" ] && \
	diff_listdirs -u ref/common_with_old_link.ls >>"$diff"
}

tests="$tests common_delete_fallback_file"
common_delete_fallback_file () {
	init work:            file=dir/test,644,2,content \
	     common-fallback: file=dir/test,644,1,old-fallback \
	                      file=keep,644,0,keep_because_of_fake_link \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_fallback_old "$out" >"$diff" && \
	[ ! -s "$err" ] && \
	diff_listdirs -u ref/common_delete_fallback_file.ls >>"$diff"
}

tests="$tests common_delete_fallback_link"
common_delete_fallback_link () {
	init work:            link=dir/test,_,_,target \
	     common-fallback: link=dir/test,_,_,other \
	                      file=dir/other,644,0,content \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_fallback_old "$out" >"$diff" && \
	[ ! -s "$err" ] && \
	diff_listdirs -u ref/common_delete_fallback_link.ls >>"$diff"
}

tests="$tests common_delete_fallback_dead_link"
common_delete_fallback_dead_link () {
	init work:            link=dir/test,_,_,target \
	     common-fallback: link=dir/test,_,_,other \
	                      file=keep,644,0,keep_because_of_fake_link \
	     common-old:
	"$cmd" -c "$workdir/dir/test" >"$out" 2>"$err" && \
	diff_sed2nd -u ref/common_fallback_old "$out" >"$diff" && \
	[ ! -s "$err" ] && \
	diff_listdirs -u ref/common_delete_fallback_dead_link.ls >>"$diff"
}

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

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

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

tests="$tests help_long"
help_long () {
	"$cmd" --help >"$out" 2>"$err" && \
	diff -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
}


# --- main ---

result='ok'
count=0

tmpdir="$(make_tmpdir)"
workdir="$tmpdir/work"
customdir="$tmpdir/custom"
out="$tmpdir/stdout"
err="$tmpdir/stderr"
content="$tmpdir/content"
diff="$tmpdir/diff"
errorlog="$tmpdir/error.log"
hostname="$HOSTNAME"
export CUSTOMIZE="$customdir" HOSTNAME=testhost

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

echo "testsuite ${script%-test}"
echo "----------------------------------------"

for func in $tests; do
	if "$func" && [ ! -e "$diff" -o ! -s "$diff" ]; then
		rc='ok'
	else
		rc='failed'
		count="$(($count+1))"
		result='failed'
		{
			echo "test: $func"
			error_report 'stdout' "$out"
			error_report 'stderr' "$err"
			error_report 'diff'   "$diff"
			echo
		} >>"$errorlog"
	fi
	rm -f "$out" "$err" "$diff"
	clean
	awk -v n="$func" -v r="$rc" 'BEGIN { \
			l=length(n)+1+length(r); \
			while (l<40) { l++; n=n "." }; print n " " r }'
done

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

export HOSTNAME="$hostname"

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 ---

