# Hey Emacs, this is a -*- shell-script -*- !!!  :-)

. "${TEST_SCRIPTS_DIR}/common.sh"

# Common variables and functions for CTDB unit tests.

trap -- '' PIPE

# Set the required result for a test.
# - Argument 1 is exit code.
# - Argument 2, if present is the required test output but "--"
#   indicates empty output.
# If argument 2 is not present or null then read required test output
# from stdin.
required_result()
{
	required_rc="${1:-0}"
	if [ -n "$2" ]; then
		if [ "$2" = "--" ]; then
			required_output=""
		else
			# Use a sub-shell to strip trailing newlines.
			# They can't be matched anyway because the
			# test is run in a sub-shell, which strips
			# trailing newlines.
			# shellcheck disable=SC2116
			required_output=$(echo "$2")
		fi
	else
		if ! tty -s; then
			required_output=$(cat)
		else
			required_output=""
		fi
	fi
}

required_error()
{
	rc=$(errcode "$1")
	shift
	required_result "$rc" "$@"
}

ok()
{
	required_result 0 "$@"
}

ok_null()
{
	ok --
}

reset_extra_header()
{
	# Re-define this function to output extra header information
	extra_header()
	{
		:
	}
}

reset_extra_footer()
{
	# Re-define this function to output extra footer information
	extra_footer()
	{
		:
	}
}

reset_extra_header
reset_extra_footer

result_print()
{
	_passed="$1"
	_out="$2"
	_rc="$3"

	if "$CTDB_TEST_VERBOSE" || ! $_passed; then
		extra_header

		cat <<EOF
--------------------------------------------------
Output (Exit status: ${_rc}):
--------------------------------------------------
EOF
		# Avoid echo, which might expand unintentional escapes
		printf '%s\n' "$_out" |
			result_filter |
			cat "${CTDB_TEST_CAT_RESULTS_OPTS:--}"
	fi

	if ! $_passed; then
		cat <<EOF
--------------------------------------------------
Required output (Exit status: ${required_rc}):
--------------------------------------------------
EOF
		# Avoid echo, which might expand unintentional escapes
		printf '%s\n' "$required_output" |
			cat "${CTDB_TEST_CAT_RESULTS_OPTS:--}"

		if $CTDB_TEST_DIFF_RESULTS; then
			_outr=$(mktemp)
			# Avoid echo, which might expand unintentional escapes
			printf '%s\n' "$required_output" >"$_outr"

			_outf=$(mktemp)
			# Avoid echo, which might expand unintentional escapes
			printf '%s\n' "$_fout" >"$_outf"

			cat <<EOF
--------------------------------------------------
Diff:
--------------------------------------------------
EOF
			diff -u "$_outr" "$_outf" | cat -A
			rm "$_outr" "$_outf"
		fi
	fi
}

result_footer()
{
	_passed="$1"

	if "$CTDB_TEST_VERBOSE" || ! $_passed; then
		extra_footer
	fi

	if $_passed; then
		echo "PASSED"
		return 0
	else
		echo
		echo "FAILED"
		return 1
	fi
}

# Result filtering is (usually) used to replace the date/time/PID
# prefix on some CTDB tool/client log messages with the literal string
# "DATE TIME [PID]".  This allows tests to loosely match this output,
# since it can't otherwise be matched.
result_filter_default()
{
	_date_time_pid='[0-9/][0-9/]*\ [0-9:\.][0-9:\.]*\ \[[\ 0-9][\ 0-9]*\]'
	sed -e "s@^${_date_time_pid}:@DATE\ TIME\ \[PID\]:@"
}
# Used in testcases
# shellcheck disable=SC2034
TEST_DATE_STAMP=""

# Override this function to customise output filtering.
result_filter()
{
	result_filter_default
}

result_check()
{
	_rc=$?

	# Avoid echo, which might expand unintentional escapes
	_fout=$(printf '%s\n' "$_out" | result_filter)

	if [ "$_fout" = "$required_output" ] &&
		[ "$_rc" = "$required_rc" ]; then
		_passed=true
	else
		_passed=false
	fi

	result_print "$_passed" "$_out" "$_rc"
	result_footer "$_passed"
}

test_fail()
{
	_passed=false
	return 1
}

test_case_string=""
test_case()
{
	test_case_string="$*"
}

test_header_default()
{
	echo "=================================================="
	if [ -n "$test_case_string" ]; then
		echo "Summary: ${test_case_string}"
		test_case_string=""
	fi
	echo "Running: $*"
}

reset_test_header()
{
	# Re-define this function to get different header
	test_header()
	{
		test_header_default "$@"
	}
}

reset_test_header

# Simple test harness for running binary unit tests
unit_test()
{
	test_header "$@"

	_wrapper="$VALGRIND"
	if $CTDB_TEST_COMMAND_TRACE; then
		_wrapper="strace"
	fi
	_out=$($_wrapper "$@" 2>&1)

	result_check || exit $?
}

# Simple test harness for running shell script unit tests
script_test()
{
	test_header "$@"

	_shell=""
	if ${CTDB_TEST_COMMAND_TRACE}; then
		_shell="sh -x"
	else
		_shell="sh"
	fi

	_out=$($_shell "$@" 2>&1)

	result_check || exit $?
}

# Simple test harness for running tests without tracing
unit_test_notrace()
{
	test_header "$@"

	_out=$("$@" 2>&1)

	result_check || exit $?
}

test_cleanup_hooks=""

test_cleanup()
{
	test_cleanup_hooks="${test_cleanup_hooks}${test_cleanup_hooks:+ ; }$*"
}

trap 'eval $test_cleanup_hooks' 0

local="${CTDB_TEST_SUITE_DIR}/scripts/local.sh"
if [ -r "$local" ]; then
	. "$local"
fi
