#!/usr/bin/env bash
#
# Run unit tests and create report
#
# TESTFLAGS - add additional test flags. Ex:
#
#   TESTFLAGS='-v -run TestBuild' hack/test/unit
#
# TESTDIRS - run tests for specified packages. Ex:
#
#   TESTDIRS='./pkg/term' hack/test/unit
#
set -eux -o pipefail

BUILDFLAGS=(-tags 'netgo journald')
TESTFLAGS+=" -test.timeout=${TIMEOUT:-5m}"
TESTDIRS="${TESTDIRS:-./...}"

: "${api_pkg_list:=}"
: "${client_pkg_list:=}"

mkdir -p bundles

case "$TESTDIRS" in
	"./api"* | "./...")
		api_pkg_list=$(go -C ./api list -mod=readonly "${TESTDIRS/#\.\/api/\.}")
		;;
esac
if [ -n "${api_pkg_list}" ]; then
	# These are tests for a separate module. Run tests for this module with
	# '-mod=readonly' to prevent the "vendor/" directory being used, which
	# does have a copy of these files, but does not contain test files.
	#
	# From the Go documentation https://golang.org/ref/mod:
	#
	# - `-mod=mod` tells the go command to ignore the vendor directory and to
	#   automatically update `go.mod`, for example, when an imported package
	#   is not provided by any known module.
	# - `-mod=readonly` tells the go command to ignore the vendor directory
	#   and to report an error if `go.mod` needs to be updated.
	(
		cd api
		gotestsum --format=standard-quiet --jsonfile=../bundles/api-go-test-report.json --junitfile=../bundles/api-junit-report.xml -- \
			"${BUILDFLAGS[@]}" \
			-cover \
			-coverprofile=../bundles/api-coverage.out \
			-covermode=atomic \
			-mod=readonly \
			${TESTFLAGS} \
			${api_pkg_list} || exit $?
	)
fi

case "$TESTDIRS" in
	"./client"* | "./...")
		client_pkg_list=$(go -C ./client list -mod=readonly "${TESTDIRS/#\.\/client/\.}")
		;;
esac
if [ -n "${client_pkg_list}" ]; then
	# These are tests for a separate module. Run tests for this module with
	# '-mod=readonly' to prevent the "vendor/" directory being used, which
	# does have a copy of these files, but does not contain test files.
	#
	# From the Go documentation https://golang.org/ref/mod:
	#
	# - `-mod=mod` tells the go command to ignore the vendor directory and to
	#   automatically update `go.mod`, for example, when an imported package
	#   is not provided by any known module.
	# - `-mod=readonly` tells the go command to ignore the vendor directory
	#   and to report an error if `go.mod` needs to be updated.
	(
		cd client
		gotestsum --format=standard-quiet --jsonfile=../bundles/client-go-test-report.json --junitfile=../bundles/client-junit-report.xml -- \
			"${BUILDFLAGS[@]}" \
			-cover \
			-coverprofile=../bundles/client-coverage.out \
			-covermode=atomic \
			-mod=readonly \
			${TESTFLAGS} \
			${client_pkg_list} || exit $?
	)
fi

case "$TESTDIRS" in
	"./api"* | "./client"*)
		# modules are handled above
		;;
	*)
		exclude_paths='/vendor/|/integration'
		pkgs=$(go list "$TESTDIRS" | grep -vE "($exclude_paths)")

		pkg_list=$(echo "${pkgs}" | grep --fixed-strings -v "/libnetwork" || :)
		libnetwork_pkg_list=$(echo "${pkgs}" | grep --fixed-strings "/libnetwork" || :)

		echo "${libnetwork_pkg_list}" | grep --fixed-strings "libnetwork/drivers/bridge" \
			&& if ! type docker-proxy; then
				hack/make.sh binary-proxy install-proxy
			fi
		;;
esac

if [ -n "${pkg_list}" ]; then
	gotestsum --format=standard-quiet --jsonfile=bundles/go-test-report.json --junitfile=bundles/junit-report.xml -- \
		"${BUILDFLAGS[@]}" \
		-cover \
		-coverprofile=bundles/coverage.out \
		-covermode=atomic \
		${TESTFLAGS} \
		${pkg_list}
fi

if [ -n "${libnetwork_pkg_list}" ]; then
	rerun_flaky=1

	gotest_extra_flags="-skip=TestFlaky.*"
	# Custom -run passed, don't run flaky tests separately.
	if echo "$TESTFLAGS" | grep -Eq '(-run|-test.run)[= ]'; then
		rerun_flaky=0
		gotest_extra_flags=""
	fi

	# libnetwork tests invoke iptables, and cannot be run in parallel. Execute
	# tests within /libnetwork with '-p=1' to run them sequentially. See
	# https://github.com/moby/moby/issues/42458#issuecomment-873216754 for details.
	gotestsum --format=standard-quiet --jsonfile=bundles/libnetwork-go-test-report.json --junitfile=bundles/libnetwork-junit-report.xml \
		-- "${BUILDFLAGS[@]}" \
		-cover \
		-coverprofile=bundles/libnetwork-coverage.out \
		-covermode=atomic \
		-p=1 \
		${gotest_extra_flags} \
		${TESTFLAGS} \
		${libnetwork_pkg_list}

	if [ $rerun_flaky -eq 1 ]; then
		gotestsum --format=standard-quiet --jsonfile=bundles/libnetwork-flaky-go-test-report.json --junitfile=bundles/libnetwork-flaky-junit-report.xml \
			--packages "${libnetwork_pkg_list}" \
			--rerun-fails=4 \
			-- "${BUILDFLAGS[@]}" \
			-cover \
			-coverprofile=bundles/libnetwork-flaky-coverage.out \
			-covermode=atomic \
			-p=1 \
			-test.run 'TestFlaky.*' \
			${TESTFLAGS}
	fi
fi
