mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-11-10 03:44:47 +00:00
db76f62798
A complete passing PR run spent 318 and 567 seconds inside the two smoke inventories. Filename parity places the long deadline controls together. Partition every discovered suite by measured duration while preserving sorted per-shard execution, real deadlines, failure collection and the existing checks. New suites use a default weight; stale hints cannot add or omit coverage, and incomplete selection fails before execution. Contract-Neutral: CI scheduling of unchanged complete script suites only; product, installer, release and verification acceptance contracts are unchanged. Change-source: pulse-maintainer
180 lines
4.8 KiB
Bash
Executable File
180 lines
4.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Simple harness to execute smoke tests under scripts/tests/.
|
|
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
TEST_DIR="${ROOT_DIR}/scripts/tests"
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/tests/run.sh [test-script ...]
|
|
scripts/tests/run.sh --smoke-shard <0|1>
|
|
scripts/tests/run.sh --docs-shard <0|1|2|3>
|
|
|
|
Run all scripts/tests/test-*.sh and scripts/tests/test_*.py tests or a subset
|
|
when specified. Documentation CI discovers every documentation suite and runs
|
|
one of four disjoint, sorted shards; it does not maintain a second suite list.
|
|
Smoke CI balances the complete inventory across two isolated, sorted shards
|
|
using measured suite durations. New suites receive a default scheduling weight.
|
|
EOF
|
|
}
|
|
|
|
discover_tests() {
|
|
local -n ref=$1
|
|
local mode="${2:-all}"
|
|
local inventory
|
|
local -a patterns=(-name 'test-*.sh' -o -name 'test_*.py')
|
|
if [[ "${mode}" == docs ]]; then
|
|
# Keep the established safety helpers whose names predate the docs suffix.
|
|
patterns=(-name 'test_*docs*.py' -o -name 'test_vm_disk_diagnostics.py'
|
|
-o -name 'test_troubleshooting_logs.py'
|
|
-o -name 'test-retired-trial-acquisition-docs.sh')
|
|
fi
|
|
# Process substitution hides producer failures from mapfile. Admit the
|
|
# complete discovery pipeline before running any tests, never a partial list.
|
|
if ! inventory="$(
|
|
find "${TEST_DIR}" -maxdepth 1 -type f \
|
|
\( "${patterns[@]}" \) | LC_ALL=C sort
|
|
)"; then
|
|
echo "Failed to discover the complete smoke test inventory under ${TEST_DIR}" >&2
|
|
return 1
|
|
fi
|
|
ref=()
|
|
if [[ -n "${inventory}" ]]; then
|
|
mapfile -t ref <<< "${inventory}"
|
|
fi
|
|
}
|
|
|
|
resolve_test_path() {
|
|
local input="$1"
|
|
if [[ "${input}" == /* ]]; then
|
|
printf '%s\n' "${input}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ -f "${TEST_DIR}/${input}" ]]; then
|
|
printf '%s\n' "${TEST_DIR}/${input}"
|
|
return 0
|
|
fi
|
|
|
|
if [[ -f "${input}" ]]; then
|
|
printf '%s\n' "${input}"
|
|
return 0
|
|
fi
|
|
|
|
return 1
|
|
}
|
|
|
|
run_tests() {
|
|
local -a tests=("$@")
|
|
local total=0
|
|
local passed=0
|
|
local failed=0
|
|
|
|
for test in "${tests[@]}"; do
|
|
((total += 1))
|
|
local display="${test#${ROOT_DIR}/}"
|
|
printf '==> %s\n' "${display}"
|
|
if [[ "${test}" == *.py ]]; then
|
|
if (cd "${ROOT_DIR}" && python3 "${test}"); then
|
|
echo "PASS"
|
|
((passed += 1))
|
|
else
|
|
echo "FAIL"
|
|
((failed += 1))
|
|
fi
|
|
elif (cd "${ROOT_DIR}" && "${test}"); then
|
|
echo "PASS"
|
|
((passed += 1))
|
|
else
|
|
echo "FAIL"
|
|
((failed += 1))
|
|
fi
|
|
echo
|
|
done
|
|
|
|
echo "Summary: ${passed}/${total} passed"
|
|
if (( failed > 0 )); then
|
|
echo "Failures: ${failed}"
|
|
return 1
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
main() {
|
|
if [[ $# -gt 0 ]]; then
|
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
fi
|
|
|
|
local -a tests=()
|
|
if [[ "${1:-}" == --docs-shard ]]; then
|
|
if [[ $# -ne 2 || ! "${2:-}" =~ ^[0-3]$ ]]; then
|
|
echo "Usage: scripts/tests/run.sh --docs-shard <0|1|2|3>" >&2
|
|
return 1
|
|
fi
|
|
local -a doc_tests=()
|
|
if ! discover_tests doc_tests docs; then
|
|
return 1
|
|
fi
|
|
local index
|
|
for ((index = $2; index < ${#doc_tests[@]}; index += 4)); do
|
|
tests+=("${doc_tests[index]}")
|
|
done
|
|
printf 'Documentation shard %s/4: %s of %s suites\n' "$2" "${#tests[@]}" "${#doc_tests[@]}"
|
|
elif [[ "${1:-}" == --smoke-shard ]]; then
|
|
if [[ $# -ne 2 || ! "${2:-}" =~ ^[0-1]$ ]]; then
|
|
echo "Usage: scripts/tests/run.sh --smoke-shard <0|1>" >&2
|
|
return 1
|
|
fi
|
|
local -a smoke_tests=()
|
|
if ! discover_tests smoke_tests; then
|
|
return 1
|
|
fi
|
|
local selection
|
|
if (( ${#smoke_tests[@]} > 0 )); then
|
|
# Capture the entire successful selector before admitting any suite. A
|
|
# partial output from a failed selector is not a complete shard. Timing
|
|
# hints only place discovered suites; they cannot add or omit coverage.
|
|
if ! selection="$(
|
|
printf '%s\n' "${smoke_tests[@]}" | python3 \
|
|
"${ROOT_DIR}/scripts/select-smoke-test-shard.py" \
|
|
"${ROOT_DIR}/.github/scripts/smoke-test-seconds.txt" 2 "$2"
|
|
)"; then
|
|
echo "Failed to partition the complete smoke test inventory" >&2
|
|
return 1
|
|
fi
|
|
if [[ -n "${selection}" ]]; then
|
|
mapfile -t tests <<< "${selection}"
|
|
fi
|
|
fi
|
|
printf 'Smoke shard %s/2: %s of %s suites\n' "$2" "${#tests[@]}" "${#smoke_tests[@]}"
|
|
elif [[ $# -gt 0 ]]; then
|
|
local arg resolved
|
|
for arg in "$@"; do
|
|
if ! resolved="$(resolve_test_path "${arg}")"; then
|
|
echo "Unknown test: ${arg}" >&2
|
|
exit 1
|
|
fi
|
|
tests+=("${resolved}")
|
|
done
|
|
else
|
|
if ! discover_tests tests; then
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
if [[ ${#tests[@]} -eq 0 ]]; then
|
|
echo "No tests found under ${TEST_DIR}" >&2
|
|
exit 1
|
|
fi
|
|
|
|
run_tests "${tests[@]}"
|
|
}
|
|
|
|
main "$@"
|