#!/usr/bin/env bash

function print_usage {
    echo ''
    echo 'CoreCLR test runner wrapper script.'
    echo ''
    echo 'Run tests using run.sh, then rerun the failures, if any,'
    echo 'until the number of failures stabilizes. Thus, when running'
    echo 'flaky tests, or running tests on a flaky platform, only the'
    echo 'repeatable, "real", failures are reported.'
    echo ''
    echo 'Tests are rerun in sequential mode (passing --sequential to run.sh).'
    echo 'This hopefully avoids resource exhaustion and other parallel run problems.'
    echo ''
    echo 'A maximum number of iterations can be specified.'
    echo ''
    echo 'Command line:'
    echo ''
    echo 'runtesttilstable.sh [options] [arguments for run.sh]'
    echo ''
    echo 'Any unknown argument is passed directly to run.sh.'
    echo ''
    echo 'Optional arguments:'
    echo '  -h|--help                        : Show usage information.'
    echo '  --max-iterations=<count>         : Specify the maximum number of iterations. Default: 4.'
    echo ''
}

function exit_with_error {
    local errorMessage=$1
    local printUsage=$2

    if [ -z "$printUsage" ]; then
        ((printUsage = 0))
    fi

    echo "$errorMessage"
    if ((printUsage != 0)); then
        print_usage
    fi
    exit $EXIT_CODE_EXCEPTION
}

# Handle Ctrl-C. We will stop execution and print the results that
# we gathered so far.
function handle_ctrl_c {
    echo ""
    echo "*** Stopping... ***"
    print_results
    exit_with_error "Test run aborted by Ctrl+C."
}

# Register the Ctrl-C handler
trap handle_ctrl_c INT

# Where are we?
scriptPath=$(dirname $0)

# Exit code constants
readonly EXIT_CODE_SUCCESS=0       # Script ran normally.
readonly EXIT_CODE_EXCEPTION=1     # Script exited because something exceptional happened (e.g. bad arguments, Ctrl-C interrupt).
readonly EXIT_CODE_TEST_FAILURE=2  # Script completed successfully, but one or more tests failed.

# Argument variables
((maxIterations = 20))

# Handle arguments
__UnprocessedBuildArgs=

# We need to capture the --testRootDir argument so we know where the test pass/fail/skip files will be placed.
testRootDir=

# We need to handle the --playlist argument specially. The first run, we pass it through (if passed).
# After that, we use the --playlist argument ourselves, so we don't pass through the original one.
playlistArgument=

for i in "$@"
do
    case $i in
        -h|--help)
            print_usage
            exit $EXIT_CODE_SUCCESS
            ;;
        --max-iterations=*)
            maxIterations=${i#*=}
            ;;
        --playlist=*)
            playlistArgument=$i
            ;;
        --testRootDir=*)
            testRootDir=${i#*=}
            # Also pass it on to run.sh
            __UnprocessedBuildArgs="$__UnprocessedBuildArgs $i"
            ;;
        *)
            __UnprocessedBuildArgs="$__UnprocessedBuildArgs $i"
            ;;
    esac
done

# Check testRootDir; this check is also done by run.sh.

if [ -z "$testRootDir" ]; then
    echo "--testRootDir is required."
    print_usage
    exit $EXIT_CODE_EXCEPTION
fi
if [ ! -d "$testRootDir" ]; then
    echo "Directory specified by --testRootDir does not exist: $testRootDir"
    exit $EXIT_CODE_EXCEPTION
fi

# Now start running the tests.

nextcmd="${scriptPath}/run.sh ${playlistArgument} ${__UnprocessedBuildArgs}"
echo "Running: $nextcmd"
$nextcmd
exitCode=$?
if [ $exitCode -eq $EXIT_CODE_TEST_FAILURE ]; then
    # Now, we loop, rerunning the failed tests up to maxIterations times minus one
    # (the initial run counts as an iteration).
    ((totalRerunCount = $maxIterations - 1))
    for (( i=1; i<=$totalRerunCount; i++ )); do
        if [ ! -e "$testRootDir/coreclrtests.fail.txt" ]; then
            exit_with_error "Error: couldn't find $testRootDir/coreclrtests.fail.txt"
        fi

        num_errors=$(grep -c '' "$testRootDir/coreclrtests.fail.txt")
        echo "Test run failed with $num_errors errors:"
        cat "$testRootDir/coreclrtests.fail.txt"
        echo ''

        echo "Rerunning failures ($i of $totalRerunCount reruns)..."

        # Move the fail file to a different location, so it can be used without getting trashed by the
        # next run's error file.
        retryFile="$testRootDir/coreclrtests.retry.txt"
        if [ -e "$retryFile" ]; then
            rm -f "$retryFile"
            if [ -e "$retryFile" ]; then
                exit_with_error "Error: couldn't delete $retryFile"
            fi
        fi
        mv "$testRootDir/coreclrtests.fail.txt" "$retryFile"

        nextcmd="${scriptPath}/run.sh --sequential --playlist=${retryFile} ${__UnprocessedBuildArgs}"
        echo "Running: $nextcmd"
        $nextcmd
        exitCode=$?
        if [ $exitCode -ne $EXIT_CODE_TEST_FAILURE ]; then
            # Either success or exceptional failure; we're done. For test failure, we loop,
            # if we haven't hit the maximum number of allowed iterations.
            break
        fi
    done
fi

exit $exitCode
