#!/usr/bin/env bash
# ^ "Shebang" line. Tells the OS to run this file with bash, found via the
#   PATH by /usr/bin/env, so it works whether bash lives in /bin or /usr/local/bin.

# -----------------------------------------------------------------------------
# smoke-test.sh (annotated version)
# Builds the OpenTAP Docker image and runs a plugin-free smoke-test plan.
# Run from the folder that holds the Dockerfile, plans/ and Settings/.
# Usage: ./smoke-test-annotated.sh [image-tag]
# -----------------------------------------------------------------------------

set -euo pipefail
# set -e          Exit immediately if any command fails (returns non-zero).
# set -u          Treat use of an undefined variable as an error, catching typos.
# set -o pipefail In a pipeline (a | b), report failure if ANY command fails,
#                 not just the last one. Together these make the script stop
#                 at the first problem instead of carrying on with bad state.

TAG="${1:-opentap-smoke:test}"
# TAG=...         Create a variable TAG, to hold the name of the Docker image. 
# ${1:-default}   Use the first command-line argument ($1) if one was given;
#                 otherwise fall back to "opentap-smoke:test". This is the
#                 image name and tag (name:tag) used for build and run.

PLATFORM_ARGS=()
# Create an empty bash array. It will hold extra docker options, if any are
# needed. An array (rather than a plain string) keeps each option intact.

if [[ "$(uname -s)" == "Darwin" && "$(uname -m)" == "arm64" ]]; then
# if [[ ... ]]    Bash test. Runs the block below only when the test is true.
# $(uname -s)     Runs "uname -s" and substitutes its output: the OS name.
#                 "Darwin" means macOS.
# $(uname -m)     The CPU architecture. "arm64" means Apple silicon.
# &&              Both conditions must be true.
  PLATFORM_ARGS=(--platform linux/amd64)
  # On Apple-silicon Macs, tell docker to build and run an x64 (amd64) image
  # under emulation, because the Dockerfile downloads the x64 OpenTAP build.
fi
# "fi" ends the if block ("if" spelled backwards).

mkdir -p Settings logs
# mkdir     Make directories.
# -p        Don't complain if they already exist; create parents if needed.
# Settings  The Dockerfile COPYs this folder, so it must exist (it can be empty).
# logs      Receives OpenTAP session logs and the console output from the run.

echo "== 1. Build image"
# Print a progress heading to the terminal.

docker build "${PLATFORM_ARGS[@]}" -t "$TAG" .
# docker build              Build an image from the Dockerfile.
# "${PLATFORM_ARGS[@]}"     Expand the array: either nothing, or
#                           "--platform linux/amd64" on Apple silicon.
# -t "$TAG"                 Tag (name) the resulting image, e.g. opentap-smoke:test.
# .                         The build context: the current folder, which holds
#                           the Dockerfile, plans/ and Settings/.

echo "== 2. Check OpenTAP inside the image"
# Progress heading.

docker run --rm "${PLATFORM_ARGS[@]}" --entrypoint tap "$TAG" package list --installed
# docker run                Start a container from the image.
# --rm                      Delete the container automatically when it exits.
# --entrypoint tap          Override the Dockerfile's ENTRYPOINT ("tap run")
#                           so we can run a different tap command.
# "$TAG"                    The image to run.
# package list --installed  Arguments passed to tap: list installed OpenTAP
#                           packages. If this works, OpenTAP and .NET are OK.

echo "== 3. Run the smoke-test plan"
# Progress heading.

set +e
# Temporarily turn OFF "exit on error" so a failing test run doesn't abort
# the script before we can report the failure ourselves.

docker run --rm "${PLATFORM_ARGS[@]}" \
  -v "$PWD/logs:/opt/tap/SessionLogs" \
  "$TAG" plans/MyPlan.TapPlan --non-interactive 2>&1 | tee logs/console.txt
# docker run --rm ...       Start a throwaway container (as above).
# \                         Line continuation: the command carries on to the
#                           next line. Nothing may follow the backslash.
# -v host:container         Mount a folder. $PWD/logs on your computer appears
#                           as /opt/tap/SessionLogs inside the container, so
#                           OpenTAP's session logs survive after it exits.
# $PWD                      The current working directory.
# "$TAG" plans/...          Run the image; the plan path is passed to the
#                           ENTRYPOINT, giving "tap run plans/MyPlan.TapPlan".
# --non-interactive         Tell tap never to wait for user input.
# 2>&1                      Send error output (stderr, stream 2) to the same
#                           place as normal output (stdout, stream 1).
# | tee logs/console.txt    Pipe the output to tee, which shows it on screen
#                           AND saves a copy to logs/console.txt.

RC=${PIPESTATUS[0]}
# PIPESTATUS is a bash array of exit codes from the last pipeline.
# [0] is the first command in it (docker run), not tee. Save it as RC
# ("return code"): 0 means success, anything else means failure.

set -e
# Turn "exit on error" back on for the rest of the script.

echo "== 4. Verify"
# Progress heading.

if [[ $RC -ne 0 ]]; then
# -ne             "Not equal" (numeric comparison). True if the run failed.
  echo "FAIL: tap run exited with code $RC"; exit 1
  # Print the failure and its exit code.
  # ;              Separates two commands on one line.
  # exit 1         Stop the script with a non-zero code, so CI marks it failed.
fi

if ! grep -q "SMOKE-TEST-OK" logs/console.txt; then
# grep            Search a file for text.
# -q              Quiet: print nothing, just succeed (found) or fail (not found).
# "SMOKE-TEST-OK" The marker line the plan's last step logs.
# !               Negate: the block runs when the marker was NOT found.
  echo "FAIL: marker line not found in output"; exit 1
  # The plan didn't reach its last step, so report failure and stop.
fi

echo "PASS: image builds, OpenTAP runs, plan completes (session logs in ./logs)"
# All checks passed. The script ends here with exit code 0 (success).
