# Copyright 2020 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Support for unittest.bash

#### Set up the test environment.

set -euo pipefail

cat_jvm_log () {
  if [[ "$log_content" =~ \
      "(error code:".*", error message: '".*"', log file: '"(.*)"')" ]]; then
    echo >&2
    echo "Content of ${BASH_REMATCH[1]}:" >&2
    cat "${BASH_REMATCH[1]}" >&2
  fi
}

# Print message in "$1" then exit with status "$2"
die () {
  # second argument is optional, defaulting to 1
  local status_code=${2:-1}
  # Stop capturing stdout/stderr, and dump captured output
  if [[ "$CAPTURED_STD_ERR" -ne 0 || "$CAPTURED_STD_OUT" -ne 0 ]]; then
    restore_outputs
    if [[ "$CAPTURED_STD_OUT" -ne 0 ]]; then
        cat "${TEST_TMPDIR}/captured.out"
        CAPTURED_STD_OUT=0
    fi
    if [[ "$CAPTURED_STD_ERR" -ne 0 ]]; then
        cat "${TEST_TMPDIR}/captured.err" 1>&2
        cat_jvm_log "$(cat "${TEST_TMPDIR}/captured.err")"
        CAPTURED_STD_ERR=0
    fi
  fi

  if [[ -n "${1-}" ]] ; then
      echo "$1" 1>&2
  fi
  if [[ -n "${BASH-}" ]]; then
    local caller_n=0
    while [[ $caller_n -lt 4 ]] && \
        caller_out=$(caller $caller_n 2>/dev/null); do
      test $caller_n -eq 0 && echo "CALLER stack (max 4):"
      echo "  $caller_out"
      let caller_n=caller_n+1
    done 1>&2
  fi
  if [[ -n "${status_code}" && "${status_code}" -ne 0 ]]; then
      exit "$status_code"
  else
      exit 1
  fi
}

# Print message in "$1" then record that a non-fatal error occurred in
# ERROR_COUNT
ERROR_COUNT="${ERROR_COUNT:-0}"
error () {
  if [[ -n "$1" ]] ; then
      echo "$1" 1>&2
  fi
  ERROR_COUNT=$(($ERROR_COUNT + 1))
}

# Die if "$1" != "$2", print $3 as death reason
check_eq () {
  [[ "$1" = "$2" ]] || die "Check failed: '$1' == '$2' ${3:+ ($3)}"
}

# Die if "$1" == "$2", print $3 as death reason
check_ne () {
  [[ "$1" != "$2" ]] || die "Check failed: '$1' != '$2' ${3:+ ($3)}"
}

# The structure of the following if statements is such that if '[[' fails
# (e.g., a non-number was passed in) then the check will fail.

# Die if "$1" > "$2", print $3 as death reason
check_le () {
  [[ "$1" -gt "$2" ]] || die "Check failed: '$1' <= '$2' ${3:+ ($3)}"
}

# Die if "$1" >= "$2", print $3 as death reason
check_lt () {
  [[ "$1" -lt "$2" ]] || die "Check failed: '$1' < '$2' ${3:+ ($3)}"
}

# Die if "$1" < "$2", print $3 as death reason
check_ge () {
  [[ "$1" -ge "$2" ]] || die "Check failed: '$1' >= '$2' ${3:+ ($3)}"
}

# Die if "$1" <= "$2", print $3 as death reason
check_gt () {
  [[ "$1" -gt "$2" ]] || die "Check failed: '$1' > '$2' ${3:+ ($3)}"
}

# Die if $2 !~ $1; print $3 as death reason
check_match ()
{
  expr match "$2" "$1" >/dev/null || \
    die "Check failed: '$2' does not match regex '$1' ${3:+ ($3)}"
}

# Run command "$1" at exit. Like "trap" but multiple atexits don't
# overwrite each other. Will break if someone does call trap
# directly. So, don't do that.
ATEXIT="${ATEXIT-}"
atexit () {
  if [[ -z "$ATEXIT" ]]; then
      ATEXIT="$1"
  else
      ATEXIT="$1 ; $ATEXIT"
  fi
  trap "$ATEXIT" EXIT
}

## TEST_TMPDIR
if [[ -z "${TEST_TMPDIR:-}" ]]; then
  export TEST_TMPDIR="$(mktemp -d ${TMPDIR:-/tmp}/bazel-test.XXXXXXXX)"
fi
if [[ ! -e "${TEST_TMPDIR}" ]]; then
  mkdir -p -m 0700 "${TEST_TMPDIR}"
  # Clean TEST_TMPDIR on exit
  atexit "rm -fr ${TEST_TMPDIR}"
fi

# Functions to compare the actual output of a test to the expected
# (golden) output.
#
# Usage:
#   capture_test_stdout
#   ... do something ...
#   diff_test_stdout "$TEST_SRCDIR/path/to/golden.out"

# Redirect a file descriptor to a file.
CAPTURED_STD_OUT="${CAPTURED_STD_OUT:-0}"
CAPTURED_STD_ERR="${CAPTURED_STD_ERR:-0}"

capture_test_stdout () {
  exec 3>&1 # Save stdout as fd 3
  exec 4>"${TEST_TMPDIR}/captured.out"
  exec 1>&4
  CAPTURED_STD_OUT=1
}

capture_test_stderr () {
  exec 6>&2 # Save stderr as fd 6
  exec 7>"${TEST_TMPDIR}/captured.err"
  exec 2>&7
  CAPTURED_STD_ERR=1
}

# Force XML_OUTPUT_FILE to an existing path
if [[ -z "${XML_OUTPUT_FILE:-}" ]]; then
  XML_OUTPUT_FILE=${TEST_TMPDIR}/ouput.xml
fi

# Functions to provide easy access to external repository outputs in the sibling
# repository layout.
#
# Usage:
#   bin_dir <repository name>
#   genfiles_dir <repository name>
#   testlogs_dir <repository name>

testlogs_dir() {
  echo $(bazel info bazel-testlogs | sed "s|bazel-out|bazel-out/$1|")
}
