#!/bin/bash

set -e

DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SLOW=
TAGS=
PARALLEL=
RACE="-race -covermode=atomic"
TIMEOUT=10m
VERBOSE=

usage() {
    echo "$0 [-slow] [-in-container foo] [-netgo] [-timeout 1m]"
}

while [ $# -gt 0 ]; do
    case "$1" in
        "-v")
            VERBOSE="-v"
            shift 1
            ;;
        "-slow")
            SLOW=true
            shift 1
            ;;
        "-no-race")
            RACE=
            shift 1
            ;;
        "-netgo")
            TAGS="netgo"
            shift 1
            ;;
        "-tags")
            TAGS="$2"
            shift 2
            ;;
        "-p")
            PARALLEL=true
            shift 1
            ;;
        "-timeout")
            TIMEOUT=$2
            shift 2
            ;;
        *)
            usage
            exit 2
            ;;
    esac
done

GO_TEST_ARGS=(-tags "${TAGS[@]}" -cpu 4 -timeout $TIMEOUT $VERBOSE)

if [ -n "$SLOW" ] || [ -n "$CIRCLECI" ] || [ -n "$CI" ]; then
    SLOW=true
fi

if [ -n "$SLOW" ]; then
    GO_TEST_ARGS=("${GO_TEST_ARGS[@]}" ${RACE})

    # shellcheck disable=SC2153
    if [ -n "$COVERDIR" ]; then
        coverdir="$COVERDIR"
    else
        coverdir=$(mktemp -d coverage.XXXXXXXXXX)
    fi

    mkdir -p "$coverdir"
fi

fail=0

if [ -z "$TESTDIRS" ]; then
    # NB: Relies on paths being prefixed with './'.
    TESTDIRS=($(git ls-files -- 'cmd/*_test.go' 'pkg/*_test.go' 'tools/*_test.go' | xargs -n1 dirname | sort -u | sed -e 's|^|./|'))
else
    # TESTDIRS on the right side is not really an array variable, it
    # is just a string with spaces, but it is written like that to
    # shut up the shellcheck tool.
    TESTDIRS=($(for d in ${TESTDIRS[*]}; do echo "$d"; done))
fi

# If running on circle, use the scheduler to work out what tests to run on what shard
if [ -n "$CIRCLECI" ] && [ -z "$NO_SCHEDULER" ] && [ -x "$DIR/sched" ]; then
    PREFIX=$(go list -e ./ | sed -e 's/\//-/g')
    TESTDIRS=($(echo "${TESTDIRS[@]}" | "$DIR/sched" sched "$PREFIX-$CIRCLE_PROJECT_USERNAME-$CIRCLE_PROJECT_REPONAME-$CIRCLE_BUILD_NUM" "$CIRCLE_NODE_TOTAL" "$CIRCLE_NODE_INDEX"))
    echo "${TESTDIRS[@]}"
fi

PACKAGE_BASE=$(go list -e ./)

run_test() {
    local dir=$1

    local GO_TEST_ARGS_RUN=("${GO_TEST_ARGS[@]}")
    if [ -n "$SLOW" ]; then
        local COVERPKGS
        COVERPKGS=$( (
            go list "$dir"
            go list -f '{{join .Deps "\n"}}' "$dir" | grep -v "vendor" | grep "^$PACKAGE_BASE/"
        ) | paste -s -d, -)
        local output
        output=$(mktemp "$coverdir/unit.XXXXXXXXXX")
        local GO_TEST_ARGS_RUN=("${GO_TEST_ARGS[@]}" -coverprofile=$output -coverpkg=$COVERPKGS)
    fi

    local START
    START=$(date +%s)
    if ! go test "${GO_TEST_ARGS_RUN[@]}" "$dir"; then
        fail=1
    fi
    local END
    END=$(date +%s)
    local RUNTIME=$((END - START))

    # Report test runtime when running on circle, to help scheduler
    if [ -n "$CIRCLECI" ] && [ -z "$NO_SCHEDULER" ] && [ -x "$DIR/sched" ]; then
        "$DIR/sched" time "$dir" "$RUNTIME"
    fi
}

for dir in "${TESTDIRS[@]}"; do
    if [ -n "$PARALLEL" ]; then
        run_test "$dir" &
    else
        run_test "$dir"
    fi
done

if [ -n "$PARALLEL" ]; then
    wait
fi

if [ -n "$SLOW" ] && [ -z "$COVERDIR" ]; then
    cover "$coverdir"/* >profile.cov
    rm -rf "$coverdir"
    go tool cover -html=profile.cov -o=coverage.html
    go tool cover -func=profile.cov | tail -n1
fi

exit $fail
