#!/usr/bin/env bash

# Validate Chapel installation by compiling and executing an example job.
#
#
# Exits with zero status code when compilation and execution work as
# expected. Expects that chpl is available on the PATH and that CHPL_HOME is
# set correctly.
#
# It works by copying the test from $CHPL_HOME, which can  either be an
# installation from source/rpm or a working copy of the source repo, to a
# temporary directory in $HOME/.chpl/, then executing the binary and diffing
# the output with the .good file.
#
# Using a temporary directory is necessary for most installations, since the
# user running the tests will not have the permissions to modify $CHPL_HOME
# (e.g. a system wide chapel installation) nor would they want to clutter it
# with test artifacts.
#
# $HOME/.chpl/ is used instead of something like $TMPDIR (or /tmp) to improve
# the likelihood that multi locale tests will succeed. $HOME tends to be
# consistent and available on all nodes in distributed systems, unlike $TMPDIR
# which tends to be an in-memory filesystem exclusive to each node.
#
# To use something other than $HOME/.chpl, set the CHPL_CHECK_INSTALL_DIR
# environment variable.

function log_info()
{
    local msg=$@
    echo "[Info] ${msg}"
}

function log_warning()
{
    local msg=$@
    echo "[Warn] ${msg}"
}

function log_error()
{
    local msg=$@
    echo "[Fail] ${msg}" >&2
}

function log_debug()
{
    local msg=$@
    if [ "${CHPL_CHECK_DEBUG}" == "1" ]; then
        echo "[Debug] ${msg}" >&2
    fi
}

log_debug "Debug output is turned on, because \$CHPL_CHECK_DEBUG == 1"

# Base name for test job in examples
TEST_JOB_BASENAME=hello6-taskpar-dist

# Notify user we are running this script
log_info "Running minimal test script: \$CHPL_HOME/util/test/checkChplInstall"

BIN_NAME=chpl

CHPL_BIN="$(which ${BIN_NAME} 2> /dev/null)"

# Verify chpl is in $PATH
if [ -z "${CHPL_BIN}" ] ; then
    log_error "${BIN_NAME} not found. Make sure it available in the current PATH."
    exit 1
elif [ ! -x ${CHPL_BIN} ] ; then
    log_error "Found ${BIN_NAME} at ${CHPL_BIN} but it is not executable."
    exit 1
else
    log_info "Found executable ${BIN_NAME} in ${CHPL_BIN}."
fi

# Verify CHPL_HOME is correctly set.
if [ -z "${CHPL_HOME+x}" ] ; then
    log_error "CHPL_HOME is not set in environment."
    exit 1
elif [ ! -d ${CHPL_HOME} ] ; then
    log_error "CHPL_HOME is not a directory: ${CHPL_HOME}"
    exit 1
else
    log_info "Found \$CHPL_HOME directory: ${CHPL_HOME}"
fi

# Create ~/.chpl directory, if it does not already exist
CHPL_DIR=${CHPL_CHECK_INSTALL_DIR:-${HOME}/.chpl}
if [ ! -d ${CHPL_DIR} ] ; then
    log_info "${CHPL_DIR} does not exist. Creating it."
    mkdir -p ${CHPL_DIR} || { log_error "Failed to create ${CHPL_DIR}." && exit 1 ; }
    CHPL_DIR_REMOVE=1
else
    log_info "${CHPL_DIR} already exists. Using it."
    CHPL_DIR_REMOVE=0
fi

# Location of test job
if [ -d ${CHPL_HOME}/test/release/examples ] ; then
    # Install from source repo.
    TEST_DIR=${CHPL_HOME}/test/release/examples
    REL_TEST_DIR=test/release/examples
elif [ -d ${CHPL_HOME}/examples ] ; then
    # Install from tarball.
    TEST_DIR=${CHPL_HOME}/examples
    REL_TEST_DIR=examples
else
    log_error "Could not find test cases in CHPL_HOME: ${CHPL_HOME}."
    exit 1
fi

TMP_TEST_DIR="$(mktemp -d ${CHPL_DIR}/chapel-test-XXXXX)"
log_info "Temporary test job directory: ${TMP_TEST_DIR}"

TEST_JOB=${TEST_JOB_BASENAME}

# Collect comm protocol environment variables (lowercase to avoid conflicts)
chpl_comm="$($CHPL_HOME/util/chplenv/chpl_comm.py)"
chpl_launcher="$($CHPL_HOME/util/chplenv/chpl_launcher.py)"

# Log file for compile output
TEST_COMP_OUT=${TEST_JOB}.comp.out

# Necessary to suppress warnings when WARNINGS=1 (e.g. nightly build settings)
COMP_FLAGS="--cc-warnings"

# Compile test job into temporary directory
log_info "Compiling \$CHPL_HOME/${REL_TEST_DIR}/${TEST_JOB}.chpl"
${CHPL_BIN} ${TEST_DIR}/${TEST_JOB}.chpl ${COMP_FLAGS} -o ${TMP_TEST_DIR}/${TEST_JOB} > ${TMP_TEST_DIR}/${TEST_COMP_OUT} 2>&1

# Check that compile was successful
COMPILE_STATUS=$?
if [ ${COMPILE_STATUS} -ne 0 ]; then
    log_error "Test job failed to compile - Chapel is not installed correctly"
    log_error "Compilation output:"
    cat ${TMP_TEST_DIR}/${TEST_COMP_OUT}
    exit 1
elif [ -s ${TMP_TEST_DIR}/${TEST_COMP_OUT} ]; then
    log_error "Test job compiled with output - Chapel is not installed correctly"
    log_error "Compilation output:"
    cat ${TMP_TEST_DIR}/${TEST_COMP_OUT}
    exit 1
else
    log_info "Test job compiled into ${TMP_TEST_DIR}/${TEST_JOB}"
fi

# Cleanup the tmp directory whenever exit is invoked
function cleanup_tmp_dir()
{
    log_debug "Removing ${TMP_TEST_DIR}"
    rm -rf ${TMP_TEST_DIR}
    if [ "${CHPL_DIR_REMOVE}" == "1" ]; then
        log_debug "Removing ${CHPL_DIR}"
        rm -rf ${CHPL_DIR}
    fi
}
trap cleanup_tmp_dir EXIT

# Find number of locales and .good file
if [ ${chpl_comm} == "none" ]; then
    NUMLOCALES=1
    GOOD=${TEST_DIR}/${TEST_JOB}.comm-none.good
else
    NUMLOCALES="$(cat ${TEST_DIR}/NUMLOCALES)"
    GOOD=${TEST_DIR}/${TEST_JOB}.good
fi

# Check for valid launchers
if [ ${chpl_launcher} == "slurm-srun" -o ${chpl_launcher} == "amudprun" -o ${chpl_launcher} == "none" ]; then
    log_info "\$CHPL_LAUNCHER=${chpl_launcher} is compatible with test script."
else
    log_warning "\$CHPL_LAUNCHER=${chpl_launcher} is not compatible with test script."
    log_warning "This does not necessarily indicate that your Chapel installation is incorrect."
    log_warning "See \$CHPL_HOME/doc/launcher.rst for information on manually launching Chapel programs."
    # This exit code should be recognized as a failure to complete check, but
    # not necessarily failure of build
    exit 10
fi

TEST_EXEC_OUT=${TEST_JOB}.exec.out

# Run compiled binary with parsed execution options
(
    cd ${TMP_TEST_DIR}

    EXEC_OPTS="$(cat ${TEST_DIR}/${TEST_JOB}.execopts)"

    log_info "Running test job."
    ./${TEST_JOB} -nl${NUMLOCALES} ${EXEC_OPTS} | sort > ${TEST_EXEC_OUT} 2>&1
    log_info "Test job complete."

    # Check result
    DIFF_OUTPUT=$(diff -q ${TEST_EXEC_OUT} ${GOOD})

    # Return success code (0) if diff is good
    if [ -z "${DIFF_OUTPUT}" ]; then
    echo "SUCCESS: 'make check' passed!"
    exit 0
    else
        # Rerun test job with -v flag to help user identify discrepancy
        log_error "There was an issue with the installation, test job output incorrect."
        echo ""
        echo "== Verbose Test Job Execution Output =="
        ./${TEST_JOB} -nl${NUMLOCALES} ${EXEC_OPTS} -v
        echo ""
        echo "== Diff Log =="
        echo "$(diff ${TEST_EXEC_OUT} ${GOOD})"
        # This exit code should be recognized as successfully building, but
        # with some errors
        exit 20
    fi
)

