@@ -25,6 +25,7 @@ if [ -z "$INIT_ENV" ]; then
2525fi
2626
2727# Only source once (idempotent)
28+ # NOTE: We intentionally **do not export** any new vars. They stay local to this shell.
2829if [ -z " ${__INIT_ENV_LOADED:- } " ]; then
2930 # shellcheck disable=SC1090
3031 . " $INIT_ENV "
@@ -62,6 +63,16 @@ if [ -z "${REPEAT_POLICY:-}" ]; then REPEAT_POLICY="all"; fi
6263JUNIT_OUT=" "
6364VERBOSE=" 0"
6465
66+ # --- Stabilizers (opt-in) ---
67+ RETRY_ON_FAIL=" 0" # extra attempts after a FAIL
68+ POST_TEST_SLEEP=" 0" # settle time after each case
69+
70+ # --- Custom module source (opt-in; default is untouched) ---
71+ KO_DIRS=" " # colon-separated list of dirs that contain .ko files
72+ KO_TREE=" " # alt root that has lib/modules/$KVER
73+ KO_TARBALL=" " # optional tarball that we unpack once
74+ KO_PREFER_CUSTOM=" 0" # 1 = try custom first; default 0 = system first
75+
6576if [ -z " ${VIDEO_STACK:- } " ]; then VIDEO_STACK=" auto" ; fi
6677if [ -z " ${VIDEO_PLATFORM:- } " ]; then VIDEO_PLATFORM=" " ; fi
6778if [ -z " ${VIDEO_FW_DS:- } " ]; then VIDEO_FW_DS=" " ; fi
@@ -94,8 +105,15 @@ Usage: $0 [--config path.json|/path/dir] [--dir DIR] [--pattern GLOB]
94105 [--downstream-fw PATH] [--force]
95106 [--app /path/to/iris_v4l2_test]
96107 [--ssid SSID] [--password PASS]
108+ [--ko-dir DIR[:DIR2:...]] # opt-in: search these dirs for .ko on failure
109+ [--ko-tree ROOT] # opt-in: modprobe -d ROOT (expects lib/modules/\$ (uname -r))
110+ [--ko-tar FILE.tar[.gz|.xz]] # opt-in: unpack once under /run/iris_mods/\$ KVER, set --ko-tree/--ko-dir accordingly
111+ [--ko-prefer-custom] # opt-in: try custom sources before system
97112 [--app-launch-sleep S] [--inter-test-sleep S]
98113 [--log-flavor NAME] # internal: e.g. upstream or downstream (used by --stack both)
114+ # --- Stabilizers ---
115+ [--retry-on-fail N] # retry up to N times if a case ends FAIL
116+ [--post-test-sleep S] # sleep S seconds after each case
99117EOF
100118}
101119
@@ -190,6 +208,22 @@ while [ $# -gt 0 ]; do
190208 shift
191209 PASSWORD=" $1 "
192210 ;;
211+ --ko-dir)
212+ shift
213+ KO_DIRS=" $1 "
214+ ;;
215+ --ko-tree)
216+ shift
217+ KO_TREE=" $1 "
218+ ;;
219+ --ko-tar)
220+ shift
221+ KO_TARBALL=" $1 "
222+ ;;
223+ --ko-prefer-custom)
224+ KO_PREFER_CUSTOM=" 1"
225+ ;;
226+
193227 --app-launch-sleep)
194228 shift
195229 APP_LAUNCH_SLEEP=" $1 "
@@ -202,6 +236,15 @@ while [ $# -gt 0 ]; do
202236 shift
203237 LOG_FLAVOR=" $1 "
204238 ;;
239+ # --- Stabilizers ---
240+ --retry-on-fail)
241+ shift
242+ RETRY_ON_FAIL=" $1 "
243+ ;;
244+ --post-test-sleep)
245+ shift
246+ POST_TEST_SLEEP=" $1 "
247+ ;;
205248 --help|-h)
206249 usage
207250 exit 0
@@ -234,6 +277,44 @@ export INTER_TEST_SLEEP
234277# --- EARLY dependency check (bail out fast) ---
235278
236279# Ensure the app is executable if a path was provided but lacks +x
280+
281+ # --- Optional: unpack a custom module tarball **once** (no env exports) ---
282+ KVER=" $( uname -r 2> /dev/null || printf ' %s' unknown) "
283+ if [ -n " $KO_TARBALL " ] && [ -f " $KO_TARBALL " ]; then
284+ DEST=" /run/iris_mods/$KVER "
285+ if [ ! -d " $DEST " ]; then
286+ mkdir -p " $DEST " 2> /dev/null || true
287+ case " $KO_TARBALL " in
288+ * .tar|* .tar.gz|* .tgz|* .tar.xz|* .txz|* .tar.zst)
289+ if command -v tar > /dev/null 2>&1 ; then
290+ # best-effort; keep extraction bounded to DEST
291+ tar -xf " $KO_TARBALL " -C " $DEST " 2> /dev/null || true
292+ fi
293+ ;;
294+ * )
295+ # not a tar? treat as a directory if user passed one by mistake
296+ :
297+ ;;
298+ esac
299+ fi
300+ # decide whether the tar contained a full tree or loose .ko’s
301+ if [ -d " $DEST /lib/modules/$KVER " ]; then
302+ KO_TREE=" $DEST "
303+ else
304+ # find first dir that has at least one .ko (bounded depth)
305+ first_ko_dir=" $( find " $DEST " -type f -name ' *.ko*' -maxdepth 3 2> /dev/null | head -n1 | xargs -r dirname) "
306+ if [ -n " $first_ko_dir " ]; then
307+ if [ -n " $KO_DIRS " ]; then
308+ KO_DIRS=" $first_ko_dir :$KO_DIRS "
309+ else
310+ KO_DIRS=" $first_ko_dir "
311+ fi
312+ fi
313+ fi
314+ # quiet summary (only if user opted-in)
315+ log_info " Custom module source prepared (tree='${KO_TREE:- none} ', dirs='${KO_DIRS:- none} ', prefer_custom=$KO_PREFER_CUSTOM )"
316+ fi
317+
237318if [ -n " $VIDEO_APP " ] && [ -f " $VIDEO_APP " ] && [ ! -x " $VIDEO_APP " ]; then
238319 chmod +x " $VIDEO_APP " 2> /dev/null || true
239320 if [ ! -x " $VIDEO_APP " ]; then
@@ -497,6 +578,14 @@ if [ "${VIDEO_STACK}" = "both" ]; then
497578 args=" $args --inter-test-sleep $( printf %s " $INTER_TEST_SLEEP " ) "
498579 fi
499580
581+ # --- Stabilizers passthrough ---
582+ if [ -n " ${RETRY_ON_FAIL:- } " ]; then
583+ args=" $args --retry-on-fail $( printf %s " $RETRY_ON_FAIL " ) "
584+ fi
585+ if [ -n " ${POST_TEST_SLEEP:- } " ]; then
586+ args=" $args --post-test-sleep $( printf %s " $POST_TEST_SLEEP " ) "
587+ fi
588+
500589 printf " %s" " $args "
501590 }
502591
@@ -564,6 +653,15 @@ log_info "APP=$VIDEO_APP"
564653if [ -n " $VIDEO_FW_DS " ]; then
565654 log_info " Downstream FW override: $VIDEO_FW_DS "
566655fi
656+ if [ -n " $KO_TREE$KO_DIRS$KO_TARBALL " ]; then
657+ # print only when user actually provided custom sources
658+ if [ -n " $KO_TREE " ]; then
659+ log_info " Custom module tree (modprobe -d): $KO_TREE "
660+ fi
661+ if [ -n " $KO_DIRS " ]; then
662+ log_info " Custom ko dir(s): $KO_DIRS (prefer_custom=$KO_PREFER_CUSTOM )"
663+ fi
664+ fi
567665if [ -n " $VIDEO_FW_BACKUP_DIR " ]; then
568666 log_info " FW backup override: $VIDEO_FW_BACKUP_DIR "
569667fi
@@ -1019,6 +1117,38 @@ while IFS= read -r cfg; do
10191117 fi
10201118 fi
10211119
1120+ # (2) Retry on final failure (extra attempts outside REPEAT loop, before recording results)
1121+ if [ " $final " = " FAIL" ] && [ " $RETRY_ON_FAIL " -gt 0 ] 2> /dev/null; then
1122+ r=1
1123+ log_info " [$id ] RETRY_ON_FAIL: up to $RETRY_ON_FAIL additional attempt(s)"
1124+ while [ " $r " -le " $RETRY_ON_FAIL " ]; do
1125+ # optional delay between retries, reuse REPEAT_DELAY for consistency
1126+ if [ " $REPEAT_DELAY " -gt 0 ] 2> /dev/null; then
1127+ sleep " $REPEAT_DELAY "
1128+ fi
1129+
1130+ log_info " [$id ] retry attempt $r /$RETRY_ON_FAIL "
1131+ if video_run_once " $cfg " " $logf " " $TIMEOUT " " $SUCCESS_RE " " $LOGLEVEL " ; then
1132+ pass_runs=$(( pass_runs + 1 ))
1133+ final=" PASS"
1134+ log_pass " [$id ] RETRY succeeded — marking PASS"
1135+ break
1136+ else
1137+ # capture latest rc marker for visibility
1138+ rc_val=" $( awk -F' =' ' /^END-RUN rc=/{print $2}' " $logf " 2> /dev/null | tail -n1 | tr -d ' ' ) "
1139+ if [ -n " $rc_val " ]; then
1140+ case " $rc_val " in
1141+ 139) log_warn " [$id ] Retry exited rc=139 (SIGSEGV)." ;;
1142+ 134) log_warn " [$id ] Retry exited rc=134 (SIGABRT)." ;;
1143+ 137) log_warn " [$id ] Retry exited rc=137 (SIGKILL/OOM?)." ;;
1144+ * ) : ;;
1145+ esac
1146+ fi
1147+ fi
1148+ r=$(( r + 1 ))
1149+ done
1150+ fi
1151+
10221152 {
10231153 printf ' RESULT id=%s mode=%s pretty="%s" final=%s pass_runs=%s fail_runs=%s elapsed=%s\n' \
10241154 " $id " " $mode " " $pretty " " $final " " $pass_runs " " $fail_runs " " $elapsed "
@@ -1052,6 +1182,15 @@ while IFS= read -r cfg; do
10521182 fi
10531183 fi
10541184
1185+ # (6) Post-test settle sleep
1186+ case " $POST_TEST_SLEEP " in
1187+ ' ' |* [!0-9]* )
1188+ :
1189+ ;;
1190+ 0) : ;;
1191+ * ) log_info " Post-test sleep ${POST_TEST_SLEEP} s" ; sleep " $POST_TEST_SLEEP " ;;
1192+ esac
1193+
10551194 if [ " $MAX " -gt 0 ] && [ " $total " -ge " $MAX " ]; then
10561195 log_info " Reached MAX=$MAX tests; stopping"
10571196 break
0 commit comments