kit_sh_assert.sh (3122B)
1 # test/lib/kit_sh_assert.sh — Type-K mode-P procedural assert verbs + the 2 # CAS object/tree helpers shared by cas/ and pkg/. 3 # 4 # Sourced AFTER kit_sh_report.sh (kit_sh_kit.sh does this). The verbs route 5 # through kit_pass/kit_fail/kit_skip so counting, summary, and exit are unified — 6 # a harness calls kit_report_init at the top and kit_summary/kit_exit at the end, 7 # never its own counters. 8 # 9 # Contract: the caller provides a writable scratch dir $work; per-check 10 # .out/.err/.diag files are written under it. Mode-P suites are SERIAL (they 11 # share one $work and mutate fixtures), so these verbs do not emit KIT_EV 12 # events / participate in the corpus engine's parallel replay. 13 14 # ---- result verbs (thin wrappers over the report layer) -------------------- 15 16 ok() { kit_pass "$1"; } 17 18 # not_ok NAME [DIAGFILE] : record a failure; if DIAGFILE is given and non-empty, 19 # show it indented after the FAIL line. 20 not_ok() { 21 kit_fail "$1" 22 if [ "$#" -gt 1 ] && [ -s "$2" ]; then sed 's/^/ | /' "$2"; fi 23 } 24 25 skip_test() { kit_skip "$1" "${2:-}"; } 26 27 # ---- command-result asserts ------------------------------------------------ 28 29 run_ok() { 30 name=$1; shift 31 if "$@" > "$work/$name.out" 2> "$work/$name.err"; then ok "$name" 32 else not_ok "$name" "$work/$name.err"; fi 33 } 34 35 run_fail() { 36 name=$1; shift 37 if "$@" > "$work/$name.out" 2> "$work/$name.err"; then 38 { echo "command unexpectedly succeeded"; sed 's/^/stdout: /' "$work/$name.out"; } > "$work/$name.diag" 39 not_ok "$name" "$work/$name.diag" 40 else ok "$name"; fi 41 } 42 43 # ---- content asserts ------------------------------------------------------- 44 45 contains() { 46 name=$1; file=$2; needle=$3 47 if grep -F "$needle" "$file" >/dev/null 2>&1; then ok "$name" 48 else 49 { printf 'missing text: %s\n' "$needle"; sed 's/^/file: /' "$file"; } > "$work/$name.diag" 50 not_ok "$name" "$work/$name.diag" 51 fi 52 } 53 54 same_file() { 55 name=$1; want=$2; got=$3 56 if cmp -s "$want" "$got"; then ok "$name" 57 else 58 { printf 'files differ:\n'; printf ' want: %s\n' "$want"; printf ' got: %s\n' "$got"; } > "$work/$name.diag" 59 not_ok "$name" "$work/$name.diag" 60 fi 61 } 62 63 is_executable() { 64 name=$1; file=$2 65 if [ -x "$file" ]; then ok "$name" 66 else echo "not executable: $file" > "$work/$name.diag"; not_ok "$name" "$work/$name.diag"; fi 67 } 68 69 assert_file_exists() { 70 name=$1; file=$2 71 if [ -f "$file" ]; then ok "$name" 72 else echo "missing file: $file" > "$work/$name.diag"; not_ok "$name" "$work/$name.diag"; fi 73 } 74 75 # ---- CAS object/tree helpers (shared by cas + pkg) ------------------------- 76 77 first_hex_id() { 78 sed -n 's/.*\([0-9a-fA-F]\{64\}\).*/\1/p' "$1" | sed -n '1p' 79 } 80 81 id_prefix() { 82 printf '%.2s' "$1" 83 } 84 85 cas_object_path() { 86 root=$1; kind=$2; id=$3 87 prefix=$(id_prefix "$id") 88 printf '%s/%s/%s/%s\n' "$root" "$kind" "$prefix" "$id" 89 } 90 91 tree_blob_for_path() { 92 tree_file=$1; want=$2 93 awk -v want="$want" ' 94 $0 == "[file]" { in_file = 1; path = ""; blob = ""; next } 95 in_file && /^path = / { path = substr($0, 8); next } 96 in_file && /^blob = / { 97 blob = substr($0, 8); 98 if (path == want) { print blob; exit; } 99 } 100 ' "$tree_file" 101 }