#!/bin/bash
#
# This script runs Go language unit tests for the image-inspector repository. Arguments to this script
# are parsed as a list of packages to test until the first argument starting with '-' or '--' is
# found. That argument and all following arguments are interpreted as flags to be passed directly
# to `go test`. If no arguments are given, then "all" packages are tested.
#
# Coverage reports and jUnit XML reports can be generated by this script as well, but both cannot
# be generated at once.
#
# This script consumes the following parameters as environment variables:
#  - DRY_RUN:             prints all packages that would be tested with the args that would be used and exits
#  - TIMEOUT:             the timeout for any one unit test (default '60s')
#  - DETECT_RACES:        toggles the 'go test' race detector (defaults '-race')
#  - COVERAGE_SPEC:       a set of flags for 'go test' that specify the coverage behavior (default '-cover -covermode=atomic')
#  - GOTEST_FLAGS:        any other flags to be sent to 'go test'
set -o errexit
set -o nounset
set -o pipefail

function exit_trap() {
    local return_code=$?
    echo "[DEBUG] Exit trap handler got return code ${return_code}"

    end_time=$(date +%s)

    if [[ "${return_code}" -eq "0" ]]; then
        verb="succeeded"
    else
        verb="failed"
    fi

    echo "$0 ${verb} after $((${end_time} - ${start_time})) seconds"
    exit "${return_code}"
}

trap exit_trap EXIT

start_time=$(date +%s)
CODE_ROOT=$(dirname "${BASH_SOURCE}")/..
source "${CODE_ROOT}/hack/common.sh"
source "${CODE_ROOT}/hack/util.sh"
cd "${CODE_ROOT}"
ii::log::install_errexit
ii::build::setup_env

# Internalize environment variables we consume and default if they're not set
dry_run="${DRY_RUN:-}"
test_timeout="${TIMEOUT:-120s}"
detect_races="${DETECT_RACES:-true}"
coverage_spec="${COVERAGE_SPEC:--cover -covermode atomic}"
gotest_flags="${GOTEST_FLAGS:-}"

# determine if user wanted verbosity
verbose=
if [[ "${gotest_flags}" =~ -v( |$) ]]; then
    verbose=true
fi

# Build arguments for 'go test'
if [[ -z "${verbose}" ]]; then
    gotest_flags+=" -v"
fi

if [[ "${detect_races}" == "true" ]]; then
    gotest_flags+=" -race"
fi

# check to see if user has not disabled coverage mode
if [[ -n "${coverage_spec}" ]]; then
    # if we have a coverage spec set, we add it. '-race' implies '-cover -covermode atomic'
    # but specifying both at the same time does not lead to an error so we can add both specs
    gotest_flags+=" ${coverage_spec}"
fi

# check to see if user has not disabled test timeouts
if [[ -n "${test_timeout}" ]]; then
    gotest_flags+=" -timeout ${test_timeout}"
fi

# list_test_packages_under lists all packages containing Golang test files that we want to run as unit tests
# under the given base dir in the OpenShift Origin tree
function list_test_packages_under() {
    local basedir=$@

    # we do not quote ${basedir} to allow for multiple arguments to be passed in as well as to allow for
    # arguments that use expansion, e.g. paths containing brace expansion or wildcards
    find ${basedir} -not \(                   \
        \(                                    \
              -path 'vendor'                  \
              -o -path '*_output'             \
              -o -path '*.git'                \
              -o -path '*vendor/*'            \
              -o -path '*test/*'              \
        \) -prune                             \
    \) -name '*_test.go' | xargs -n1 dirname | sort -u | xargs -n1 printf "${II_GO_PACKAGE}/%s\n"
}

# Break up the positional arguments into packages that need to be tested and arguments that need to be passed to `go test`
package_args=
for arg in "$@"; do
    if [[ "${arg}" =~ -.* ]]; then
        # we found an arg that begins with a dash, so we stop interpreting arguments
        # henceforth as packages and instead interpret them as flags to give to `go test`
        break
    fi
    # an arg found before the first flag is a package
    package_args+=" ${arg}"
    shift
done
gotest_flags+=" $*"

# Determine packages to test
vendor_package_prefix="vendor/"
test_packages=
if [[ -n "${package_args}" ]]; then
    for package in ${package_args}; do
        # If we're trying to recursively test a package under vendor, strip the vendor prefix so go test can find the packages correctly
        if [[ "${package}" == "${vendor_package_prefix}"*"/..." ]]; then
            test_packages="${test_packages} ${package:${#vendor_package_prefix}}"
        else
            test_packages="${test_packages} ${II_GO_PACKAGE}/${package}"
        fi
    done
else
    # If no packages are given to test, we need to generate a list of all packages with unit tests
    ii_test_packages="$(list_test_packages_under '*')"
    test_packages="${ii_test_packages}"
fi

go test ${gotest_flags} -tags 'containers_image_openpgp exclude_graphdriver_devicemapper exclude_graphdriver_btrfs' ${test_packages}
