#!/bin/bash

myscript=$(basename $0)
if [ ! -f "$myscript" ] ; then
	echo "Please run $myscript whilst standing in the same directory" 1>&2
	exit 1
fi

# The Windows build symlinks cock things for 'diff -ur' on the restore later,
# so delete them first.
rm -f ../burp-depkgs
rm -f ../burp-cross-tools

path="$PWD"
build="$path/build"
includedir="$path/build"
excludedir="$includedir/src"
logs="$path/logs"
target="$path/target"
serveroutputlog="$logs/server-output.log"
serversystemlog="$logs/server-system.log"
clientlog="$logs/client.log"
beduplog="$logs/bedup.log"
difflog="$logs/diff.log"
restoredir="$path/restore"
serverpid=
clientconf=etc/burp/burp.conf
serverconf=etc/burp/burp-server.conf
fsize=1024

kill_server()
{
	if [ -n "$serverpid" ] ; then
		echo "Killing test server"
		kill -9 $serverpid
		serverpid=
	fi
}

trap "kill_server" 0 1 2 3 15

fail()
{
	echo
	echo "Test failed: $@"
	echo
	kill_server
	exit 1
}

makedir()
{
	rm -rf "$1"
	mkdir -p "$1" || fail "could not mkdir $1"
}

cdir()
{
	cd "$1" || fail "could not cd to $1"
}

build_and_install()
{
	# Create a build directory, and fill it with the source.
	makedir "$build"
	makedir "$build/test"
	ls ../ | while read f ; do
		[ "$f" = "test" ] && continue
		[ "$f" = ".git" ] && continue
		cp -ar ../"$f" "$build" || fail "could not copy ../$f to $build"
	done || exit 1

	# Create a target directory, compile burp and install it into the
	# target.
	makedir "$target"
	cdir "$build"
	make clean
	#./configure --prefix="$target" --datarootdir="$target"/usr/share --sysconfdir="$target"/etc/burp || fail "configure failed"
	./configure || fail "configure failed"
	make || fail "make failed"
	# For some reason, make is not returning an error code.
	# Look for important binaries before carrying on.
	[ -f src/burp ] || fail "make failed"
	[ -f src/bedup ] || fail "make failed"
	make install DESTDIR="$target" || fail "make install failed"
}

sed_rep()
{
	sed -i -e "$1" "$2" || fail "sed $1 failed $2f"
}

sed_rep_both()
{
	sed_rep "$1" $clientconf
	sed_rep "$1" $serverconf
}

run_bedup()
{
	echo "Starting test bedup"
	./usr/sbin/bedup -c $serverconf -l >> "$beduplog" 2>&1 \
		|| fail "bedup returned $?"
}

run_backup()
{
	local waited=0
	local working="$target/var/spool/burp/testclient/working"
	local finishing="$target/var/spool/burp/testclient/finishing"
	SLEEPTIME=2

	echo "Starting test client backup"
	./usr/sbin/burp -c $clientconf -a b >> "$clientlog" 2>&1 \
		|| fail "client backup returned $?"

	# Repeatedly check to see whether the server has finished, rather
	# than just sleeping some length of time. 
	while true ; do
		sleep $SLEEPTIME
		[ ! -e "$working" -a ! -e "$finishing" ] \
			&& sleep $SLEEPTIME && break
		waited=$((waited+$SLEEPTIME))
		[ "$waited" -gt 120 ] && \
		  fail "client backup seemed to be complete after 2 minutes"
	done
}

run_verify()
{
	local num="$1"
	echo "Starting test client verify of backup $num"
	./usr/sbin/burp -c $clientconf -a v -b "$num" \
		>> "$clientlog" 2>&1 || fail "client verify returned $?"
}

run_restore()
{
	local num="$1"
	local dir="$2"
	echo "Starting test client restore of backup $num"
	makedir "$dir"
	./usr/sbin/burp -c $clientconf -a r -b "$num" -d "$dir" \
		>>"$clientlog" 2>&1 || fail "client restore returned $?"
}

write_message()
{
	message="$1"
	echo "$message"
	echo "$message" >> $serveroutputlog
	echo "$message" >> $serversystemlog
	echo "$message" >> $clientlog
	echo "$message" >> $difflog
	echo "$message" >> $beduplog
}

start_test()
{
	write_message "
Test $1
$2"
}

end_test()
{
	write_message "Test $1 OK"
}

change_source_files()
{
	cp -r "$build/src" "$build/src-new" \
		|| fail "could not cp $build/src to $build/src-new"

	# Move a directory, which simulates deleting and adding new files.
	if [ -d "$build/debian" ] && [ ! -e "$build/debian-new" ] ; then
		mv "$build/debian" "$build/debian-new" \
		  || fail "could not move $build/debian to $build/debian-new"
	elif [ ! -e "$build/debian" ] && [ -d "$build/debian-new" ] ; then
		mv "$build/debian-new" "$build/debian" \
		  || fail "could not move $build/debian-new to $build/debian"
	else
		fail "could not move debian directories" 
	fi

	# Scramble a whole bunch of files
	find "$build/autoconf" -type f | while read f ; do
		LC_ALL='C' sort --random-sort "$f" > tmpfile \
			|| fail "could not randomise $f"
		mv tmpfile "$f" || fail "could not move tmpfile to $f"
	done
}

compression_off()
{
	sed_rep 's/^compression = .*//g' $serverconf
	echo "compression = 0" >> $serverconf
}

compression_on()
{
	sed_rep 's/^compression = .*//g' $serverconf
	echo "compression = 9" >> $serverconf
}

encryption_off()
{
	sed_rep 's/^encryption_password = .*//g' $clientconf
}

encryption_on()
{
	encryption_off
	echo "encryption_password = 7djh34nmm$sd" >> $clientconf
}

min_file_size_off()
{
	sed_rep 's/^min_file_size = .*//g' $clientconf
}

min_file_size_on()
{
	min_file_size_off
	echo "min_file_size = $fsize" >> $clientconf
}

max_file_size_off()
{
	sed_rep 's/^max_file_size = .*//g' $clientconf
}

max_file_size_on()
{
	max_file_size_off
	echo "max_file_size = $fsize" >> $clientconf
}

exclude_off()
{
	sed_rep 's/^exclude = .*//g' $clientconf
}

exclude_on()
{
	exclude_off
	echo "exclude = $excludedir" >> $clientconf
}

include_off()
{
	sed_rep 's/^include = .*//g' $clientconf
}

include_on()
{
	include_off
	echo "include = $includedir" >> $clientconf
}

include_ext_off()
{
	sed_rep 's/^include_ext = c//g' $clientconf
}

include_ext_on()
{
	include_ext_off
	echo "include_ext = c" >> $clientconf
}

exclude_ext_off()
{
	sed_rep 's/^exclude_ext = c//g' $clientconf
}

exclude_ext_on()
{
	exclude_ext_off
	echo "exclude_ext = c" >> $clientconf
}

normal_settings()
{
	compression_on
	encryption_off
	max_file_size_off
	min_file_size_off
	exclude_off
	include_on
	include_ext_off
	exclude_ext_off
}

build_and_install

makedir "$logs"

cdir "$target"

# Tweak the example configuration files by removing the leading '/' from all
# the paths, and changing the port numbers.
sed_rep_both "s#= /#= $target/#g"
sed_rep_both 's/port = 4971/port = 4998/g'
sed_rep_both 's/port = 4972/port = 4999/g'
sed_rep "s#^CA_DIR.*#CA_DIR = $target/etc/burp/CA#g" "$target/etc/burp/CA.cnf"

echo
echo "Starting tests"
echo "Server output log: $serversystemlog"
echo "Server system log: $serveroutputlog"
echo "       Client log: $clientlog"
echo "        Bedup log: $beduplog"
echo "         Diff log: $difflog"
echo "More logs can be found in:"
echo "$target/var/spool/burp/testclient/<backup number>"
echo

echo "Initialising server certificate authority"
# Need to do this outside of the server, because it might take a long time.
./usr/sbin/burp -c "$serverconf" -g >> "$serveroutputlog" 2>&1 || fail "Initialising server certificate authority failed"
echo

# Start up the server
echo "Starting test server"
./usr/sbin/burp -c "$serverconf" -l "$serversystemlog" -F >> "$serveroutputlog" 2>&1 &
serverpid=$!

# Be kind, and wait a little for it to start.
sleep 5


# ----- Test 1 -----
start_test 1 "First backup/restore comparison"
normal_settings
#echo "server_script_pre = /tmp/server.sh" >>"$serverconf"
run_backup
# Verify the specific backup number 1. Later tests will use 'all', which will
# verify all available backups. Might as well test both methods.
run_verify 1
run_restore 1 "$restoredir"1
diff -ur "$build" "$restoredir"1/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore differed from the original!"
end_test 1

# ----- Test 2 -----
start_test 2 "Second backup/restore comparison"
normal_settings
run_backup
run_verify all
# There should be no 'data' directory left in the first backup directory,
# because all the files should have moved over into the second backup
# directory.
backuppath1=$(echo "$target"/var/spool/burp/testclient/0000001*)
backuppath2=$(echo "$target"/var/spool/burp/testclient/0000002*)
[ -d "$backuppath1"/data ] \
	&& fail "something changed between backup 1 and backup 2!"
diff -ur "$backuppath1"/manifest.gz "$backuppath2/manifest.gz" \
	>>"$difflog" 2>&1 \
		|| fail "manifests changed between backup 1 and backup 2!"
# Make sure that restoring from either backup gives the same result.
run_restore 1 "$restoredir"1
run_restore 2 "$restoredir"2
diff -ur "$build" "$restoredir"1/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 1 differed from the original!"
diff -ur "$restoredir"1/"$build" "$restoredir"2/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 1 and 2 differ!"
end_test 2

# ----- Test 3 -----
start_test 3 "Third backup/restore comparison, with changes"
normal_settings
change_source_files
run_backup
run_verify all
run_restore 3 "$restoredir"3
diff -ur "$build" "$restoredir"3/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 3 differed from the original!"
# Make sure that restoring backup number 2 gives the same as restoring backup
# number 2 gave in test 2.
run_restore 2 "$restoredir"4
diff -ur "$restoredir"2/"$build" "$restoredir"4/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 2 and 4 differ!"
end_test 3

# ----- Test 4 -----
start_test 4 "Deduplication, backup/restore comparison"
normal_settings
run_bedup
run_verify all
run_restore 3 "$restoredir"4
diff -ur "$build" "$restoredir"4/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 4 differed from the original!"
# Make sure that restoring backup number 2 gives the same as restoring backup
# number 2 gave in test 2.
run_restore 2 "$restoredir"4
diff -ur "$restoredir"2/"$build" "$restoredir"4/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 2 and 4 differ!"
# Run another backup, so we can continue to refer to them by the same number
# as the test number (easier on the brain).
run_backup
end_test 4

# ----- Test 5 -----
start_test 5 "Turn compression off, change files, backup/restore comparison"
compression_off
encryption_off
max_file_size_off
min_file_size_off
change_source_files
run_backup
run_verify all
run_restore 5 "$restoredir"5
diff -ur "$build" "$restoredir"5/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 5 differed from the original!"
end_test 5

# ----- Test 6 -----
start_test 6 "Turn compression off, encryption on, change files, backup/restore comparison"
normal_settings
compression_off
encryption_on
change_source_files
run_backup
run_verify all
run_restore 6 "$restoredir"6
diff -ur "$build" "$restoredir"6/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 6 differed from the original!"
end_test 6

# ----- Test 7 -----
start_test 7 "Turn compression on, encryption on, change files, backup/restore comparison"
normal_settings
compression_on
encryption_on
change_source_files
run_backup
run_verify all
run_restore 7 "$restoredir"7
diff -ur "$build" "$restoredir"7/"$build" >>"$difflog" 2>&1 \
	|| fail "client restore 7 differed from the original!"
end_test 7

# ----- Test 8 -----
start_test 8 "Min file size, backup/restore comparison"
normal_settings
min_file_size_on
change_source_files
run_backup
run_verify all
run_restore 8 "$restoredir"8
f=$(find "$restoredir"8 -size -${fsize}c -type f)
[ -n "$f" ] && fail "found file sizes greater than $fsize: $f"
end_test 8

# ----- Test 9 -----
start_test 9 "Max file size, backup/restore comparison"
normal_settings
max_file_size_on
change_source_files
run_backup
run_verify all
run_restore 9 "$restoredir"9
f=$(find "$restoredir"9 -size +${fsize}c -type f)
[ -n "$f" ] && fail "found file sizes less than $fsize: $f"
end_test 9

# ----- Test 10 -----
start_test 10 "Exclude directory, backup/restore comparison"
normal_settings
exclude_on
run_backup
run_verify all
run_restore 10 "$restoredir"10
[ -d "$restoredir"10/$excludedir ] && fail "$excludedir should not have been restored!"
[ ! -d "$restoredir"10/$includedir ] && fail "$includedir should have been restored!"
end_test 10

# ----- Test 11 -----
start_test 11 "Exclude extension, backup/restore comparison"
normal_settings
exclude_ext_on
run_backup
run_verify all
run_restore 11 "$restoredir"11
f=$(find "$restoredir"11 -type f -name '*.c')
[ -n "$f" ] && fail "$restoredir11 should not contain any '.c' files"
end_test 11

# ----- Test 12 -----
start_test 12 "Include extension, backup/restore comparison"
normal_settings
include_ext_on
run_backup
run_verify all
run_restore 12 "$restoredir"12
f=$(find "$restoredir"12 -type f -name '*.h')
[ -n "$f" ] && fail "$restoredir12 should not contain any '.h' files"
f=$(find "$restoredir"12 -type f -name '*.c')
[ -z "$f" ] && fail "$restoredir12 should contain '.c' files"
end_test 12

echo
echo "All tests succeeded"
echo

exit 0
