#!/bin/bash
# Uncomment next line to print each bash command before it is executed
#set -x

# function to list the log lines that contains all the failing test report in ctest log file
# first argument = file to check
# should contains all the lines that matches:
# x/N Test #nnn: mame-of-the-test ....................***Failed:   s.ms sec
# x/N Test #nnn: mame-of-the-test ....................***SegFault:   s.ms sec
# x/N Test #nnn: mame-of-the-test ....................***Exception:   s.ms sec
# x/N Test #nnn: mame-of-the-test ....................***Timeout:   s.ms sec
# x/N Test #nnn: mame-of-the-test ....................***Exit code 0x...
# But not the lines that are sometimes produced on Windows, e.g.
# x/N Test #nnn: name-of-the-text ....................Exit code 0xc000007b
# ***Exception:   0.41 sec
# (***Exception is on it own as there was a linefeed just after the Exit code text)
#
# These lines are later on filtered to extract the name of the test (so it can be rerun)
listFailedTests() {
    echo "$(grep -e "\*\*\*Failed" -e "SegFault" -e "^[^\*].*\*\****Exception" -e "\*\*\*Timeout" -e ".*\.\.Exit code 0x" $1 )"
}
# count the number of CMake Errors
numberOfCMakeErrors() {
    echo "$(grep -e "CMake Error at" $1 | wc -l)"
}

# Path needs to use forward slashes
# This is ok on Linux but since gitlab-runner 11.7 on windows all path variables use backward slash instead of forward slash
# → Replace all backslash to forward slash 
export PROJECT_SOURCE_DIR=${PROJECT_SOURCE_DIR//\\//}
export CI_PROJECT_DIR=${CI_PROJECT_DIR//\\//}
export JUNIT_OUTPUT_DIRECTORY="$CI_PROJECT_DIR/ctest-results"
mkdir -p "$JUNIT_OUTPUT_DIRECTORY"
echo "Job $CI_JOB_NAME"
echo "PROJECT_SOURCE_DIR=$PROJECT_SOURCE_DIR"
echo "CI_PROJECT_DIR=$CI_PROJECT_DIR"
echo "JUNIT_OUTPUT_DIRECTORY=$JUNIT_OUTPUT_DIRECTORY"

if ! grep -q TRIGGER_STAGE_TEST "${PROJECT_LOG_DIR}/trigger-stage.txt"; then
    echo "Job skipped as /test flag not in commit message and CAMITK_CI_STAGE < $STAGE_CONFIGURE"; 
    exit 1;
fi

if [[ "$OS" != "win10" && "$OS" != "win11" ]]; then
    echo "===== Configuring xvfb =====" > >(tee --append ${PROJECT_LOG_DIR}/test.log) 2>&1
    # Starts the server first (try to avoid unexpected and random "QXcbConnection: Could not connect to display :99")
    export DISPLAY=":98"
    # create a specific file for xauth
    export XAUTHORITY=$(mktemp)
    # remove +iglx for now 
    Xvfb $DISPLAY -screen 0 1280x1024x16 -ac -nolisten tcp -nolisten unix -auth $XAUTHORITY & > >(tee --append ${PROJECT_LOG_DIR}/test.log) 2>&1
    trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
    # give some times to start the Xvfb
    sleep 10
    # On linux, use QT_QPA_PLATFORM=xcb (and not offscreen)
    # see also https://doc.qt.io/qt-5/embedded-linux.html#linuxfb 
    export QT_QPA_FONTDIR=/usr/share/fonts/truetype/dejavu
    export QT_QPA_PLATFORM=xcb
else
    # On windows, run test without screen/opengl/...
    export QT_QPA_PLATFORM=offscreen
    # Required to redirect qDebug() to stdout and get the qDebug messages in the log
    export QT_ASSUME_STDERR_HAS_CONSOLE=1
fi

echo "===== Running ctest ====="

# note: JUNIT_OUTPUT_DIRECTORY is relative to $PROJECT_BUILD_DIR
ctest --extra-verbose \
      --output-on-failure \
      -DCTEST_SITE="$CDASH_SITE" \
      -DCI_MODE="$CAMITK_CI_MODE" \
      -DCI_ID="P $CI_PIPELINE_ID - J $CI_BUILD_ID" \
      -DCI_BRANCH="$CI_COMMIT_REF_NAME" \
      -DCI_BUILD_SETTINGS="$COMPILER_CONFIG" \
      -DCI_PROJECT_LOG_DIRECTORY="$CI_PROJECT_DIR/$PROJECT_LOG_DIR" \
      -DCTEST_SOURCE_DIRECTORY="$PROJECT_SOURCE_DIR" \
      -DCTEST_BINARY_DIRECTORY="$PROJECT_BUILD_DIR" \
      -DJUNIT_OUTPUT_DIRECTORY="$JUNIT_OUTPUT_DIRECTORY" \
      -S $PROJECT_SOURCE_DIR/sdk/cmake/ctest/ci-test.cmake > >(tee --append ${PROJECT_LOG_DIR}/test.log | grep --line-buffered -e "Test \#" -e "^--") 2>&1

# Using ctest with its --rerun-failed flag in order to only rerun the failed tests 
# seems to be buggy.
# See CMake bugs:
# - https://gitlab.kitware.com/cmake/cmake/issues/17767
# - https://gitlab.kitware.com/cmake/cmake/issues/16314
# Otherwise it would only requires to add the following lines:
#     echo "===== Re-running failed tests ====="
#     ctest --extra-verbose \
#         --rerun-failed \
#         -DCTEST_SITE="$CDASH_SITE" \
#         -DCI_MODE="$CAMITK_CI_MODE" \
#         -DCI_ID="P $CI_PIPELINE_ID - J $CI_BUILD_ID" \
#         -DCI_BRANCH="$CI_COMMIT_REF_NAME" \
#         -DCI_BUILD_SETTINGS="$COMPILER_CONFIG" \
#         -DCI_PROJECT_LOG_DIRECTORY="$CI_PROJECT_DIR/$PROJECT_LOG_DIR" \
#         -DCTEST_SOURCE_DIRECTORY="$PROJECT_SOURCE_DIR" \
#         -DCTEST_BINARY_DIRECTORY="$PROJECT_BUILD_DIR" \
#         -S $PROJECT_SOURCE_DIR/sdk/cmake/ctest/ci-test.cmake > >(tee --append ${PROJECT_LOG_DIR}/test.log | grep --line-buffered -e "Test \#" -e "^--") 2>&1

# So as for now, parse the log and do a manual rerun
echo
echo "===== Not run (disabled) tests ====="
grep -e "Not Run" $CI_PROJECT_DIR/$PROJECT_LOG_DIR/test.log

# as ctest return a strange 255 error, check the log
if grep --quiet "Fatal error" $CI_PROJECT_DIR/$PROJECT_LOG_DIR/ci-test.log; then
    echo
    echo "===== Fatal errors ====="
    echo "Found fatal error in $CI_PROJECT_DIR/$PROJECT_LOG_DIR/ci-test.log"
    echo 
    
    testToReRun=$(listFailedTests $CI_PROJECT_DIR/$PROJECT_LOG_DIR/test.log)
    nrOfFailed=$(echo "$testToReRun" | wc -l)
    echo "===== $nrOfFailed failed test(s) ====="
    echo "$testToReRun"
    echo
    
    echo "===== Re-running failed tests ====="
    echo "Checking $CI_PROJECT_DIR/$PROJECT_LOG_DIR/test.log for failed test" > ${PROJECT_LOG_DIR}/rerun-test.log
    echo "" >> ${PROJECT_LOG_DIR}/rerun-test.log
    # loop over failed tests
    echo "$testToReRun" | while IFS= read -r line; do 
        testname=$(echo $line | sed -r 's/.+[0-9]+:\s(.*)\s\..*/\1/g' | cut -f1 -d" ")
        echo "Re-running $testname..." > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1

        reason=$(echo $line | sed -r 's/.*\s\.\.\.(.*)/\1/g')
        echo "Reason:" > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1
        # add ----> at the beginning of the line to distinguished real error log during rerunning 
        echo "----> $reason" > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1
        echo "" > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1
        
        echo "Exact command:"  >> ${PROJECT_LOG_DIR}/rerun-test.log
        echo "ctest --extra-verbose \
              --output-on-failure \
              -DCTEST_SITE=\"$CDASH_SITE\" \
              -DCI_MODE=\"$CAMITK_CI_MODE\" \
              -DCI_ID=\"P $CI_PIPELINE_ID - J $CI_BUILD_ID\" \
              -DCI_BRANCH=\"$CI_COMMIT_REF_NAME\" \
              -DCI_BUILD_SETTINGS=\"$COMPILER_CONFIG\" \
              -DCI_PROJECT_LOG_DIRECTORY=\"$CI_PROJECT_DIR/$PROJECT_LOG_DIR\" \
              -DCTEST_SOURCE_DIRECTORY=\"$PROJECT_SOURCE_DIR\" \
              -DCTEST_BINARY_DIRECTORY=\"$PROJECT_BUILD_DIR\" \
              -DJUNIT_OUTPUT_DIRECTORY="$JUNIT_OUTPUT_DIRECTORY" \
              -DRERUN_TESTNAME=$testname \
              -S $PROJECT_SOURCE_DIR/sdk/cmake/ctest/ci-rerun.cmake > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1"  >> ${PROJECT_LOG_DIR}/rerun-test.log

        echo "Re-run test result:" > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1
        ctest --extra-verbose \
              --output-on-failure \
              -DCTEST_SITE="$CDASH_SITE" \
              -DCI_MODE="$CAMITK_CI_MODE" \
              -DCI_ID="P $CI_PIPELINE_ID - J $CI_BUILD_ID" \
              -DCI_BRANCH="$CI_COMMIT_REF_NAME" \
              -DCI_BUILD_SETTINGS="$COMPILER_CONFIG" \
              -DCI_PROJECT_LOG_DIRECTORY="$CI_PROJECT_DIR/$PROJECT_LOG_DIR" \
              -DCTEST_SOURCE_DIRECTORY="$PROJECT_SOURCE_DIR" \
              -DCTEST_BINARY_DIRECTORY="$PROJECT_BUILD_DIR" \
              -DJUNIT_OUTPUT_DIRECTORY="$JUNIT_OUTPUT_DIRECTORY" \
              -DRERUN_TESTNAME=$testname \
              -S $PROJECT_SOURCE_DIR/sdk/cmake/ctest/ci-rerun.cmake > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1
        
        echo > >(tee --append ${PROJECT_LOG_DIR}/rerun-test.log) 2>&1
    done
    
    # check the rerun log (listFailedTests but remove the reason lines added in the lg)
    reRunFailed=$(listFailedTests ${PROJECT_LOG_DIR}/rerun-test.log | grep "^[^-].*" | grep -v "^Re-running")
    nrOfFailed=$(echo "$testToReRun" | wc -l)
    nrOfCMakErrors=$(numberOfCMakeErrors ${PROJECT_LOG_DIR}/rerun-test.log)
    if test -z "$reRunFailed"; then
        echo "===== All rerun tests passed ====="
        echo "Job passed"
        if test -z "$nrOfCMakErrors"; then
            echo "===== WARNING ====="
            echo "===== WARNING: Found ${nrOfCMakErrors} CMake Errors ====="
            echo "===== WARNING: Check cdash to make sure they are just false positives ====="
            grep -e "CMake Error at" $1
            echo "===== WARNING ====="
        fi
    else
        echo
        echo "===== Fatal errors in rerun tests ====="
        echo "Found fatal error in rerun test log $CI_PROJECT_DIR/$PROJECT_LOG_DIR/rerun-test.log"
        echo 
        
        
        echo "$reRunFailed failed test(s) during rerun:"
        echo "$reRunFailed" | while IFS= read -r line; do
            echo -n "- "
            echo $line | sed -r 's/.+[0-9]+:\s(.*)\s\..*/\1/g' | cut -f1 -d" "
        done
        if test -z "$nrOfCMakErrors"; then
            echo "===== Found ${nrOfCMakErrors} CMake Errors ====="
        fi
        echo
        echo "Job failed"
        echo
        
        # send error to the pipeline
        exit 1
    fi
else
    echo "===== All tests passed ====="
    echo "Everything OK. No test needed to be rerun."
    echo
    echo "Job passed"
    echo
fi

echo "===== finished at: $(date --rfc-2822) ====="