#!/bin/sh
#
# Run the functional test suite in parallel via pabot.
#
# Usage:
#   ./run-parallel.sh [--processes N] [pabot-args...]
#
# Notes on parallelism:
#   * pabot splits at the *suite* level by default. Each worker process gets
#     its own port range (base + worker_index*100) and its own /tmp prefix
#     (/tmp/rspamd-functional-<worker_index>/), driven by PABOTEXECUTIONPOOLID
#     in test/functional/lib/vars.py.
#   * The 001_merged/ directory holds 30 sub-suites that share one rspamd
#     and one redis, so pabot keeps it on a single worker (long pole).
#   * Suites that talk to dummy_http / dummy_llm / dummy_http_early /
#     dummy_udp via Lua-side hardcoded URLs (test/functional/lua/*.lua and
#     a few configs/*.conf entries with literal :18080/:18081/:18083/:5005)
#     are NOT yet parallel-safe -- those Lua files would need to read ports
#     from env or be templated. The --exclude flag below skips the affected
#     suites until that follow-up lands.
#
# Requirements:
#   pip install --user robotframework robotframework-pabot psutil

set -eu

SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
TOPDIR=${RSPAMD_TOPDIR:-$(cd "$SCRIPT_DIR/../.." && pwd)}
INSTALLROOT=${RSPAMD_INSTALLROOT:-$TOPDIR/install}

PROCESSES=${PROCESSES:-$(getconf _NPROCESSORS_ONLN 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)}

# Halve the count if it's huge -- each suite spawns rspamd + redis, so we
# want to fit comfortably in RAM. Cap at 8 by default.
if [ "$PROCESSES" -gt 8 ]; then
    PROCESSES=8
fi

if ! command -v pabot >/dev/null 2>&1; then
    echo "pabot not found. Install with: pip install --user robotframework-pabot" >&2
    exit 1
fi

# Preflight: every dummy_* helper must be started through the centralized
# Start Dummy Service barrier (lib/rspamd.robot), never a bare Start Process,
# or the start/scan race that flakes under parallel execution comes back.
python3 "$SCRIPT_DIR/util/check_no_bare_dummy_start.py" || exit 1

# Suites that still bake dummy_http/llm/udp/http_early port numbers into
# Lua test scripts or configs. Track in task #5 follow-up; tag and exclude
# until those are templated.
EXCLUDE_TAGS=""
EXCLUDE_SUITES=""

# Pass through any caller args after we've handled --processes.
ARGS=""
while [ $# -gt 0 ]; do
    case "$1" in
        --processes)
            PROCESSES=$2
            shift 2
            ;;
        --processes=*)
            PROCESSES=${1#--processes=}
            shift
            ;;
        *)
            ARGS="$ARGS $1"
            shift
            ;;
    esac
done

echo "Running functional tests with pabot --processes $PROCESSES"
echo "Install root: $INSTALLROOT"

# Each suite has its own Suite Setup that starts rspamd, so we MUST split
# at suite level (the default for pabot). --testlevelsplit would scatter
# cases across workers and break the single-rspamd-per-suite contract.
# shellcheck disable=SC2086
RSPAMD_INSTALLROOT="$INSTALLROOT" exec pabot \
    --processes "$PROCESSES" \
    --removekeywords wuks \
    --exclude isbroken \
    $EXCLUDE_TAGS \
    $EXCLUDE_SUITES \
    -v RSPAMD_INSTALLROOT:"$INSTALLROOT" \
    $ARGS \
    "$SCRIPT_DIR/cases"
