#!/bin/sh
# Helpers shared by the ikvswitch-host-networking unit tests. This file
# is not named test_*, so run_tests never executes it directly. It must
# be sourced from a test file, which is started by run_tests with the
# working directory set to the test's folder.

set -e

TESTDIR=$(pwd)
IKVROOT=$(readlink -f "${TESTDIR}/../..")
SCRIPT_SRC=$(readlink -f "${IKVROOT}/usr/bin/ikvswitch-host-networking")
CONF_SRC=$(readlink -f "${IKVROOT}/etc/ikvswitch/ikvswitch.conf")

SANDBOX=$(mktemp -d /tmp/ikvswitch-net-test.XXXXXX)

cleanup () {
	rm -rf "${SANDBOX}"
}
trap cleanup EXIT

# ikvswitch-host-networking hardcodes /etc/ikvswitch/ikvswitch.conf,
# writes to /proc/sys and matches /sys/devices/virtual/net/ik*: the
# tests run a patched copy of the script, with all of those redirected
# inside a mktemp sandbox, so that the suite is hermetic and never
# needs root. The fake bridges below pre-create their group_fwd_mask
# file, as the kernel would, so that the glob in forward_networking
# matches. The stateful mock binaries of bin/ prepend to the PATH and
# derive what the kernel would know from the shared mock log.
setup_sandbox () {
	mkdir -p "${SANDBOX}/etc" \
		"${SANDBOX}/proc/sys/net/ipv4" \
		"${SANDBOX}/proc/sys/net/ipv6/conf/all" \
		"${SANDBOX}/sys/devices/virtual/net/ikhb/bridge" \
		"${SANDBOX}/sys/devices/virtual/net/ikeb1-3/bridge"
	touch "${SANDBOX}/sys/devices/virtual/net/ikhb/bridge/group_fwd_mask"
	touch "${SANDBOX}/sys/devices/virtual/net/ikeb1-3/bridge/group_fwd_mask"
	reset_conf
	sed -e "s|/etc/ikvswitch/ikvswitch.conf|${SANDBOX}/etc/ikvswitch.conf|g" \
		-e "s|/proc/sys/net/ipv4/ip_forward|${SANDBOX}/proc/sys/net/ipv4/ip_forward|g" \
		-e "s|/proc/sys/net/ipv6/conf/all/forwarding|${SANDBOX}/proc/sys/net/ipv6/conf/all/forwarding|g" \
		-e "s|/sys/devices/virtual/net|${SANDBOX}/sys/devices/virtual/net|g" \
		"${SCRIPT_SRC}" >"${SANDBOX}/ikvswitch-host-networking"
	chmod +x "${SANDBOX}/ikvswitch-host-networking"
}

# Restores the pristine configuration file from the repository.
reset_conf () {
	cp "${CONF_SRC}" "${SANDBOX}/etc/ikvswitch.conf"
}

# Runs the patched ikvswitch-host-networking with the given arguments,
# capturing its output in OUT and its return code in NET_RC. The mock
# log is cumulative across runs, so that the stateful mocks know what
# a previous run created: segment() extracts the log lines produced
# by the last run.
run_net () {
	IKV_NET_MOCK_LOG=${SANDBOX}/net-mock.log
	export IKV_NET_MOCK_LOG
	[ -r "${IKV_NET_MOCK_LOG}" ] || : >"${IKV_NET_MOCK_LOG}"
	PREV_LINES=$(wc -l <"${IKV_NET_MOCK_LOG}")
	set +e
	OUT=$(PATH=${TESTDIR}/bin:${PATH} sh "${SANDBOX}/ikvswitch-host-networking" "$@" 2>&1)
	NET_RC=$?
	set -e
}

# Writes the mock log lines produced by the last run to $1.
segment () {
	tail -n +$(( PREV_LINES + 1 )) "${IKV_NET_MOCK_LOG}" >"${1}"
}

# Counts the creation operations in the mock log or in a segment.
creation_count () {
	grep -cE '^(addbr |addif |tuntap add |link add )' "${1}" || true
}

assert_eq () {
	if [ "${2}" != "${3}" ] ; then
		echo "assert_eq ${1}: got [${2}], expected [${3}]"
		exit 1
	fi
}

assert_contains () {
	case "${2}" in
	*"${3}"*) ;;
	*)
		echo "assert_contains ${1}: [${2}] does not contain [${3}]"
		exit 1
	;;
	esac
}

mock_has () {
	if ! grep -Fq -- "${2}" "${SANDBOX}/net-mock.log" ; then
		echo "mock_has ${1}: mock log does not contain [${2}]"
		exit 1
	fi
}

mock_count () {
	grep -cF -- "${1}" "${SANDBOX}/net-mock.log" || true
}
