/ tests / test_completion.sh
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 $?