aboutsummaryrefslogtreecommitdiffstats
path: root/test-logic.sh
blob: 13d97f8f183427c57078307bdb7f56f7bae9bc35 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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"