aboutsummaryrefslogtreecommitdiffstats
path: root/test-logic.sh
diff options
context:
space:
mode:
Diffstat (limited to 'test-logic.sh')
-rwxr-xr-xtest-logic.sh111
1 files changed, 111 insertions, 0 deletions
diff --git a/test-logic.sh b/test-logic.sh
new file mode 100755
index 0000000..13d97f8
--- /dev/null
+++ b/test-logic.sh
@@ -0,0 +1,111 @@
+#!/bin/bash
+#
+# Logic self-check for sbo-batch-test. Covers the pure, VM-independent parts:
+# dependency resolution (topo order, %README%, unmet-dep, cycles) and
+# BLOCKED-BY-DEP propagation (depends_on_failed). No overlay, no chroot.
+#
+# Run: bash test-logic.sh
+#
+set -uo pipefail
+
+SCRIPT="$(dirname "$0")/sbo-batch-test"
+T=$(mktemp -d)
+BASE=$(mktemp -d); mkdir -p "$BASE/var/log/packages"
+
+cleanup() { rm -rf "$T" "$BASE"; }
+trap cleanup EXIT
+
+# Fake SBo tree under one category. mk <prog> "<REQUIRES>".
+mk() { mkdir -p "$T/cat/$1"; echo "REQUIRES=\"$2\"" > "$T/cat/$1/$1.info"; }
+
+# Graph:
+# c (no deps)
+# b -> c
+# a -> b, %README%
+# d -> nonexistentpkg (unmet)
+# e -> f, f -> e (cycle)
+# g -> b (for blocked-by-dep: if b fails, g blocks)
+mk c ""
+mk b "c"
+mk a "b %README%"
+mk d "nonexistentpkg"
+mk e "f"
+mk f "e"
+mk g "b"
+
+# Source the script without running main(), then override config AFTER the
+# source (sourcing re-runs the CONFIG block, which would clobber test vars).
+LIB=$(mktemp)
+sed '/^main "\$@"$/d' "$SCRIPT" > "$LIB"
+# shellcheck disable=SC1090
+source "$LIB" 2>/dev/null
+rm -f "$LIB"
+SBO_TREE_ROOTS=("$T")
+SLACKWARE_BASE="$BASE"
+
+pass=0; fail=0
+ok() { echo " ok: $1"; ((pass++)); return 0; }
+bad() { echo " FAIL: $1"; ((fail++)); return 0; }
+
+# --- resolution -------------------------------------------------------------
+resolve_target "$T/cat/a"
+order=""; for x in "${RESOLVED_ORDER[@]}"; do order+="$(basename "$x") "; done
+order="${order% }" # trim trailing space
+[[ "$order" == "c b a" ]] && ok "topo order c b a" || bad "topo order, got: [$order]"
+[[ "${HAS_README[$T/cat/a]:-}" == "1" ]] && ok "%README% recorded" || bad "%README% not recorded"
+[[ ${#UNMET[@]} -eq 0 ]] && ok "no false unmet" || bad "unexpected unmet"
+
+resolve_target "$T/cat/d"
+[[ ${#UNMET[@]} -eq 1 ]] && ok "unmet-dep caught" || bad "unmet-dep missed"
+
+resolve_target "$T/cat/e"
+[[ ${#CYCLES[@]} -ge 1 ]] && ok "cycle caught" || bad "cycle missed"
+
+# --- BLOCKED-BY-DEP (depends_on_failed) -------------------------------------
+# depends_on_failed <slackbuild-dir> <nameref-to-dead-prog-array>
+# returns 0 if the dir directly REQUIRES any prog in the dead list.
+
+dead=(b)
+if depends_on_failed "$T/cat/g" dead; then ok "g blocked when b dead"; else bad "g should block on b"; fi
+if depends_on_failed "$T/cat/a" dead; then ok "a blocked when b dead (direct dep)"; else bad "a should block on b"; fi
+
+dead=(c)
+# a does NOT directly require c (a->b->c). One-hop check must say no here.
+if depends_on_failed "$T/cat/a" dead; then bad "a wrongly blocked on c (not a direct dep)"; else ok "a not directly blocked by c"; fi
+# but b DOES directly require c.
+if depends_on_failed "$T/cat/b" dead; then ok "b blocked when c dead"; else bad "b should block on c"; fi
+
+dead=()
+if depends_on_failed "$T/cat/a" dead; then bad "a blocked with empty dead list"; else ok "no block when nothing dead"; fi
+
+# %README% token in REQUIRES must not be treated as a dead dep.
+dead=("%README%")
+if depends_on_failed "$T/cat/a" dead; then bad "%README% treated as dep"; else ok "%README% not treated as dep"; fi
+
+# Propagation invariant note: depends_on_failed is a DIRECT-requires check only.
+# Transitive blocking works because run_target iterates in topo order and adds
+# each blocked package's own prog name to `dead`, so the failure of c blocks b
+# (direct), then b's name enters `dead`, which then blocks a (direct on b).
+# Simulate that one-hop cascade here:
+dead=(c)
+# topo order for a is: c b a. c "fails" -> seed dead=(c).
+chain=(c b a)
+declare -A blocked=()
+for p in "${chain[@]}"; do
+ [[ "$p" == "c" ]] && continue # c is the original failure, already in dead
+ if depends_on_failed "$T/cat/$p" dead; then
+ blocked[$p]=1
+ dead+=("$p") # mark dependents-of-this as blockable next hop
+ fi
+done
+if [[ "${blocked[b]:-}" == "1" && "${blocked[a]:-}" == "1" ]]; then
+ ok "transitive cascade c->b->a via one-hop propagation"
+else
+ bad "cascade dead: b=${blocked[b]:-0} a=${blocked[a]:-0}"
+fi
+
+# --- result -----------------------------------------------------------------
+echo
+echo "$pass passed, $fail failed"
+[[ $fail -eq 0 ]] || exit 1
+echo "ALL LOGIC CHECKS PASS"