aboutsummaryrefslogtreecommitdiffstats
path: root/test-logic.sh
blob: 4505ecb99281ed879f646bcd9261dfd8f467cb0f (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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#!/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

# --- package cache ----------------------------------------------------------
PKG_CACHE=$(mktemp -d)
mkc() { mkdir -p "$PKG_CACHE/$1/$2"; : > "$PKG_CACHE/$1/$2/$3"; }

# exact version match -> cached
mkc net libfoo "libfoo-1.1-x86_64-1_danix.txz"
[[ "$(cache_decision net libfoo 1.1)" == "cached" ]] && ok "cache hit on version match" || bad "cache_decision should be cached, got [$(cache_decision net libfoo 1.1)]"

# different version cached -> bump:old:new
[[ "$(cache_decision net libfoo 1.2)" == "bump:1.1:1.2" ]] && ok "cache bump reported" || bad "cache_decision bump wrong, got [$(cache_decision net libfoo 1.2)]"

# nothing cached -> new
[[ "$(cache_decision net libbar 1.0)" == "new" ]] && ok "cache new for absent prog" || bad "cache_decision should be new, got [$(cache_decision net libbar 1.0)]"

# empty PKG_CACHE disables -> new
PKG_CACHE_SAVE="$PKG_CACHE"; PKG_CACHE=""
[[ "$(cache_decision net libfoo 1.1)" == "new" ]] && ok "empty PKG_CACHE disables (new)" || bad "disabled cache should be new, got [$(cache_decision net libfoo 1.1)]"
PKG_CACHE="$PKG_CACHE_SAVE"

# cache_path returns the hit file for a matching version
hit="$(cache_path net libfoo 1.1)"
[[ "$hit" == "$PKG_CACHE/net/libfoo/libfoo-1.1-x86_64-1_danix.txz" ]] && ok "cache_path returns hit" || bad "cache_path wrong, got [$hit]"

# cache_path empty when version does not match
[[ -z "$(cache_path net libfoo 9.9)" ]] && ok "cache_path empty on miss" || bad "cache_path should be empty on miss"

# cache_store evicts: prog dir holds exactly the new file
srctmp=$(mktemp -d); : > "$srctmp/libfoo-1.2-x86_64-1_danix.txz"
cache_store net libfoo "$srctmp/libfoo-1.2-x86_64-1_danix.txz"
count=$(find "$PKG_CACHE/net/libfoo" -name '*.t?z' | wc -l)
[[ "$count" -eq 1 ]] && ok "cache_store evicts to one file" || bad "cache_store left $count files"
[[ -e "$PKG_CACHE/net/libfoo/libfoo-1.2-x86_64-1_danix.txz" ]] && ok "cache_store stored new file" || bad "cache_store did not store new file"
rm -rf "$srctmp"

# --- result -----------------------------------------------------------------
echo
echo "$pass passed, $fail failed"
[[ $fail -eq 0 ]] || exit 1
echo "ALL LOGIC CHECKS PASS"