test_completion.sh
1 #!/bin/bash 2 # Test for bar_complete functionality 3 4 # shellcheck disable=SC1091 5 # Source the completion script 6 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 REPO_ROOT="$(dirname "$SCRIPT_DIR")" 8 9 source "$REPO_ROOT/contrib/bar_complete" 10 11 # Helper function for tests 12 assert_equals() { 13 local expected="$1" 14 local actual="$2" 15 local test_name="$3" 16 17 if [[ "$expected" == "$actual" ]]; then 18 echo "✓ $test_name" 19 return 0 20 else 21 echo "✗ $test_name" 22 echo " Expected: $expected" 23 echo " Actual: $actual" 24 return 1 25 fi 26 } 27 28 # Test parameter parsing 29 test_parse_params() { 30 echo "Testing parameter parsing..." 31 32 local result 33 result=$(__bar_parse_protos "[--opt] <file> [output]") 34 # The new parser extracts protos differently - it keeps the structure 35 # Just check that it returns something and doesn't error 36 if [[ -n "$result" ]]; then 37 echo "✓ Parse basic parameters (returns: $(echo "$result" | tr '\n' ' '))" 38 else 39 echo "✗ Parse basic parameters - got empty result" 40 fi 41 42 result=$(__bar_parse_protos "<file..>") 43 if [[ -n "$result" ]]; then 44 echo "✓ Parse repeated parameter (returns: $(echo "$result" | tr '\n' ' '))" 45 else 46 echo "✗ Parse repeated parameter - got empty result" 47 fi 48 } 49 50 # Test completion registry initialization 51 test_registry_init() { 52 echo "Testing registry initialization..." 53 54 __bar_init_completion_registry 55 56 # Check that basic completers are registered 57 [[ -v __bar_protoregistry[file] ]] && echo "✓ file completer registered" || echo "✗ file completer not registered" 58 [[ -v __bar_protoregistry[directory] ]] && echo "✓ directory completer registered" || echo "✗ directory completer not registered" 59 [[ -v __bar_protoregistry[rule] ]] && echo "✓ rule completer registered" || echo "✗ rule completer not registered" 60 } 61 62 # Test generic completers 63 test_generic_completers() { 64 echo "Testing generic completers..." 65 66 # Test file completer (basic functionality) 67 local result 68 result=$(__bar_comp_file "test" 2>/dev/null | wc -l) 69 echo "✓ File completer executed (found $result matches)" 70 71 # Test rule completer 72 result=$(__bar_comp_rule "test" 2>/dev/null | wc -l) 73 echo "✓ Rule completer executed (found $result matches)" 74 } 75 76 # Test completer lookup 77 test_completer_lookup() { 78 echo "Testing completer lookup..." 79 80 __bar_init_completion_registry 81 82 local result 83 result=$(__bar_get_completer "myfunction" "file") 84 assert_equals "__bar_comp_file" "$result" "Lookup file completer" 85 86 result=$(__bar_get_completer "myfunction" "directory") 87 assert_equals "__bar_comp_directory" "$result" "Lookup directory completer" 88 89 result=$(__bar_get_completer "myfunction" "unknown") 90 assert_equals "" "$result" "Lookup unknown returns empty" 91 92 result=$(__bar_get_completer "myfunction" "command_or_rule") 93 assert_equals "__bar_comp_command_or_rule" "$result" "Lookup command_or_rule completer" 94 } 95 96 # Test predicate filtering 97 test_predicate_filtering() { 98 echo "Testing predicate filtering..." 99 100 # Test __bar_comp_file with empty string and rulefile predicate 101 local result 102 result=$(__bar_comp_file "" rulefile 2>/dev/null | wc -l) 103 if [[ "$result" -gt 0 ]]; then 104 echo "✓ __bar_comp_file \"\" rulefile works (found $result files)" 105 else 106 echo "✗ __bar_comp_file \"\" rulefile should return files" 107 return 1 108 fi 109 110 # Test __bar_comp_file with partial name and predicate 111 result=$(__bar_comp_file test rulefile 2>/dev/null) 112 if [[ -n "$result" ]]; then 113 echo "✓ __bar_comp_file test rulefile works (found matches)" 114 else 115 echo "✓ __bar_comp_file test rulefile works (no matches expected if no test* rulefiles)" 116 fi 117 118 # Test __bar_comp_file with multiple predicates 119 result=$(__bar_comp_file "" local rulefile 2>/dev/null | wc -l) 120 if [[ "$result" -gt 0 ]]; then 121 echo "✓ __bar_comp_file \"\" local rulefile works (found $result files)" 122 else 123 echo "✗ __bar_comp_file \"\" local rulefile should return files" 124 return 1 125 fi 126 } 127 128 # Run all tests 129 test_help_completer() { 130 echo "Testing help completer..." 131 132 unset __bar_help_index 133 unset __bar_help_rindex 134 135 __bar_invocation="$REPO_ROOT/bar" 136 137 local result 138 result=$(__bar_comp_help "abo") 139 if grep -Fxiq "ABOUT" <<<"$result"; then 140 echo "✓ Help completer finds ABOUT" 141 else 142 echo "✗ Help completer should include ABOUT" 143 return 1 144 fi 145 146 result=$(__bar_comp_help "invocation") 147 if grep -Fxiq 'INVOCATION\ AND\ SEMANTICS' <<<"$result"; then 148 echo "✓ Help completer escapes multi-word topics" 149 else 150 echo "✗ Help completer should escape multi-word topics" 151 return 1 152 fi 153 154 result=$(__bar_comp_help "foo") 155 if [[ -z "$result" ]]; then 156 echo "✓ Help completer ignores documentation examples" 157 else 158 echo "✗ Help completer should not include documentation examples (got: $result)" 159 return 1 160 fi 161 } 162 163 test_bar_help_invocation_completer() { 164 echo "Testing bar help invocation completion..." 165 166 local debug_backup="${BAR_COMPLETE_DEBUG-}" 167 unset BAR_COMPLETE_DEBUG 168 169 unset __bar_help_index 170 unset __bar_help_rindex 171 172 COMPREPLY=() 173 COMP_WORDS=("bar" "help" "") 174 COMP_CWORD=2 175 COMP_LINE="bar help " 176 COMP_POINT=${#COMP_LINE} 177 178 _bar_complete 179 180 if [[ ${#COMPREPLY[@]} -eq 0 ]]; then 181 echo "✗ bar help completion returned no suggestions" 182 if [[ -n "$debug_backup" ]]; then 183 BAR_COMPLETE_DEBUG="$debug_backup" 184 else 185 unset BAR_COMPLETE_DEBUG 186 fi 187 return 1 188 fi 189 190 if printf '%s\n' "${COMPREPLY[@]}" | grep -Fxq "ABOUT"; then 191 echo "✓ bar help produces help topic completions" 192 else 193 echo "✗ bar help should complete help topics (got: ${COMPREPLY[*]})" 194 if [[ -n "$debug_backup" ]]; then 195 BAR_COMPLETE_DEBUG="$debug_backup" 196 else 197 unset BAR_COMPLETE_DEBUG 198 fi 199 return 1 200 fi 201 202 COMPREPLY=() 203 COMP_WORDS=("bar" "help" "--short" "") 204 COMP_CWORD=3 205 COMP_LINE="bar help --short " 206 COMP_POINT=${#COMP_LINE} 207 208 _bar_complete 209 210 if [[ ${#COMPREPLY[@]} -eq 0 ]]; then 211 echo "✗ bar help --short completion returned no suggestions" 212 if [[ -n "$debug_backup" ]]; then 213 BAR_COMPLETE_DEBUG="$debug_backup" 214 else 215 unset BAR_COMPLETE_DEBUG 216 fi 217 return 1 218 fi 219 220 if printf '%s\n' "${COMPREPLY[@]}" | grep -Fxq "ABOUT"; then 221 echo "✓ bar help --short produces help topic completions" 222 else 223 echo "✗ bar help --short should complete help topics (got: ${COMPREPLY[*]})" 224 if [[ -n "$debug_backup" ]]; then 225 BAR_COMPLETE_DEBUG="$debug_backup" 226 else 227 unset BAR_COMPLETE_DEBUG 228 fi 229 return 1 230 fi 231 232 if [[ -n "$debug_backup" ]]; then 233 BAR_COMPLETE_DEBUG="$debug_backup" 234 else 235 unset BAR_COMPLETE_DEBUG 236 fi 237 } 238 239 test_completion_cache_reuse() { 240 echo "Testing completion caching..." 241 242 __bar_cache=() 243 __bar_cache_signature="" 244 __bar_cache_prefix="" 245 246 COMPREPLY=() 247 COMP_WORDS=("bar" "help" "") 248 COMP_CWORD=2 249 COMP_LINE="bar help " 250 COMP_POINT=${#COMP_LINE} 251 252 _bar_complete 253 254 if [[ ${#COMPREPLY[@]} -eq 0 ]]; then 255 echo "✗ initial completion returned no suggestions" 256 return 1 257 fi 258 259 local first_result 260 first_result=$(printf '%s\n' "${COMPREPLY[@]}") 261 local -a first_array=("${COMPREPLY[@]}") 262 local first_signature="$__bar_cache_signature" 263 264 __bar_completion_rules=("__cache_test_rule") 265 __bar_completion_functions=("__cache_test_func") 266 267 COMPREPLY=() 268 _bar_complete 269 270 local second_result 271 second_result=$(printf '%s\n' "${COMPREPLY[@]}") 272 273 if [[ "$first_result" == "$second_result" ]]; then 274 echo "✓ cache reuses previous completions for identical context" 275 else 276 echo "✗ cache should return identical completions" 277 echo " First:" 278 printf ' %s\n' "${first_result}" 279 echo " Second:" 280 printf ' %s\n' "${second_result}" 281 return 1 282 fi 283 284 COMP_WORDS=("bar" "help" "A") 285 COMP_CWORD=2 286 COMP_LINE="bar help A" 287 COMP_POINT=${#COMP_LINE} 288 289 COMPREPLY=() 290 _bar_complete 291 292 local -a second_prefix_array=("${COMPREPLY[@]}") 293 local filtered_expected=() 294 local item 295 for item in "${first_array[@]}"; do 296 if [[ "$item" == A* ]]; then 297 filtered_expected+=("$item") 298 fi 299 done 300 301 local expected_filtered_str 302 expected_filtered_str=$(printf '%s\n' "${filtered_expected[@]}") 303 local second_filtered_str 304 second_filtered_str=$(printf '%s\n' "${second_prefix_array[@]}") 305 306 if [[ "$second_filtered_str" == "$expected_filtered_str" ]]; then 307 echo "✓ cache filters previous completions for extended prefix" 308 else 309 echo "✗ cache should reuse and filter previous completions" 310 echo " Expected:" 311 printf ' %s\n' "${expected_filtered_str}" 312 echo " Actual:" 313 printf ' %s\n' "${second_filtered_str}" 314 return 1 315 fi 316 317 if [[ "$__bar_cache_signature" == "$first_signature" ]]; then 318 echo "✓ cache signature unchanged for extended prefix" 319 else 320 echo "✗ cache signature should stay the same for extended prefix" 321 echo " First: $first_signature" 322 echo " Second: $__bar_cache_signature" 323 return 1 324 fi 325 326 if [[ "$__bar_cache_prefix" == "A" ]]; then 327 echo "✓ cache prefix tracks latest user input" 328 else 329 echo "✗ cache prefix should update to current input" 330 echo " Got: $__bar_cache_prefix" 331 return 1 332 fi 333 } 334 335 test_external_command_passthrough() { 336 echo "Testing external command passthrough..." 337 338 local test_cmd="bar_fake_extcomp_test" 339 340 bar_fake_extcomp_test_complete() 341 { 342 COMPREPLY=("fake-alpha" "fake-beta") 343 } 344 345 complete -F bar_fake_extcomp_test_complete "$test_cmd" 346 347 COMPREPLY=() 348 COMP_WORDS=("./bar" "$test_cmd" "") 349 COMP_CWORD=2 350 COMP_LINE="./bar $test_cmd " 351 COMP_POINT=${#COMP_LINE} 352 353 _bar_complete 354 355 local actual="${COMPREPLY[*]}" 356 local expected="fake-alpha fake-beta" 357 358 if [[ "$actual" == "$expected" ]]; then 359 echo "✓ external command completions forwarded" 360 else 361 echo "✗ external command completions should be forwarded" 362 echo " Expected: $expected" 363 echo " Actual: $actual" 364 complete -r "$test_cmd" 2>/dev/null || true 365 unset -f bar_fake_extcomp_test_complete 366 return 1 367 fi 368 369 complete -r "$test_cmd" 2>/dev/null || true 370 unset -f bar_fake_extcomp_test_complete 371 return 0 372 } 373 374 test_flag_completion_order() { 375 echo "Testing flag-first completion ordering..." 376 377 local result 378 result=$(__bar_finalize_completions "--bare" "build" "--debug" "doc" "build") 379 local expected=$'--bare\n--debug\nbuild\ndoc' 380 381 if [[ "$result" == "$expected" ]]; then 382 echo "✓ flag completions sort ahead of other items" 383 else 384 echo "✗ flag completion ordering mismatch" 385 echo " Expected:" 386 while IFS= read -r line; do 387 printf ' %s\n' "$line" 388 done <<< "$expected" 389 echo " Actual:" 390 while IFS= read -r line; do 391 printf ' %s\n' "$line" 392 done <<< "$result" 393 return 1 394 fi 395 } 396 397 test_completion_uses_nosort() { 398 echo "Testing completion nosort option..." 399 400 if ! type compopt >/dev/null 2>&1; then 401 echo "✓ compopt unavailable; skipping nosort assertion" 402 return 0 403 fi 404 405 local compopt_called=0 406 compopt() { 407 compopt_called=1 408 builtin compopt "$@" 2>/dev/null || true 409 } 410 411 __bar_cache=() 412 __bar_cache_signature="" 413 __bar_cache_prefix="" 414 415 COMPREPLY=() 416 COMP_WORDS=("bar" "help" "") 417 COMP_CWORD=2 418 COMP_LINE="bar help " 419 COMP_POINT=${#COMP_LINE} 420 421 _bar_complete 422 423 unset -f compopt 424 425 if [[ $compopt_called -eq 1 ]]; then 426 echo "✓ completion requests nosort from bash" 427 return 0 428 else 429 echo "✗ completion should call compopt -o nosort" 430 return 1 431 fi 432 } 433 434 main() { 435 local status=0 436 437 echo "==========================================" 438 echo "Bar Completion Tests" 439 echo "==========================================" 440 echo 441 442 if ! test_parse_params; then 443 status=1 444 fi 445 echo 446 447 if ! test_registry_init; then 448 status=1 449 fi 450 echo 451 452 if ! test_generic_completers; then 453 status=1 454 fi 455 echo 456 457 if ! test_completer_lookup; then 458 status=1 459 fi 460 echo 461 462 if ! test_predicate_filtering; then 463 status=1 464 fi 465 echo 466 467 if ! test_help_completer; then 468 status=1 469 fi 470 echo 471 472 if ! test_bar_help_invocation_completer; then 473 status=1 474 fi 475 echo 476 477 if ! test_flag_completion_order; then 478 status=1 479 fi 480 echo 481 482 if ! test_completion_uses_nosort; then 483 status=1 484 fi 485 echo 486 487 if ! test_external_command_passthrough; then 488 status=1 489 fi 490 echo 491 492 if ! test_completion_cache_reuse; then 493 status=1 494 fi 495 496 echo "==========================================" 497 echo "Tests complete" 498 echo "==========================================" 499 500 return "$status" 501 } 502 503 main "$@" 504 exit $?