Files
cvmmap-streamer/scripts/acceptance_standalone.sh
T

400 lines
9.9 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
STREAMER_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
BUILD_DIR="${STREAMER_ROOT}/build"
BUILD_BIN_DIR="${BUILD_DIR}/bin"
EVIDENCE_ROOT="${STREAMER_ROOT}/.sisyphus/evidence"
TASK_EVIDENCE_DIR="${EVIDENCE_ROOT}/task-14-acceptance"
SUMMARY_HELPER="${SCRIPT_DIR}/acceptance_summary_helper.py"
RUN_ID=""
RUN_DIR=""
MANIFEST_TSV=""
SUMMARY_JSON=""
LATEST_SUMMARY_JSON="${EVIDENCE_ROOT}/task-14-acceptance-summary.json"
EVIDENCE_TEXT="${EVIDENCE_ROOT}/task-14-acceptance.txt"
STARTED_AT_UTC="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
mkdir -p "${TASK_EVIDENCE_DIR}"
allocate_run_dir() {
local attempts=0
while (( attempts < 50 )); do
local candidate_id
candidate_id="$(date +"%Y%m%dT%H%M%S")-$(date +"%N")-p$$-$RANDOM"
local candidate_dir="${TASK_EVIDENCE_DIR}/${candidate_id}"
if mkdir "${candidate_dir}" 2>/dev/null; then
RUN_ID="${candidate_id}"
RUN_DIR="${candidate_dir}"
MANIFEST_TSV="${RUN_DIR}/rows.tsv"
SUMMARY_JSON="${RUN_DIR}/summary.json"
return 0
fi
attempts=$((attempts + 1))
sleep 0.01
done
echo "failed to allocate unique acceptance run directory" >&2
return 1
}
allocate_run_dir || exit 1
RUN_HASH="$(printf '%s' "${RUN_ID}" | cksum | awk '{print $1}')"
PORT_OFFSET="$((RUN_HASH % 1000))"
RTP_PORT_BASE="$((51040 + PORT_OFFSET))"
RTMP_PORT_BASE="$((19360 + PORT_OFFSET))"
echo -e "order\trow_id\tname\tprotocol\tcodec\ttransport\tstatus\treason\tduration_ms\temitter_rc\treceiver_rc\temitter_log\treceiver_log\tsdp_path" > "${MANIFEST_TSV}"
cleanup_pids=()
cleanup_all() {
for pid in "${cleanup_pids[@]:-}"; do
if [[ -n "${pid}" ]] && kill -0 "${pid}" 2>/dev/null; then
kill "${pid}" 2>/dev/null || true
wait "${pid}" 2>/dev/null || true
fi
done
}
trap cleanup_all EXIT
binary_exists() {
local path="$1"
[[ -x "${path}" ]]
}
binary_candidates() {
local name="$1"
printf '%s\n' "${BUILD_BIN_DIR}/${name}" "${BUILD_DIR}/${name}"
}
resolve_binary() {
local name="$1"
local candidate=""
while IFS= read -r candidate; do
if binary_exists "${candidate}"; then
printf '%s\n' "${candidate}"
return 0
fi
done < <(binary_candidates "${name}")
return 1
}
wait_pid() {
local pid="$1"
local timeout_s="$2"
local elapsed=0
while kill -0 "${pid}" 2>/dev/null; do
if (( elapsed >= timeout_s )); then
kill "${pid}" 2>/dev/null || true
wait "${pid}" 2>/dev/null || true
return 124
fi
sleep 1
elapsed=$((elapsed + 1))
done
wait "${pid}" 2>/dev/null
return $?
}
append_manifest_row() {
local order="$1"
local row_id="$2"
local name="$3"
local protocol="$4"
local codec="$5"
local transport="$6"
local status="$7"
local reason="$8"
local duration_ms="$9"
local emitter_rc="${10}"
local receiver_rc="${11}"
local emitter_log="${12}"
local receiver_log="${13}"
local sdp_path="${14}"
echo -e "${order}\t${row_id}\t${name}\t${protocol}\t${codec}\t${transport}\t${status}\t${reason}\t${duration_ms}\t${emitter_rc}\t${receiver_rc}\t${emitter_log}\t${receiver_log}\t${sdp_path}" >> "${MANIFEST_TSV}"
}
run_rtp_row() {
local order="$1"
local row_id="$2"
local codec="$3"
local row_dir="${RUN_DIR}/${order}-${row_id}"
mkdir -p "${row_dir}"
local emitter_log="${row_dir}/rtp_output.log"
local receiver_log="${row_dir}/rtp_receiver.log"
local sdp_path="${row_dir}/stream.sdp"
local port
port="$((RTP_PORT_BASE + (order - 1) * 2))"
local payload_type=96
if [[ "${codec}" == "h265" ]]; then
payload_type=98
fi
local row_start_ms
row_start_ms="$(date +%s%3N)"
"${RTP_RECEIVER_TESTER}" \
--port "${port}" \
--expect-pt "${payload_type}" \
--packet-threshold 1 \
--timeout-ms 12000 >"${receiver_log}" 2>&1 &
local receiver_pid=$!
cleanup_pids+=("${receiver_pid}")
sleep 1
set +e
"${RTP_OUTPUT_TESTER}" \
--host 127.0.0.1 \
--port "${port}" \
--payload-type "${payload_type}" \
--codec "${codec}" \
--encoder-device software \
--sdp-path "${sdp_path}" \
--frames 48 \
--width 320 \
--height 240 \
--frame-interval-ms 20 >"${emitter_log}" 2>&1
local emitter_rc=$?
set -e
set +e
wait_pid "${receiver_pid}" 20
local receiver_rc=$?
set -e
local row_end_ms
row_end_ms="$(date +%s%3N)"
local duration_ms=$((row_end_ms - row_start_ms))
local status="PASS"
local reason="all-processes-ok"
if (( emitter_rc != 0 || receiver_rc != 0 )); then
status="FAIL"
reason="emitter_rc=${emitter_rc},receiver_rc=${receiver_rc}"
if (( receiver_rc == 0 )) && grep -Eq "Broken pipe|Connection reset by peer" "${emitter_log}"; then
status="PASS"
reason="receiver exited cleanly after threshold; emitter observed peer close"
fi
fi
append_manifest_row \
"${order}" \
"${row_id}" \
"RTP + ${codec}" \
"rtp" \
"${codec}" \
"udp" \
"${status}" \
"${reason}" \
"${duration_ms}" \
"${emitter_rc}" \
"${receiver_rc}" \
"${emitter_log}" \
"${receiver_log}" \
"${sdp_path}"
printf "[%s] RTP + %s => %s (%s)\n" "${row_id}" "${codec}" "${status}" "${reason}"
}
run_rtmp_row() {
local order="$1"
local row_id="$2"
local codec="$3"
local transport="$4"
local row_dir="${RUN_DIR}/${order}-${row_id}"
mkdir -p "${row_dir}"
local emitter_log="${row_dir}/rtmp_output.log"
local receiver_log="${row_dir}/rtmp_stub.log"
local port
port="$((RTMP_PORT_BASE + (order - 3) * 2))"
local mode="h264"
if [[ "${codec}" == "h265" ]]; then
mode="h265-enhanced"
fi
local row_start_ms
row_start_ms="$(date +%s%3N)"
"${RTMP_STUB_TESTER}" \
--mode "${mode}" \
--listen-host 127.0.0.1 \
--listen-port "${port}" \
--video-threshold 4 \
--timeout-ms 12000 >"${receiver_log}" 2>&1 &
local receiver_pid=$!
cleanup_pids+=("${receiver_pid}")
sleep 1
set +e
"${RTMP_OUTPUT_TESTER}" \
--rtmp-url "rtmp://127.0.0.1:${port}/live/${row_id}" \
--transport "${transport}" \
--codec "${codec}" \
--encoder-device software \
--frames 32 \
--width 320 \
--height 240 \
--frame-interval-ms 20 \
--linger-ms 200 >"${emitter_log}" 2>&1
local emitter_rc=$?
set -e
set +e
wait_pid "${receiver_pid}" 20
local receiver_rc=$?
set -e
local row_end_ms
row_end_ms="$(date +%s%3N)"
local duration_ms=$((row_end_ms - row_start_ms))
local status="PASS"
local reason="all-processes-ok"
if (( emitter_rc != 0 || receiver_rc != 0 )); then
status="FAIL"
reason="emitter_rc=${emitter_rc},receiver_rc=${receiver_rc}"
if (( receiver_rc == 0 )) && grep -Eq "Broken pipe|Connection reset by peer" "${emitter_log}"; then
status="PASS"
reason="receiver exited cleanly after threshold; emitter observed peer close"
fi
fi
append_manifest_row \
"${order}" \
"${row_id}" \
"RTMP + ${codec} + ${transport}" \
"rtmp" \
"${codec}" \
"${transport}" \
"${status}" \
"${reason}" \
"${duration_ms}" \
"${emitter_rc}" \
"${receiver_rc}" \
"${emitter_log}" \
"${receiver_log}" \
""
printf "[%s] RTMP + %s + %s => %s (%s)\n" "${row_id}" "${codec}" "${transport}" "${status}" "${reason}"
}
main() {
local required=(
"rtp_output_tester"
"rtp_receiver_tester"
"rtmp_output_tester"
"rtmp_stub_tester"
)
local missing=()
for bin in "${required[@]}"; do
if ! resolve_binary "${bin}" >/dev/null; then
missing+=("${BUILD_BIN_DIR}/${bin} or ${BUILD_DIR}/${bin}")
fi
done
if (( ${#missing[@]} > 0 )); then
{
echo "task=14"
echo "run_id=${RUN_ID}"
echo "run_dir=${RUN_DIR}"
echo "manifest=${MANIFEST_TSV}"
echo "missing_binaries=${missing[*]}"
} > "${EVIDENCE_TEXT}"
echo "missing binaries: ${missing[*]}" >&2
return 1
fi
RTP_OUTPUT_TESTER="$(resolve_binary rtp_output_tester)"
RTP_RECEIVER_TESTER="$(resolve_binary rtp_receiver_tester)"
RTMP_OUTPUT_TESTER="$(resolve_binary rtmp_output_tester)"
RTMP_STUB_TESTER="$(resolve_binary rtmp_stub_tester)"
run_rtp_row 1 "rtp_h264" "h264"
run_rtp_row 2 "rtp_h265" "h265"
run_rtmp_row 3 "rtmp_h264_libavformat" "h264" "libavformat"
run_rtmp_row 4 "rtmp_h265_libavformat" "h265" "libavformat"
run_rtmp_row 5 "rtmp_h264_ffmpeg_process" "h264" "ffmpeg_process"
run_rtmp_row 6 "rtmp_h265_ffmpeg_process" "h265" "ffmpeg_process"
local finished_at_utc
finished_at_utc="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
python3 "${SUMMARY_HELPER}" \
--manifest "${MANIFEST_TSV}" \
--output "${SUMMARY_JSON}" \
--run-id "${RUN_ID}" \
--run-dir "${RUN_DIR}" \
--started-at "${STARTED_AT_UTC}" \
--finished-at "${finished_at_utc}"
cp -f "${SUMMARY_JSON}" "${LATEST_SUMMARY_JSON}" 2>/dev/null || true
local total_count pass_count fail_count all_pass
total_count="$(python3 - <<'PY' "${SUMMARY_JSON}"
import json, sys
data = json.load(open(sys.argv[1], "r", encoding="utf-8"))
counts = data.get("counts", {})
print(counts.get("total", 0))
PY
)"
pass_count="$(python3 - <<'PY' "${SUMMARY_JSON}"
import json, sys
data = json.load(open(sys.argv[1], "r", encoding="utf-8"))
counts = data.get("counts", {})
print(counts.get("pass", 0))
PY
)"
fail_count="$(python3 - <<'PY' "${SUMMARY_JSON}"
import json, sys
data = json.load(open(sys.argv[1], "r", encoding="utf-8"))
counts = data.get("counts", {})
print(counts.get("fail", 0))
PY
)"
all_pass="$(python3 - <<'PY' "${SUMMARY_JSON}"
import json, sys
data = json.load(open(sys.argv[1], "r", encoding="utf-8"))
print("true" if data.get("all_pass", False) else "false")
PY
)"
{
echo "task=14"
echo "run_id=${RUN_ID}"
echo "run_dir=${RUN_DIR}"
echo "manifest=${MANIFEST_TSV}"
echo "summary_json=${SUMMARY_JSON}"
echo "started_at=${STARTED_AT_UTC}"
echo "finished_at=${finished_at_utc}"
echo "counts_total=${total_count}"
echo "counts_pass=${pass_count}"
echo "counts_fail=${fail_count}"
echo "all_pass=${all_pass}"
echo "matrix_rows=rtp_h264,rtp_h265,rtmp_h264_libavformat,rtmp_h265_libavformat,rtmp_h264_ffmpeg_process,rtmp_h265_ffmpeg_process"
} > "${EVIDENCE_TEXT}"
if [[ "${all_pass}" == "true" ]]; then
echo "acceptance matrix PASS (${pass_count}/${total_count})"
echo "summary: ${SUMMARY_JSON}"
return 0
fi
echo "acceptance matrix FAIL (${pass_count}/${total_count})" >&2
echo "summary: ${SUMMARY_JSON}" >&2
return 1
}
main "$@"