diff --git a/Runner/init_env b/Runner/init_env index a76d5882..2dc8b4c0 100755 --- a/Runner/init_env +++ b/Runner/init_env @@ -29,8 +29,9 @@ if [ -z "$ROOT_DIR" ]; then fi fi -if [ ! -d "$ROOT_DIR/utils" ] || [ ! -f "$ROOT_DIR/utils/functestlib.sh" ]; then -echo "[ERROR] Could not detect testkit root (missing utils/ or functestlib.sh)" >&2 +# --- Validate and export key environment paths --- +if [ -z "${ROOT_DIR:-}" ] || [ ! -d "$ROOT_DIR/utils" ] || [ ! -f "$ROOT_DIR/utils/functestlib.sh" ]; then + echo "[ERROR] Could not detect testkit root (missing utils/ or functestlib.sh)" >&2 exit 1 fi @@ -38,3 +39,15 @@ export ROOT_DIR export TOOLS="$ROOT_DIR/utils" export __RUNNER_SUITES_DIR="$ROOT_DIR/suites" export __RUNNER_UTILS_BIN_DIR="$ROOT_DIR/common" + +# --- Ensure TOOLS is usable in all shells --- +case ":$PATH:" in + *":$TOOLS:"*) : ;; + *) PATH="$TOOLS:$PATH"; export PATH ;; +esac + +# --- Optional: pre-check for required tools (safe no-op for minimal builds) --- +if [ -f "$TOOLS/functestlib.sh" ]; then + # shellcheck disable=SC1090,SC1091 + . "$TOOLS/functestlib.sh" >/dev/null 2>&1 || true +fi diff --git a/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh b/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh index 32b15d86..b3865586 100755 --- a/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh +++ b/Runner/suites/Multimedia/Audio/AudioPlayback/run.sh @@ -5,17 +5,38 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # ---- Source init_env & tools ---- +SCRIPT_DIR="$( + cd "$(dirname "$0")" || exit 1 + pwd +)" INIT_ENV="" SEARCH="$SCRIPT_DIR" + while [ "$SEARCH" != "/" ]; do - if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env"; break; fi - SEARCH=$(dirname "$SEARCH") + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi + SEARCH=$(dirname "$SEARCH") done -[ -z "$INIT_ENV" ] && echo "[ERROR] init_env not found" >&2 && exit 1 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] Could not find init_env (starting at $SCRIPT_DIR)" >&2 + exit 1 +fi + +# Only source once (idempotent) +if [ -z "${__INIT_ENV_LOADED:-}" ]; then + # shellcheck disable=SC1090 + . "$INIT_ENV" + __INIT_ENV_LOADED=1 +fi + # shellcheck disable=SC1090 -[ -z "$__INIT_ENV_LOADED" ] && . "$INIT_ENV" -# shellcheck disable=SC1090,SC1091 +. "$INIT_ENV" +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" +# shellcheck disable=SC1091 . "$TOOLS/audio_common.sh" TESTNAME="AudioPlayback" @@ -38,7 +59,17 @@ STRICT="${STRICT:-0}" DMESG_SCAN="${DMESG_SCAN:-1}" VERBOSE=0 EXTRACT_AUDIO_ASSETS="${EXTRACT_AUDIO_ASSETS:-true}" -JUNIT_OUT="" + +# Network bring-up knobs (match video behavior) +if [ -z "${NET_STABILIZE_SLEEP:-}" ]; then + NET_STABILIZE_SLEEP="5" +fi +if [ -z "${TOP_LEVEL_RUN:-}" ]; then + TOP_LEVEL_RUN="1" +fi + +SSID="" +PASSWORD="" usage() { cat </dev/null || echo "$SCRIPT_DIR")" -cd "$test_path" || { log_error "cd failed: $test_path"; echo "$TESTNAME FAIL" >"$RES_FILE"; exit 1; } +if ! cd "$test_path"; then + log_error "cd failed: $test_path" + echo "$TESTNAME FAIL" >"$RES_FILE" + exit 1 +fi log_info "---------------- Starting $TESTNAME ----------------" -log_info "SoC: $(soc_id)" +# --- Platform details (robust logging; prefer helpers) --- +if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" +else + log_info "Platform Details: unknown" +fi + log_info "Args: backend=${AUDIO_BACKEND:-auto} sink=$SINK_CHOICE loops=$LOOPS timeout=$TIMEOUT formats='$FORMATS' durations='$DURATIONS' strict=$STRICT dmesg=$DMESG_SCAN extract=$EXTRACT_AUDIO_ASSETS" +# --- Rootfs minimum size check (mirror video policy) --- +if [ "$TOP_LEVEL_RUN" -eq 1 ]; then + ensure_rootfs_min_size 2 +else + log_info "Sub-run: skipping rootfs size check (already performed)." +fi + +# --- Network preflight identical to video gating --- +if [ "$TOP_LEVEL_RUN" -eq 1 ]; then + if [ "${EXTRACT_AUDIO_ASSETS}" = "true" ]; then + NET_RC="1" + + if command -v check_network_status_rc >/dev/null 2>&1; then + check_network_status_rc + NET_RC="$?" + elif command -v check_network_status >/dev/null 2>&1; then + check_network_status >/dev/null 2>&1 + NET_RC="$?" + fi + + if [ "$NET_RC" -ne 0 ]; then + video_step "" "Bring network online (Wi-Fi credentials if provided)" + ensure_network_online || true + sleep "${NET_STABILIZE_SLEEP}" + else + sleep "${NET_STABILIZE_SLEEP}" + fi + fi +else + log_info "Sub-run: skipping initial network bring-up." +fi + # Resolve backend -[ -z "$AUDIO_BACKEND" ] && AUDIO_BACKEND="$(detect_audio_backend)" +if [ -z "$AUDIO_BACKEND" ]; then + AUDIO_BACKEND="$(detect_audio_backend)" +fi if [ -z "$AUDIO_BACKEND" ]; then log_skip "$TESTNAME SKIP - no audio backend running" - echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0 + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 fi log_info "Using backend: $AUDIO_BACKEND" if ! check_audio_daemon "$AUDIO_BACKEND"; then log_skip "$TESTNAME SKIP - backend not available: $AUDIO_BACKEND" - echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0 + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 fi # Dependencies per backend if [ "$AUDIO_BACKEND" = "pipewire" ]; then - check_dependencies wpctl pw-play || { log_skip "$TESTNAME SKIP - missing PipeWire utils"; echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0; } + if ! check_dependencies wpctl pw-play; then + log_skip "$TESTNAME SKIP - missing PipeWire utils" + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi else - check_dependencies pactl paplay || { log_skip "$TESTNAME SKIP - missing PulseAudio utils"; echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0; } + if ! check_dependencies pactl paplay; then + log_skip "$TESTNAME SKIP - missing PulseAudio utils" + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 + fi fi # ----- Route sink (set default; player uses default sink) ----- SINK_ID="" case "$AUDIO_BACKEND:$SINK_CHOICE" in - pipewire:null) SINK_ID="$(pw_default_null)" ;; - pipewire:*) SINK_ID="$(pw_default_speakers)" ;; - pulseaudio:null) SINK_ID="$(pa_default_null)" ;; - pulseaudio:*) SINK_ID="$(pa_default_speakers)" ;; + pipewire:null) + SINK_ID="$(pw_default_null)" + ;; + pipewire:*) + SINK_ID="$(pw_default_speakers)" + ;; + pulseaudio:null) + SINK_ID="$(pa_default_null)" + ;; + pulseaudio:*) + SINK_ID="$(pa_default_speakers)" + ;; esac + if [ -z "$SINK_ID" ]; then log_skip "$TESTNAME SKIP - requested sink '$SINK_CHOICE' not found for $AUDIO_BACKEND" - echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0 + echo "$TESTNAME SKIP" >"$RES_FILE" + exit 0 fi if [ "$AUDIO_BACKEND" = "pipewire" ]; then SINK_NAME="$(pw_sink_name_safe "$SINK_ID")" wpctl set-default "$SINK_ID" >/dev/null 2>&1 || true - [ -n "$SINK_NAME" ] || SINK_NAME="unknown" + if [ -z "$SINK_NAME" ]; then + SINK_NAME="unknown" + fi log_info "Routing to sink: id=$SINK_ID name='$SINK_NAME' choice=$SINK_CHOICE" else SINK_NAME="$(pa_sink_name "$SINK_ID")" - [ -n "$SINK_NAME" ] || SINK_NAME="$SINK_ID" + if [ -z "$SINK_NAME" ]; then + SINK_NAME="$SINK_ID" + fi pa_set_default_sink "$SINK_ID" >/dev/null 2>&1 || true log_info "Routing to sink: name='$SINK_NAME' choice=$SINK_CHOICE" fi -# JUnit init -if [ -n "$JUNIT_OUT" ]; then JUNIT_TMP="$LOGDIR/.junit_cases.xml"; : > "$JUNIT_TMP"; fi -append_junit() { - name="$1"; elapsed="$2"; status="$3"; logf="$4" - [ -n "$JUNIT_OUT" ] || return 0 - safe_msg="$(tail -n 50 "$logf" 2>/dev/null | sed 's/&/\&/g;s//\>/g')" - { - printf ' \n' "Audio.Playback" "$name" "$elapsed" - case "$status" in - PASS) : ;; - SKIP) printf ' \n' ;; - FAIL) printf ' \n' "failed"; printf '%s\n' "$safe_msg"; printf ' \n' ;; - esac - printf ' \n' - } >> "$JUNIT_TMP" -} - # Decide minimum ok seconds if timeout>0 dur_s="$(duration_to_secs "$TIMEOUT" 2>/dev/null || echo 0)" -[ -z "$dur_s" ] && dur_s=0 +if [ -z "$dur_s" ]; then + dur_s=0 +fi + min_ok=0 if [ "$dur_s" -gt 0 ] 2>/dev/null; then - min_ok=$((dur_s - 1)); [ "$min_ok" -lt 1 ] && min_ok=1 + min_ok=$((dur_s - 1)) + if [ "$min_ok" -lt 1 ]; then + min_ok=1 + fi log_info "Watchdog/timeout: ${TIMEOUT}" else log_info "Watchdog/timeout: disabled (no timeout)" fi # ------------- Matrix execution ------------- -total=0; pass=0; fail=0; skip=0; suite_rc=0 +total=0 +pass=0 +fail=0 +skip=0 +suite_rc=0 for fmt in $FORMATS; do for dur in $DURATIONS; do clip="$(resolve_clip "$fmt" "$dur")" case_name="play_${fmt}_${dur}" total=$((total + 1)) - logf="$LOGDIR/${case_name}.log"; : > "$logf" + logf="$LOGDIR/${case_name}.log" + : > "$logf" export AUDIO_LOGCTX="$logf" if [ -z "$clip" ]; then log_warn "[$case_name] No clip mapping for format=$fmt duration=$dur" echo "$case_name SKIP (no clip mapping)" >> "$LOGDIR/summary.txt" - append_junit "$case_name" "0" "SKIP" "$logf"; skip=$((skip + 1)); continue + skip=$((skip + 1)) + continue fi - if [ "${EXTRACT_AUDIO_ASSETS:-true}" = "true" ]; then - audio_ensure_clip_ready "$clip" "$AUDIO_TAR_URL" - rc=$? - if [ "$rc" -eq 2 ] || [ "$rc" -eq 1 ]; then - log_skip "[$case_name] SKIP: Required clip missing and network unavailable or fetch failed." - echo "$case_name SKIP (clip missing)" >> "$LOGDIR/summary.txt" - append_junit "$case_name" "0" "SKIP" "$logf" - skip=$((skip + 1)) - continue - fi + if [ "${EXTRACT_AUDIO_ASSETS}" = "true" ]; then + if [ -f "$clip" ] && [ -s "$clip" ]; then + CLIP_BYTES="$(wc -c < "$clip" 2>/dev/null || echo 0)" + log_info "[$case_name] Clip already present: $clip (${CLIP_BYTES} bytes) — skipping fetch/extract." + else + log_info "[$case_name] Preparing assets for clip: $clip (not found locally)" + log_info "[$case_name] Attempting fetch/extract from: $AUDIO_TAR_URL" + + audio_ensure_clip_ready "$clip" "$AUDIO_TAR_URL" + rc=$? + + if [ "$rc" -eq 0 ] && [ -f "$clip" ]; then + CLIP_BYTES="$(wc -c < "$clip" 2>/dev/null || echo 0)" + log_info "[$case_name] Clip ready: $clip (${CLIP_BYTES} bytes)" + fi + + if [ "$rc" -eq 2 ] || [ "$rc" -eq 1 ]; then + log_skip "[$case_name] SKIP: Required clip missing and network unavailable or fetch failed." + echo "$case_name SKIP (clip missing)" >> "$LOGDIR/summary.txt" + skip=$((skip + 1)) + continue + fi + fi fi - i=1; ok_runs=0; last_elapsed=0 + i=1 + ok_runs=0 + last_elapsed=0 + while [ "$i" -le "$LOOPS" ]; do iso="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + if [ "$AUDIO_BACKEND" = "pipewire" ]; then loop_hdr="sink=$SINK_CHOICE($SINK_ID)" else loop_hdr="sink=$SINK_CHOICE($SINK_NAME)" fi + log_info "[$case_name] loop $i/$LOOPS start=$iso clip=$clip backend=$AUDIO_BACKEND $loop_hdr" + start_s="$(date +%s 2>/dev/null || echo 0)" if [ "$AUDIO_BACKEND" = "pipewire" ]; then @@ -208,29 +368,43 @@ for fmt in $FORMATS; do fi end_s="$(date +%s 2>/dev/null || echo 0)" - last_elapsed=$((end_s - start_s)); [ "$last_elapsed" -lt 0 ] && last_elapsed=0 + last_elapsed=$((end_s - start_s)) + if [ "$last_elapsed" -lt 0 ]; then + last_elapsed=0 + fi # Evidence - pw_ev=$(audio_evidence_pw_streaming || echo 0) - pa_ev=$(audio_evidence_pa_streaming || echo 0) - # ---- minimal PulseAudio fallback so pa_streaming doesn't read as 0 after teardown ---- + pw_ev="$(audio_evidence_pw_streaming || echo 0)" + pa_ev="$(audio_evidence_pa_streaming || echo 0)" + + # Minimal PulseAudio fallback so pa_streaming doesn't read as 0 after teardown if [ "$AUDIO_BACKEND" = "pulseaudio" ] && [ "$pa_ev" -eq 0 ]; then if [ "$rc" -eq 0 ] || { [ "$rc" -eq 124 ] && [ "$dur_s" -gt 0 ] 2>/dev/null && [ "$last_elapsed" -ge "$min_ok" ]; }; then pa_ev=1 fi fi - alsa_ev=$(audio_evidence_alsa_running_any || echo 0) - asoc_ev=$(audio_evidence_asoc_path_on || echo 0) - pwlog_ev=$(audio_evidence_pw_log_seen || echo 0) - [ "$AUDIO_BACKEND" = "pulseaudio" ] && pwlog_ev=0 + + alsa_ev="$(audio_evidence_alsa_running_any || echo 0)" + asoc_ev="$(audio_evidence_asoc_path_on || echo 0)" + pwlog_ev="$(audio_evidence_pw_log_seen || echo 0)" + if [ "$AUDIO_BACKEND" = "pulseaudio" ]; then + pwlog_ev=0 + fi + # Fast teardown fallback: if user-space stream was active, trust ALSA/ASoC too. if [ "$alsa_ev" -eq 0 ]; then - if [ "$AUDIO_BACKEND" = "pipewire" ] && [ "$pw_ev" -eq 1 ]; then alsa_ev=1; fi - if [ "$AUDIO_BACKEND" = "pulseaudio" ] && [ "$pa_ev" -eq 1 ]; then alsa_ev=1; fi + if [ "$AUDIO_BACKEND" = "pipewire" ] && [ "$pw_ev" -eq 1 ]; then + alsa_ev=1 + fi + if [ "$AUDIO_BACKEND" = "pulseaudio" ] && [ "$pa_ev" -eq 1 ]; then + alsa_ev=1 + fi fi - if [ "$asoc_ev" -eq 0 ] && [ "$alsa_ev" -eq 1 ]; then + + if [ "$asoc_ev" -eq 0 ] && [ "$alsa_ev" -eq 1 ]; then asoc_ev=1 fi + log_info "[$case_name] evidence: pw_streaming=$pw_ev pa_streaming=$pa_ev alsa_running=$alsa_ev asoc_path_on=$asoc_ev pw_log=$pwlog_ev" if [ "$rc" -eq 0 ]; then @@ -254,35 +428,32 @@ for fmt in $FORMATS; do dump_mixers "$LOGDIR/mixer_dump.txt" fi - status="FAIL"; [ "$ok_runs" -ge 1 ] && status="PASS" - append_junit "$case_name" "$last_elapsed" "$status" "$logf" - case "$status" in - PASS) pass=$((pass + 1)); echo "$case_name PASS" >> "$LOGDIR/summary.txt" ;; - SKIP) skip=$((skip + 1)); echo "$case_name SKIP" >> "$LOGDIR/summary.txt" ;; - FAIL) fail=$((fail + 1)); echo "$case_name FAIL" >> "$LOGDIR/summary.txt"; suite_rc=1 ;; - esac + if [ "$ok_runs" -ge 1 ]; then + pass=$((pass + 1)) + echo "$case_name PASS" >> "$LOGDIR/summary.txt" + else + fail=$((fail + 1)) + echo "$case_name FAIL" >> "$LOGDIR/summary.txt" + suite_rc=1 + fi done done log_info "Summary: total=$total pass=$pass fail=$fail skip=$skip" -if [ -n "$JUNIT_OUT" ]; then - tests=$((pass + fail + skip)) - failures="$fail" - skipped="$skip" - { - printf '\n' "$TESTNAME" "$tests" "$failures" "$skipped" - cat "$JUNIT_TMP" - printf '\n' - } > "$JUNIT_OUT" - log_info "Wrote JUnit: $JUNIT_OUT" +# --- Proper exit codes: PASS=0, FAIL=1, SKIP-only=0 --- +if [ "$pass" -eq 0 ] && [ "$fail" -eq 0 ] && [ "$skip" -gt 0 ]; then + log_skip "$TESTNAME SKIP" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 0 fi -if [ "$pass" -eq 0 ] && [ "$fail" -eq 0 ] && [ "$skip" -gt 0 ]; then - log_skip "$TESTNAME SKIP"; echo "$TESTNAME SKIP" > "$RES_FILE" -elif [ "$suite_rc" -eq 0 ]; then - log_pass "$TESTNAME PASS"; echo "$TESTNAME PASS" > "$RES_FILE" -else - log_fail "$TESTNAME FAIL"; echo "$TESTNAME FAIL" > "$RES_FILE" +if [ "$suite_rc" -eq 0 ]; then + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 fi -exit "$suite_rc" + +log_fail "$TESTNAME FAIL" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1 diff --git a/Runner/suites/Multimedia/Audio/AudioRecord/run.sh b/Runner/suites/Multimedia/Audio/AudioRecord/run.sh index c8ff0d82..a4ef5181 100755 --- a/Runner/suites/Multimedia/Audio/AudioRecord/run.sh +++ b/Runner/suites/Multimedia/Audio/AudioRecord/run.sh @@ -8,14 +8,26 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" INIT_ENV="" SEARCH="$SCRIPT_DIR" while [ "$SEARCH" != "/" ]; do - if [ -f "$SEARCH/init_env" ]; then INIT_ENV="$SEARCH/init_env"; break; fi + if [ -f "$SEARCH/init_env" ]; then + INIT_ENV="$SEARCH/init_env" + break + fi SEARCH=$(dirname "$SEARCH") done -[ -z "$INIT_ENV" ] && echo "[ERROR] init_env not found" >&2 && exit 1 + +if [ -z "$INIT_ENV" ]; then + echo "[ERROR] init_env not found" >&2 + exit 1 +fi + # shellcheck disable=SC1090 -[ -z "$__INIT_ENV_LOADED" ] && . "$INIT_ENV" -# shellcheck disable=SC1090,SC1091 +if [ -z "$__INIT_ENV_LOADED" ]; then + . "$INIT_ENV" +fi + +# shellcheck disable=SC1091 . "$TOOLS/functestlib.sh" +# shellcheck disable=SC1091 . "$TOOLS/audio_common.sh" TESTNAME="AudioRecord" @@ -54,66 +66,137 @@ EOF while [ $# -gt 0 ]; do case "$1" in - --backend) AUDIO_BACKEND="$2"; shift 2 ;; - --source) SRC_CHOICE="$2"; shift 2 ;; - --durations) DURATIONS="$2"; shift 2 ;; - --rec-secs) REC_SECS="$2"; shift 2 ;; - --loops) LOOPS="$2"; shift 2 ;; - --timeout) TIMEOUT="$2"; shift 2 ;; - --strict) STRICT=1; shift ;; - --no-dmesg) DMESG_SCAN=0; shift ;; - --junit) JUNIT_OUT="$2"; shift 2 ;; - --verbose) export VERBOSE=1; shift ;; - --help|-h) usage; exit 0 ;; - *) log_warn "Unknown option: $1"; shift ;; + --backend) + AUDIO_BACKEND="$2" + shift 2 + ;; + --source) + SRC_CHOICE="$2" + shift 2 + ;; + --durations) + DURATIONS="$2" + shift 2 + ;; + --rec-secs) + REC_SECS="$2" + shift 2 + ;; + --loops) + LOOPS="$2" + shift 2 + ;; + --timeout) + TIMEOUT="$2" + shift 2 + ;; + --strict) + STRICT=1 + shift + ;; + --no-dmesg) + DMESG_SCAN=0 + shift + ;; + --junit) + JUNIT_OUT="$2" + shift 2 + ;; + --verbose) + export VERBOSE=1 + shift + ;; + --help|-h) + usage + exit 0 + ;; + *) + log_warn "Unknown option: $1" + shift + ;; esac done # Ensure we run from the testcase dir test_path="$(find_test_case_by_name "$TESTNAME" 2>/dev/null || echo "$SCRIPT_DIR")" -cd "$test_path" || { log_error "cd failed: $test_path"; echo "$TESTNAME FAIL" >"$RES_FILE"; exit 1; } +if ! cd "$test_path"; then + log_error "cd failed: $test_path" + echo "$TESTNAME FAIL" > "$RES_FILE" + exit 1 +fi log_info "---------------- Starting $TESTNAME ----------------" -log_info "SoC: $(soc_id)" +# --- Platform details (robust logging; prefer helpers) --- +if command -v detect_platform >/dev/null 2>&1; then + detect_platform >/dev/null 2>&1 || true + log_info "Platform Details: machine='${PLATFORM_MACHINE:-unknown}' target='${PLATFORM_TARGET:-unknown}' kernel='${PLATFORM_KERNEL:-}' arch='${PLATFORM_ARCH:-}'" +else + log_info "Platform Details: unknown" +fi + log_info "Args: backend=${AUDIO_BACKEND:-auto} source=$SRC_CHOICE loops=$LOOPS durations='$DURATIONS' rec_secs=$REC_SECS timeout=$TIMEOUT strict=$STRICT dmesg=$DMESG_SCAN" # Resolve backend -[ -z "$AUDIO_BACKEND" ] && AUDIO_BACKEND="$(detect_audio_backend)" +if [ -z "$AUDIO_BACKEND" ]; then + AUDIO_BACKEND="$(detect_audio_backend)" +fi if [ -z "$AUDIO_BACKEND" ]; then log_skip "$TESTNAME SKIP - no audio backend running" - echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0 + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 fi log_info "Using backend: $AUDIO_BACKEND" if ! check_audio_daemon "$AUDIO_BACKEND"; then log_skip "$TESTNAME SKIP - backend not available: $AUDIO_BACKEND" - echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0 + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 fi # Dependencies per backend if [ "$AUDIO_BACKEND" = "pipewire" ]; then - check_dependencies wpctl pw-record || { log_skip "$TESTNAME SKIP - missing PipeWire utils"; echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0; } + if ! check_dependencies wpctl pw-record; then + log_skip "$TESTNAME SKIP - missing PipeWire utils" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 + fi else - check_dependencies pactl parecord || { log_skip "$TESTNAME SKIP - missing PulseAudio utils"; echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0; } + if ! check_dependencies pactl parecord; then + log_skip "$TESTNAME SKIP - missing PulseAudio utils" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 + fi fi # ----- Route source (set default; recorder uses default source) ----- SRC_ID="" case "$AUDIO_BACKEND:$SRC_CHOICE" in - pipewire:null) SRC_ID="$(pw_default_null_source)" ;; - pipewire:*) SRC_ID="$(pw_default_mic)" ;; - pulseaudio:null) SRC_ID="$(pa_default_null_source)" ;; - pulseaudio:*) SRC_ID="$(pa_default_mic)" ;; + pipewire:null) + SRC_ID="$(pw_default_null_source)" + ;; + pipewire:*) + SRC_ID="$(pw_default_mic)" + ;; + pulseaudio:null) + SRC_ID="$(pa_default_null_source)" + ;; + pulseaudio:*) + SRC_ID="$(pa_default_mic)" + ;; esac + if [ -z "$SRC_ID" ]; then log_skip "$TESTNAME SKIP - requested source '$SRC_CHOICE' not found for $AUDIO_BACKEND" - echo "$TESTNAME SKIP" >"$RES_FILE"; exit 0 + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 fi if [ "$AUDIO_BACKEND" = "pipewire" ]; then SRC_LABEL="$(pw_source_label_safe "$SRC_ID")" wpctl set-default "$SRC_ID" >/dev/null 2>&1 || true - [ -n "$SRC_LABEL" ] || SRC_LABEL="unknown" + if [ -z "$SRC_LABEL" ]; then + SRC_LABEL="unknown" + fi log_info "Routing to source: id/name=$SRC_ID label='$SRC_LABEL' choice=$SRC_CHOICE" else SRC_LABEL="$(pa_source_name "$SRC_ID" 2>/dev/null || echo "$SRC_ID")" @@ -123,7 +206,10 @@ fi # Watchdog info dur_s="$(duration_to_secs "$TIMEOUT" 2>/dev/null || echo 0)" -[ -z "$dur_s" ] && dur_s=0 +if [ -z "$dur_s" ]; then + dur_s=0 +fi + if [ "$dur_s" -gt 0 ] 2>/dev/null; then log_info "Watchdog/timeout: ${TIMEOUT}" else @@ -131,23 +217,46 @@ else fi # JUnit init (optional) -if [ -n "$JUNIT_OUT" ]; then JUNIT_TMP="$LOGDIR/.junit_cases.xml"; : > "$JUNIT_TMP"; fi +if [ -n "$JUNIT_OUT" ]; then + JUNIT_TMP="$LOGDIR/.junit_cases.xml" + : > "$JUNIT_TMP" +fi + append_junit() { - name="$1"; elapsed="$2"; status="$3"; logf="$4" - [ -n "$JUNIT_OUT" ] || return 0 - safe_msg="$(tail -n 50 "$logf" 2>/dev/null | sed 's/&/\&/g;s//\>/g')" + name="$1" + elapsed="$2" + status="$3" + logf="$4" + + if [ -z "$JUNIT_OUT" ]; then + return 0 + fi + + safe_msg="$( + tail -n 50 "$logf" 2>/dev/null \ + | sed 's/&/\&/g;s//\>/g' + )" + { printf ' \n' "Audio.Record" "$name" "$elapsed" case "$status" in - PASS) : ;; - SKIP) printf ' \n' ;; - FAIL) printf ' \n' "failed"; printf '%s\n' "$safe_msg"; printf ' \n' ;; + PASS) + : + ;; + SKIP) + printf ' \n' + ;; + FAIL) + printf ' \n' "failed" + printf '%s\n' "$safe_msg" + printf ' \n' + ;; esac printf ' \n' } >> "$JUNIT_TMP" } -# Auto map if REC_SECS=auto, and accept numeric tokens in DURATIONS like 35s/35sec/35secs/35seconds +# Auto map if REC_SECS=auto, and accept numeric tokens like 35s/35sec/35secs/35seconds auto_secs_for() { case "$1" in short) echo "5s" ;; @@ -158,12 +267,18 @@ auto_secs_for() { } # ---------------- Matrix execution ---------------- -total=0; pass=0; fail=0; skip=0; suite_rc=0 +total=0 +pass=0 +fail=0 +skip=0 +suite_rc=0 for dur in $DURATIONS; do case_name="record_${dur}" total=$((total + 1)) - logf="$LOGDIR/${case_name}.log"; : > "$logf" + + logf="$LOGDIR/${case_name}.log" + : > "$logf" export AUDIO_LOGCTX="$logf" secs="$REC_SECS" @@ -177,24 +292,36 @@ for dur in $DURATIONS; do fi fi - i=1; ok_runs=0; last_elapsed=0 + i=1 + ok_runs=0 + last_elapsed=0 + while [ "$i" -le "$LOOPS" ]; do iso="$(date -u +%Y-%m-%dT%H:%M:%SZ)" effective_timeout="$secs" if [ -n "$TIMEOUT" ] && [ "$TIMEOUT" != "0" ]; then effective_timeout="$TIMEOUT" fi + loop_hdr="source=$SRC_CHOICE" - [ "$AUDIO_BACKEND" = "pipewire" ] && loop_hdr="$loop_hdr($SRC_ID)" || loop_hdr="$loop_hdr($SRC_LABEL)" + if [ "$AUDIO_BACKEND" = "pipewire" ]; then + loop_hdr="$loop_hdr($SRC_ID)" + else + loop_hdr="$loop_hdr($SRC_LABEL)" + fi + log_info "[$case_name] loop $i/$LOOPS start=$iso secs=$secs backend=$AUDIO_BACKEND $loop_hdr" + out="$LOGDIR/${case_name}.wav" : > "$out" start_s="$(date +%s 2>/dev/null || echo 0)" + if [ "$AUDIO_BACKEND" = "pipewire" ]; then log_info "[$case_name] exec: pw-record -v \"$out\"" - audio_exec_with_timeout "$effective_timeout" pw-record -v "$out" >>"$logf" 2>&1 + audio_exec_with_timeout "$effective_timeout" pw-record -v "$out" >> "$logf" 2>&1 rc=$? + bytes="$(stat -c '%s' "$out" 2>/dev/null || wc -c < "$out")" if [ "$rc" -ne 0 ] && [ "${bytes:-0}" -gt 1024 ] 2>/dev/null; then @@ -206,7 +333,7 @@ for dur in $DURATIONS; do log_warn "[$case_name] first attempt rc=$rc bytes=$bytes; retry with --target $SRC_ID" : > "$out" log_info "[$case_name] exec: pw-record -v --target \"$SRC_ID\" \"$out\"" - audio_exec_with_timeout "$effective_timeout" pw-record -v --target "$SRC_ID" "$out" >>"$logf" 2>&1 + audio_exec_with_timeout "$effective_timeout" pw-record -v --target "$SRC_ID" "$out" >> "$logf" 2>&1 rc=$? bytes="$(stat -c '%s' "$out" 2>/dev/null || wc -c < "$out")" if [ "$rc" -ne 0 ] && [ "${bytes:-0}" -gt 1024 ] 2>/dev/null; then @@ -216,7 +343,7 @@ for dur in $DURATIONS; do fi else log_info "[$case_name] exec: parecord --file-format=wav \"$out\"" - audio_exec_with_timeout "$effective_timeout" parecord --file-format=wav "$out" >>"$logf" 2>&1 + audio_exec_with_timeout "$effective_timeout" parecord --file-format=wav "$out" >> "$logf" 2>&1 rc=$? bytes="$(stat -c '%s' "$out" 2>/dev/null || wc -c < "$out")" if [ "$rc" -ne 0 ] && [ "${bytes:-0}" -gt 1024 ] 2>/dev/null; then @@ -224,28 +351,45 @@ for dur in $DURATIONS; do rc=0 fi fi + end_s="$(date +%s 2>/dev/null || echo 0)" - last_elapsed=$((end_s - start_s)); [ "$last_elapsed" -lt 0 ] && last_elapsed=0 + last_elapsed=$((end_s - start_s)) + if [ "$last_elapsed" -lt 0 ]; then + last_elapsed=0 + fi # Evidence pw_ev=$(audio_evidence_pw_streaming || echo 0) pa_ev=$(audio_evidence_pa_streaming || echo 0) + # ---- minimal PulseAudio fallback so pa_streaming doesn't read as 0 after teardown ---- if [ "$AUDIO_BACKEND" = "pulseaudio" ] && [ "$pa_ev" -eq 0 ]; then - if [ "$rc" -eq 0 ] && [ "${bytes:-0}" -gt 1024 ] 2>/dev/null; then pa_ev=1; fi + if [ "$rc" -eq 0 ] && [ "${bytes:-0}" -gt 1024 ] 2>/dev/null; then + pa_ev=1 + fi fi + alsa_ev=$(audio_evidence_alsa_running_any || echo 0) asoc_ev=$(audio_evidence_asoc_path_on || echo 0) pwlog_ev=$(audio_evidence_pw_log_seen || echo 0) - [ "$AUDIO_BACKEND" = "pulseaudio" ] && pwlog_ev=0 + if [ "$AUDIO_BACKEND" = "pulseaudio" ]; then + pwlog_ev=0 + fi + # Fast teardown fallback: if user-space stream was active, trust ALSA/ASoC too. if [ "$alsa_ev" -eq 0 ]; then - if [ "$AUDIO_BACKEND" = "pipewire" ] && [ "$pw_ev" -eq 1 ]; then alsa_ev=1; fi - if [ "$AUDIO_BACKEND" = "pulseaudio" ] && [ "$pa_ev" -eq 1 ]; then alsa_ev=1; fi + if [ "$AUDIO_BACKEND" = "pipewire" ] && [ "$pw_ev" -eq 1 ]; then + alsa_ev=1 + fi + if [ "$AUDIO_BACKEND" = "pulseaudio" ] && [ "$pa_ev" -eq 1 ]; then + alsa_ev=1 + fi fi + if [ "$asoc_ev" -eq 0 ] && [ "$alsa_ev" -eq 1 ]; then - asoc_ev=1 + asoc_ev=1 fi + log_info "[$case_name] evidence: pw_streaming=$pw_ev pa_streaming=$pa_ev alsa_running=$alsa_ev asoc_path_on=$asoc_ev bytes=${bytes:-0} pw_log=$pwlog_ev" # Final PASS/FAIL @@ -264,12 +408,27 @@ for dur in $DURATIONS; do dump_mixers "$LOGDIR/mixer_dump.txt" fi - status="FAIL"; [ "$ok_runs" -ge 1 ] && status="PASS" + status="FAIL" + if [ "$ok_runs" -ge 1 ]; then + status="PASS" + fi + append_junit "$case_name" "$last_elapsed" "$status" "$logf" + case "$status" in - PASS) pass=$((pass + 1)); echo "$case_name PASS" >> "$LOGDIR/summary.txt" ;; - SKIP) skip=$((skip + 1)); echo "$case_name SKIP" >> "$LOGDIR/summary.txt" ;; - FAIL) fail=$((fail + 1)); echo "$case_name FAIL" >> "$LOGDIR/summary.txt"; suite_rc=1 ;; + PASS) + pass=$((pass + 1)) + echo "$case_name PASS" >> "$LOGDIR/summary.txt" + ;; + SKIP) + skip=$((skip + 1)) + echo "$case_name SKIP" >> "$LOGDIR/summary.txt" + ;; + FAIL) + fail=$((fail + 1)) + echo "$case_name FAIL" >> "$LOGDIR/summary.txt" + suite_rc=1 + ;; esac done @@ -287,9 +446,19 @@ if [ -n "$JUNIT_OUT" ]; then log_info "Wrote JUnit: $JUNIT_OUT" fi +# Exit codes: PASS=0, FAIL=1, SKIP=2 +if [ "$pass" -eq 0 ] && [ "$fail" -eq 0 ] && [ "$skip" -gt 0 ]; then + log_skip "$TESTNAME SKIP" + echo "$TESTNAME SKIP" > "$RES_FILE" + exit 2 +fi + if [ "$suite_rc" -eq 0 ]; then - log_pass "$TESTNAME PASS"; echo "$TESTNAME PASS" > "$RES_FILE" -else - log_fail "$TESTNAME FAIL"; echo "$TESTNAME FAIL" > "$RES_FILE" + log_pass "$TESTNAME PASS" + echo "$TESTNAME PASS" > "$RES_FILE" + exit 0 fi -exit "$suite_rc" + +log_fail "$TESTNAME FAIL" +echo "$TESTNAME FAIL" > "$RES_FILE" +exit 1