|
| 1 | +#!/bin/sh |
| 2 | +# shellcheck disable=SC2034 # vars are passed by name to functions sourced from cc.sh |
| 3 | +# shellcheck disable=SC2154 # lsep/sep are defined by the sourced cc.sh block |
| 4 | +# |
| 5 | +# Copyright Spack Project Developers. See COPYRIGHT file for details. |
| 6 | +# |
| 7 | +# SPDX-License-Identifier: (Apache-2.0 OR MIT) |
| 8 | +# |
| 9 | +# Unit tests for the list-manipulation primitives in cc.sh (empty, setsep, |
| 10 | +# append, extend, preextend). The block between '# BEGIN list functions' and |
| 11 | +# '# END list functions' in cc.sh is extracted and sourced, so we can call |
| 12 | +# the functions directly without running the rest of the wrapper. |
| 13 | + |
| 14 | +SCRIPT_DIR=$(CDPATH='' cd -- "$(dirname -- "$0")" && pwd) |
| 15 | +REPO_DIR=$(CDPATH='' cd -- "$SCRIPT_DIR/.." && pwd) |
| 16 | +CC_SH="$REPO_DIR/cc.sh" |
| 17 | + |
| 18 | +if [ ! -f "$CC_SH" ]; then |
| 19 | + echo "Cannot find cc.sh at $CC_SH" >&2 |
| 20 | + exit 1 |
| 21 | +fi |
| 22 | + |
| 23 | +FUNCS_SH=$(mktemp) |
| 24 | +trap 'rm -f "$FUNCS_SH"' EXIT INT TERM |
| 25 | + |
| 26 | +awk '/^# BEGIN list functions$/{flag=1; next} /^# END list functions$/{flag=0} flag' \ |
| 27 | + "$CC_SH" > "$FUNCS_SH" |
| 28 | + |
| 29 | +# shellcheck disable=SC1090 |
| 30 | +. "$FUNCS_SH" |
| 31 | + |
| 32 | +# --------------------------------------------------------------------------- |
| 33 | +# Test harness |
| 34 | +# --------------------------------------------------------------------------- |
| 35 | + |
| 36 | +PASS_COUNT=0 |
| 37 | +FAIL_COUNT=0 |
| 38 | +CURRENT_TEST="" |
| 39 | +CURRENT_FAILED=0 |
| 40 | + |
| 41 | +start_test() { |
| 42 | + CURRENT_TEST="$1" |
| 43 | + CURRENT_FAILED=0 |
| 44 | +} |
| 45 | + |
| 46 | +end_test() { |
| 47 | + if [ "$CURRENT_FAILED" -eq 0 ]; then |
| 48 | + PASS_COUNT=$((PASS_COUNT + 1)) |
| 49 | + printf 'PASS %s\n' "$CURRENT_TEST" |
| 50 | + else |
| 51 | + FAIL_COUNT=$((FAIL_COUNT + 1)) |
| 52 | + printf 'FAIL %s (%d sub-check(s) failed)\n' "$CURRENT_TEST" "$CURRENT_FAILED" |
| 53 | + fi |
| 54 | +} |
| 55 | + |
| 56 | +fail() { |
| 57 | + CURRENT_FAILED=$((CURRENT_FAILED + 1)) |
| 58 | + printf ' [%s] %s\n' "$CURRENT_TEST" "$1" >&2 |
| 59 | +} |
| 60 | + |
| 61 | +# expect_eq LABEL ACTUAL EXPECTED |
| 62 | +expect_eq() { |
| 63 | + if [ "$2" != "$3" ]; then |
| 64 | + # Render the bell separator visibly in error output. |
| 65 | + _exp=$(printf '%s' "$3" | tr "$lsep" '|') |
| 66 | + _act=$(printf '%s' "$2" | tr "$lsep" '|') |
| 67 | + fail "$1: expected '$_exp', got '$_act'" |
| 68 | + fi |
| 69 | +} |
| 70 | + |
| 71 | +# expect_true LABEL CMD... |
| 72 | +expect_true() { |
| 73 | + _label="$1"; shift |
| 74 | + if ! "$@"; then |
| 75 | + fail "$_label: expected success, got failure" |
| 76 | + fi |
| 77 | +} |
| 78 | + |
| 79 | +# expect_false LABEL CMD... |
| 80 | +expect_false() { |
| 81 | + _label="$1"; shift |
| 82 | + if "$@"; then |
| 83 | + fail "$_label: expected failure, got success" |
| 84 | + fi |
| 85 | +} |
| 86 | + |
| 87 | +# --------------------------------------------------------------------------- |
| 88 | +# Tests |
| 89 | +# --------------------------------------------------------------------------- |
| 90 | + |
| 91 | +test_empty() { |
| 92 | + unset tvar || true |
| 93 | + expect_true empty_unset empty tvar |
| 94 | + tvar='' |
| 95 | + expect_true empty_empty empty tvar |
| 96 | + tvar='x' |
| 97 | + expect_false empty_nonempty empty tvar |
| 98 | + tvar=' ' |
| 99 | + expect_false empty_space empty tvar |
| 100 | + unset tvar |
| 101 | +} |
| 102 | + |
| 103 | +test_setsep() { |
| 104 | + setsep foo_dirs; expect_eq setsep_dirs "$sep" ':' |
| 105 | + setsep FOO_DIRS; expect_eq setsep_DIRS "$sep" ':' |
| 106 | + setsep MYPATH; expect_eq setsep_PATH "$sep" ':' |
| 107 | + setsep MYPATHS; expect_eq setsep_PATHS "$sep" ':' |
| 108 | + setsep foo_list; expect_eq setsep_list "$sep" "$lsep" |
| 109 | + setsep whatever; expect_eq setsep_other "$sep" ' ' |
| 110 | +} |
| 111 | + |
| 112 | +test_append() { |
| 113 | + # _list (lsep separator) |
| 114 | + tgt_list='' |
| 115 | + append tgt_list a |
| 116 | + expect_eq append_list_empty "$tgt_list" "a" |
| 117 | + append tgt_list b |
| 118 | + expect_eq append_list_two "$tgt_list" "a${lsep}b" |
| 119 | + append tgt_list c |
| 120 | + expect_eq append_list_three "$tgt_list" "a${lsep}b${lsep}c" |
| 121 | + |
| 122 | + # _dirs (colon separator) |
| 123 | + tgt_dirs='' |
| 124 | + append tgt_dirs /a |
| 125 | + append tgt_dirs /b |
| 126 | + expect_eq append_dirs "$tgt_dirs" "/a:/b" |
| 127 | + |
| 128 | + # default (space separator) |
| 129 | + tgt_other='' |
| 130 | + append tgt_other x |
| 131 | + append tgt_other y |
| 132 | + expect_eq append_default "$tgt_other" "x y" |
| 133 | +} |
| 134 | + |
| 135 | +test_extend_empty_source() { |
| 136 | + src_list='' |
| 137 | + tgt_list='existing' |
| 138 | + extend tgt_list src_list |
| 139 | + expect_eq extend_empty_src "$tgt_list" 'existing' |
| 140 | +} |
| 141 | + |
| 142 | +test_extend_single_into_empty() { |
| 143 | + src_list='a' |
| 144 | + tgt_list='' |
| 145 | + extend tgt_list src_list |
| 146 | + expect_eq extend_single "$tgt_list" 'a' |
| 147 | +} |
| 148 | + |
| 149 | +test_extend_multi_into_empty() { |
| 150 | + src_list="a${lsep}b${lsep}c" |
| 151 | + tgt_list='' |
| 152 | + extend tgt_list src_list |
| 153 | + expect_eq extend_multi_empty "$tgt_list" "a${lsep}b${lsep}c" |
| 154 | +} |
| 155 | + |
| 156 | +test_extend_multi_into_nonempty() { |
| 157 | + src_list="b${lsep}c" |
| 158 | + tgt_list='a' |
| 159 | + extend tgt_list src_list |
| 160 | + expect_eq extend_multi_nonempty "$tgt_list" "a${lsep}b${lsep}c" |
| 161 | +} |
| 162 | + |
| 163 | +test_extend_prefix() { |
| 164 | + src_list="b${lsep}c" |
| 165 | + tgt_list='a' |
| 166 | + extend tgt_list src_list '-I' |
| 167 | + expect_eq extend_prefix "$tgt_list" "a${lsep}-Ib${lsep}-Ic" |
| 168 | +} |
| 169 | + |
| 170 | +test_extend_cross_separator() { |
| 171 | + # Source uses ':' (dirs), target uses lsep (list). |
| 172 | + src_dirs='a:b:c' |
| 173 | + tgt_list='' |
| 174 | + extend tgt_list src_dirs |
| 175 | + expect_eq extend_dirs_to_list_empty "$tgt_list" "a${lsep}b${lsep}c" |
| 176 | + |
| 177 | + tgt_list='x' |
| 178 | + extend tgt_list src_dirs '-L' |
| 179 | + expect_eq extend_dirs_to_list_nonempty "$tgt_list" "x${lsep}-La${lsep}-Lb${lsep}-Lc" |
| 180 | +} |
| 181 | + |
| 182 | +test_extend_default_separator() { |
| 183 | + src_other='a b c' |
| 184 | + tgt_other='x' |
| 185 | + extend tgt_other src_other |
| 186 | + expect_eq extend_default_sep "$tgt_other" 'x a b c' |
| 187 | +} |
| 188 | + |
| 189 | +test_preextend_empty_source() { |
| 190 | + src_list='' |
| 191 | + tgt_list='existing' |
| 192 | + preextend tgt_list src_list |
| 193 | + expect_eq preextend_empty_src "$tgt_list" 'existing' |
| 194 | +} |
| 195 | + |
| 196 | +test_preextend_single_into_empty() { |
| 197 | + src_list='a' |
| 198 | + tgt_list='' |
| 199 | + preextend tgt_list src_list |
| 200 | + expect_eq preextend_single "$tgt_list" 'a' |
| 201 | +} |
| 202 | + |
| 203 | +test_preextend_multi_into_empty() { |
| 204 | + # The original reversed-prepend logic existed to preserve source order. |
| 205 | + src_list="a${lsep}b${lsep}c" |
| 206 | + tgt_list='' |
| 207 | + preextend tgt_list src_list |
| 208 | + expect_eq preextend_multi_empty "$tgt_list" "a${lsep}b${lsep}c" |
| 209 | +} |
| 210 | + |
| 211 | +test_preextend_multi_into_nonempty() { |
| 212 | + src_list="a${lsep}b" |
| 213 | + tgt_list="c${lsep}d" |
| 214 | + preextend tgt_list src_list |
| 215 | + expect_eq preextend_multi_nonempty "$tgt_list" "a${lsep}b${lsep}c${lsep}d" |
| 216 | +} |
| 217 | + |
| 218 | +test_preextend_prefix() { |
| 219 | + src_list="a${lsep}b" |
| 220 | + tgt_list='c' |
| 221 | + preextend tgt_list src_list '-I' |
| 222 | + expect_eq preextend_prefix "$tgt_list" "-Ia${lsep}-Ib${lsep}c" |
| 223 | +} |
| 224 | + |
| 225 | +test_preextend_cross_separator() { |
| 226 | + src_dirs='a:b:c' |
| 227 | + tgt_list='x' |
| 228 | + preextend tgt_list src_dirs |
| 229 | + expect_eq preextend_dirs_to_list "$tgt_list" "a${lsep}b${lsep}c${lsep}x" |
| 230 | +} |
| 231 | + |
| 232 | +test_lsep_prepend_pattern() { |
| 233 | + # The inline replacement for the old prepend helper, as used at |
| 234 | + # 'full_command_list="${SPACK_CCACHE_BINARY}${lsep}${full_command_list}"'. |
| 235 | + tgt_list='' |
| 236 | + append tgt_list compiler |
| 237 | + append tgt_list -O2 |
| 238 | + tgt_list="ccache${lsep}${tgt_list}" |
| 239 | + |
| 240 | + IFS="$lsep" |
| 241 | + # shellcheck disable=SC2086 |
| 242 | + set -- $tgt_list |
| 243 | + unset IFS |
| 244 | + expect_eq prepend_pattern_count "$#" 3 |
| 245 | + expect_eq prepend_pattern_first "$1" 'ccache' |
| 246 | + expect_eq prepend_pattern_mid "$2" 'compiler' |
| 247 | + expect_eq prepend_pattern_last "$3" '-O2' |
| 248 | +} |
| 249 | + |
| 250 | +# --------------------------------------------------------------------------- |
| 251 | +# Runner |
| 252 | +# --------------------------------------------------------------------------- |
| 253 | + |
| 254 | +all_tests=' |
| 255 | +test_empty |
| 256 | +test_setsep |
| 257 | +test_append |
| 258 | +test_extend_empty_source |
| 259 | +test_extend_single_into_empty |
| 260 | +test_extend_multi_into_empty |
| 261 | +test_extend_multi_into_nonempty |
| 262 | +test_extend_prefix |
| 263 | +test_extend_cross_separator |
| 264 | +test_extend_default_separator |
| 265 | +test_preextend_empty_source |
| 266 | +test_preextend_single_into_empty |
| 267 | +test_preextend_multi_into_empty |
| 268 | +test_preextend_multi_into_nonempty |
| 269 | +test_preextend_prefix |
| 270 | +test_preextend_cross_separator |
| 271 | +test_lsep_prepend_pattern |
| 272 | +' |
| 273 | + |
| 274 | +if [ $# -gt 0 ]; then |
| 275 | + tests_to_run="$*" |
| 276 | +else |
| 277 | + tests_to_run="$all_tests" |
| 278 | +fi |
| 279 | + |
| 280 | +for t in $tests_to_run; do |
| 281 | + start_test "$t" |
| 282 | + "$t" |
| 283 | + end_test |
| 284 | +done |
| 285 | + |
| 286 | +printf '\n%d passed, %d failed\n' "$PASS_COUNT" "$FAIL_COUNT" |
| 287 | +[ "$FAIL_COUNT" -eq 0 ] |
0 commit comments