diff --git a/completion/README.md b/completion/README.md index 412cc04..4654c47 100644 --- a/completion/README.md +++ b/completion/README.md @@ -40,233 +40,3 @@ source ./bash_completion.sh ``` Add this line to your `.bashrc` to enable it permanently. - -## Usage Examples - -### Stage Completion - -**Context-aware completion**: When targeting directories, only `file@` completions are shown to avoid ambiguous stage names: - -```bash -$ markdown-runner test/cases/ -B -happy.md@ parallel.md@ teardown.md@ writer.md@ -``` - -**File-specific completion**: When you specify a markdown file, completion shows stages from that file: - -```bash -$ markdown-runner -B -help init main setup teardown test -``` - -**Smart file-specific completion**: When you specify a markdown file, completion only shows stages from that file: - -```bash -$ markdown-runner test/cases/happy.md -B -test - -$ markdown-runner test/cases/teardown.md -B -main teardown -``` - -### Chunk Completion - -When you type `markdown-runner -B setup/` and press Tab twice: - -```bash -$ markdown-runner -B setup/ -setup/0 setup/1 setup/cleanup setup/init -``` - -**File-specific chunk completion**: When a file is specified, chunks are only from that file: - -```bash -$ markdown-runner test/cases/teardown.md -B main/ -main/failing main/succeeding -``` - -- **Named chunks**: Shows actual chunk IDs (like `cleanup`, `init`) -- **Unnamed chunks**: Shows indices (like `0`, `1`, `2`) - -### File Path Completion - -Navigate through directories and complete markdown files: - -```bash -$ markdown-runner test -$ markdown-runner test/ - -$ markdown-runner test/ -$ markdown-runner test/cases/ - -$ markdown-runner test/cases/ -test/cases/happy.md test/cases/parallel.md test/cases/teardown.md - -$ markdown-runner test/cases/h -$ markdown-runner test/cases/happy.md -``` - -**Efficient chaining**: Directory completions and file@ completions don't add trailing spaces, allowing you to immediately press Tab again to continue completing: - -```bash -$ markdown-runner test/cases/ -B parallel.m -$ markdown-runner test/cases/ -B parallel.md@ - -$ markdown-runner test/cases/ -B parallel.md@ -$ markdown-runner test/cases/ -B parallel.md@parallel_write -``` - -### Partial Completion - -Type part of a stage or chunk name and press Tab: - -```bash -$ markdown-runner -B set -$ markdown-runner -B setup/ - -$ markdown-runner -B setup/i -$ markdown-runner -B setup/init -``` - -## How It Works - -The completion script: - -1. **Discovers markdown files** in your current directory (or recursively with `-r` flag) -2. **Parses stage definitions** by looking for code blocks with `{"stage":"name"}` metadata -3. **Extracts chunk information** including IDs and calculates indices for unnamed chunks -4. **Provides intelligent completion** based on what you've typed so far - -### File Discovery - -- **Default**: Searches `.md` files in current directory only -- **With `-r` flag**: Recursively searches subdirectories for `.md` files - -### Stage Discovery - -Finds stages by parsing code blocks like: -```markdown -```bash {"stage":"setup", "id":"init"} -echo "Setting up..." -``` -``` - -### Chunk Discovery - -For each stage, discovers: -- **Named chunks**: Uses the `"id"` field (e.g., `init`, `cleanup`) -- **Unnamed chunks**: Assigns indices based on order (e.g., `0`, `1`, `2`) - -## Compatibility - -- Requires **bash 4.0+** -- Works with **bash-completion** package -- Tested on Fedora 42 - -## Troubleshooting - -### Completion Not Working - -1. Make sure bash-completion is installed: - ```bash - # Ubuntu/Debian - sudo apt install bash-completion - ``` - -2. Restart your shell or source the completion: - ```bash - source ~/.bash_completion.d/markdown-runner - ``` - -### No Stages Found - -- Make sure you're in a directory with `.md` files -- Check that your markdown files have properly formatted stage definitions -- Use `--recursive` flag if your markdown files are in subdirectories - -### Slow Completion - -- Large numbers of markdown files can slow completion -- Consider using the `--filter` flag to limit which files are processed -- The completion caches results within a single completion session - -## Development - -The completion script is located in `bash_completion.sh` and has been refactored into a modular architecture with 25 focused functions: - -### Core Architecture - -**Main Orchestrator:** -- `_markdown_runner_completion()`: Main completion function that delegates to specialized handlers - -**Completion Handlers:** -- `_handle_flag_value_completion()`: Handles flag value completions (`-B`, `-s`, `-t`, `--view`, `-f`) -- `_handle_flag_completion()`: Handles flag name completions and enhanced help -- `_handle_help_completion()`: Handles help command completion -- `_handle_positional_completion()`: Handles file and directory completion - -**Flag Management:** -- `_get_available_flags()`: Main flag filtering orchestrator -- `_collect_used_flags()`: Identifies already-used flags -- `_collect_incompatible_flags()`: Identifies incompatible flag combinations -- `_filter_available_flags()`: Applies filtering logic - -**Utility Functions:** -- `_get_flag_equivalent()`: Maps short/long flag equivalents -- `_flag_takes_value()`: Determines if a flag expects a value -- `_skip_flag_and_value()`: Standardized flag parsing -- `_is_markdown_file()`: Unified file extension checking -- `_find_markdown_file_in_args()`: Centralized file discovery -- `_is_completing_flag_value()`: Context detection -- `_apply_nospace_for_file_at()`: Consistent nospace behavior -- `_has_file_or_dir_argument()`: Positional argument detection - -**Specialized Completion Functions:** -- `_markdown_runner_file_completion()`: Handles file and directory completion for .md files -- `_markdown_runner_break_at_completion()`: Handles `-B` flag completion -- `_markdown_runner_start_from_completion()`: Handles `-s` flag completion -- `_markdown_runner_stage_completion()`: Discovers and completes stage names -- `_markdown_runner_chunk_completion()`: Discovers and completes chunk IDs/indices -- `_markdown_runner_file_at_stage_completion()`: Handles file@stage completion -- `_markdown_runner_file_at_chunk_completion()`: Handles file@stage/chunk completion - -**Context Detection:** -- `_is_directory_context()`: Determines if completion is for a directory vs specific file -- `_is_valid_stage_name()`: Validates stage names against markdown files - -**Enhanced UX:** -- `_markdown_runner_show_enhanced_help()`: Shows flags with short descriptions -- `_markdown_runner_show_full_help()`: Shows full categorized help - -### Architecture Benefits - -- **Maintainability**: Single-responsibility functions are easier to understand and modify -- **Testability**: Each component can be tested independently -- **Extensibility**: Clear extension points for new features -- **Debugging**: Issues can be isolated to specific functions - -## Testing - -The completion functionality is backed by a comprehensive test suite with **191 tests** across **18 test suites**: - -### Test Coverage -- **Integration tests**: End-to-end completion scenarios -- **Flag completion tests**: All flag types and combinations -- **Stage/chunk completion**: Stage discovery and chunk completion -- **File completion**: File and directory completion -- **Context detection**: Directory vs file contexts -- **Edge cases**: Error handling and boundary conditions -- **Regression tests**: Prevent future breakage - -### Running Tests -```bash -# Run all tests -cd completion/tests -./run_all_tests.sh - -# Run specific test suite -./test_flag_completions.sh -./test_stage_chunk_completion.sh -``` - -See [`tests/README.md`](tests/README.md) for detailed test documentation. diff --git a/completion/bash_completion.sh b/completion/bash_completion.sh index 8511f4a..a84832e 100644 --- a/completion/bash_completion.sh +++ b/completion/bash_completion.sh @@ -1,676 +1,515 @@ #!/bin/bash # Bash completion for markdown-runner -# Source this file or add it to your bash completion directory - -# Flag mapping configuration - central definition of short/long flag pairs -_get_flag_equivalent() { - local flag="$1" - case "$flag" in - -d) echo "--dry-run" ;; - --dry-run) echo "-d" ;; - -l) echo "--list" ;; - --list) echo "-l" ;; - -i) echo "--interactive" ;; - --interactive) echo "-i" ;; - -s) echo "--start-from" ;; - --start-from) echo "-s" ;; - -B) echo "--break-at" ;; - --break-at) echo "-B" ;; - -t) echo "--timeout" ;; - --timeout) echo "-t" ;; - -u) echo "--update-files" ;; - --update-files) echo "-u" ;; - -f) echo "--filter" ;; - --filter) echo "-f" ;; - -r) echo "--recursive" ;; - --recursive) echo "-r" ;; - -v) echo "--verbose" ;; - --verbose) echo "-v" ;; - -q) echo "--quiet" ;; - --quiet) echo "-q" ;; - -h) echo "--help" ;; - --help) echo "-h" ;; - *) echo "" ;; # No equivalent - esac -} - -# Collect flags that are already used on the command line -# Returns array of used flags including both short and long forms -_collect_used_flags() { - local used_flags=() - for ((i=1; i/dev/null | sort } -# Filter available flags by excluding used and incompatible flags -# Args: $1 - space-separated list of all possible flags -# $2 - newline-separated list of used flags -# $3 - newline-separated list of incompatible flags -# Returns: space-separated list of available flags -_filter_available_flags() { - local opts="$1" - local used_flags="$2" - local incompatible_flags="$3" - local available_opts="" - - # Convert newline-separated lists to arrays for easier searching - local used_array=() - local incompatible_array=() - - while IFS= read -r flag; do - [[ -n "$flag" ]] && used_array+=("$flag") - done <<< "$used_flags" - - while IFS= read -r flag; do - [[ -n "$flag" ]] && incompatible_array+=("$flag") - done <<< "$incompatible_flags" - - # Check each option for exclusion - for opt in $opts; do - local is_excluded=false - - # Check if flag is already used - for used in "${used_array[@]}"; do - if [[ "$opt" == "$used" ]]; then - is_excluded=true - break - fi - done - - # Check if flag is incompatible - if [[ "$is_excluded" == false ]]; then - for incompatible in "${incompatible_array[@]}"; do - if [[ "$opt" == "$incompatible" ]]; then - is_excluded=true - break - fi - done - fi +# Extract target path and recursive flag from the command line +# Scans COMP_WORDS to find the directory/file argument and -r/--recursive flag. +# Skips over flags, flag values, and @-syntax arguments to find the target path. +# Output: pipe-delimited string "path|recursive" (e.g., ".|false" or "./docs|true") +_get_target_context() { + local target_path="" + local recursive=false + local i - if [[ "$is_excluded" == false ]]; then - available_opts="$available_opts $opt" - fi + # Check for recursive flag + for ((i=1; i<${#COMP_WORDS[@]}; i++)); do + [[ "${COMP_WORDS[i]}" == "-r" || "${COMP_WORDS[i]}" == "--recursive" ]] && recursive=true done - echo "$available_opts" -} - -# Main function to get available flags (excluding already used and incompatible ones) -# Args: $1 - space-separated list of all possible flags -# Returns: space-separated list of available flags -_get_available_flags() { - local opts="$1" + # Find target path (skip flags and their values) + for ((i=1; i/dev/null || true - fi -} + # Add incompatible flags + for ((i=1; i/dev/null || true -} - -# Function to show full categorized help (like --help output) -_markdown_runner_show_full_help() { - # Show the same categorized format as --help but as completions - COMPREPLY=( - "Modes:" - " -d, --dry-run Just list what would be executed without doing it" - " -l, --list Just list the files found" - "" - "Execution Control:" - " -i, --interactive Prompt to press enter between each chunk" - " -s, --start-from string Start from a specific stage (stage or file@stage)" - " -B, --break-at string Start debugging from a specific stage or chunk" - " -t, --timeout int The timeout in minutes for every executed command" - " -u, --update-files Update the chunk output section in the markdown files" - " --ignore-breakpoints Ignore the breakpoints" - "" - "File Selection:" - " -f, --filter string Run only the files matching the regex" - " -r, --recursive Search for markdown files recursively" - "" - "Output & Logging:" - " --view string UI to be used, can be 'default' or 'ci'" - " -v, --verbose Print more logs" - " -q, --quiet Disable output" - " --no-styling Disable spinners in CLI" - "" - "Help:" - " -h, --help Show this help message" - ) - - # Use compopt to prevent adding space after completion - compopt -o nospace 2>/dev/null || true + printf '%s\n' "${excluded[@]}" | sort -u } -# Handle flag value completions -# Args: $1 - previous word (flag), $2 - current word -# Returns: 0 if handled, 1 if not a flag value -_handle_flag_value_completion() { - local prev="$1" - local cur="$2" +# Get list of flags available for completion +# Filters ALL_FLAGS to remove flags that are already used or incompatible. +# Output: space-separated list of available flags +_get_available_flags() { + local excluded + excluded=$(_get_excluded_flags) + + local available=() + local flag ex is_excluded + for flag in $ALL_FLAGS; do + is_excluded=false + while IFS= read -r ex; do + [[ "$flag" == "$ex" ]] && { is_excluded=true; break; } + done <<< "$excluded" + [[ "$is_excluded" == false ]] && available+=("$flag") + done - case "$prev" in - -B|--break-at) - _markdown_runner_break_at_completion "$cur" - _apply_nospace_for_file_at - return 0 - ;; - -s|--start-from) - _markdown_runner_start_from_completion "$cur" - _apply_nospace_for_file_at - return 0 - ;; - -t|--timeout) - # Numeric completion for timeout - COMPREPLY=( $(compgen -W "1 5 10 30 60" -- "$cur") ) - return 0 - ;; - --view) - COMPREPLY=( $(compgen -W "default ci" -- "$cur") ) - return 0 - ;; - -f|--filter) - # No specific completion for regex patterns - return 0 - ;; - esac + echo "${available[*]}" +} - return 1 # Not a flag value +# ============================================================================ +# Stage & Chunk Discovery +# ============================================================================ + +# Extract unique stage names from markdown files +# Searches for code blocks with JSON metadata containing "stage" property. +# Args: $1 - space-separated list of markdown file paths +# Output: newline-separated list of unique stage names, sorted +_get_stages() { + local files="$1" + [[ -z "$files" ]] && return + + echo "$files" | xargs grep -h '```[a-zA-Z0-9_. -]*{.*"stage"' 2>/dev/null | \ + sed -n 's/.*"stage"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | \ + sort -u } -# Handle flag completions (when current word starts with -) -# Args: $1 - current word, $2 - available options -# Returns: 0 always (always handles flag completion) -_handle_flag_completion() { - local cur="$1" - local opts="$2" +# Check if a stage name exists in the relevant markdown files +# Searches for the stage in either a specific file mentioned in args or all executable files. +# Args: $1 - stage name to validate +# Returns: 0 if stage exists, 1 otherwise +_is_valid_stage() { + local stage="$1" + local files target_file - # Check if user wants enhanced help (just "-") - if [[ "$cur" == "-" ]]; then - # Show enhanced help format for bare "-" - _markdown_runner_show_enhanced_help + # Check for specific markdown file in args first + target_file=$(_find_markdown_in_args) + if [[ -n "$target_file" ]]; then + files="$target_file" else - # Standard flag completion - local available_opts - available_opts=$(_get_available_flags "$opts") - COMPREPLY=( $(compgen -W "${available_opts}" -- "$cur") ) + files=$(_get_executable_files | tr '\n' ' ') fi - return 0 -} + [[ -z "$files" ]] && return 1 -# Handle help command completion -# Args: $1 - current word -# Returns: 0 if handled, 1 if not help -_handle_help_completion() { - local cur="$1" + echo "$files" | xargs grep -lq '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' 2>/dev/null +} - # Check for special help command - if [[ "$cur" == "help" ]] || [[ "$cur" == "hel" ]] || [[ "$cur" == "he" ]]; then - # Show full categorized help - _markdown_runner_show_full_help - return 0 - fi +# Backward compatibility alias for _is_valid_stage +_is_valid_stage_name() { + _is_valid_stage "$@" +} - return 1 # Not help +# Get chunk IDs or indices for a specific stage +# Extracts chunks belonging to a stage. If chunk has an "id" property, uses that; +# otherwise falls back to zero-based index. +# Args: $1 - stage name +# $2 - space-separated list of markdown file paths +# Output: newline-separated list of chunk identifiers +_get_chunks() { + local stage="$1" + local files="$2" + [[ -z "$files" ]] && return + + local chunks + chunks=$(echo "$files" | xargs grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' 2>/dev/null) + + local index=0 chunk_id + while IFS= read -r line; do + [[ -z "$line" ]] && continue + chunk_id=$(echo "$line" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') + echo "${chunk_id:-$index}" + ((index++)) + done <<< "$chunks" } -# Check if a file or directory is already provided as positional argument -# Returns: 0 if file/dir provided, 1 otherwise -_has_file_or_dir_argument() { - for ((i=1; i/dev/null | awk '{s+=$1} END {print s}' } -# Handle positional argument completion (files and directories) -# Args: $1 - current word, $2 - available options -# Returns: 0 always -_handle_positional_completion() { +# ============================================================================ +# Completion Functions +# ============================================================================ + +# Complete stage names with smart nospace handling +# Provides stage name completions. For multi-chunk stages, disables automatic space +# after completion so user can type "/" to access chunk completion. +# Args: $1 - current word being completed +# $2 - optional: space-separated list of markdown files (auto-detected if empty) +# Side effects: Sets COMPREPLY array; may disable nospace via compopt +_complete_stages() { local cur="$1" - local opts="$2" - - if _has_file_or_dir_argument; then - # Markdown file or directory already provided, show flags - local available_opts - available_opts=$(_get_available_flags "$opts") - COMPREPLY=( $(compgen -W "${available_opts}" -- "$cur") ) - else - # No markdown file or directory yet, complete with .md files and directories - _markdown_runner_file_completion "$cur" - - # Check if any completion ends with '/' (directory) - # If so, disable space suffix to allow chaining - local has_dirs=false - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == */ ]]; then - has_dirs=true - break - fi - done - - if [[ "$has_dirs" == true ]]; then - compopt -o nospace 2>/dev/null || true + local files="$2" + + # If files not provided, get them intelligently + if [[ -z "$files" ]]; then + local target_file + target_file=$(_find_markdown_in_args) + if [[ -n "$target_file" ]]; then + files="$target_file" + else + files=$(_get_executable_files | tr '\n' ' ') fi fi - return 0 -} - -_markdown_runner_completion() { - local cur prev opts - COMPREPLY=() - cur="${COMP_WORDS[COMP_CWORD]}" - prev="${COMP_WORDS[COMP_CWORD-1]}" - - # Basic options - opts="-d --dry-run -l --list -i --interactive -s --start-from -B --break-at -t --timeout -u --update-files --ignore-breakpoints -f --filter -r --recursive --view -v --verbose -q --quiet --no-styling -h --help" - - # Try flag value completion first - if _handle_flag_value_completion "$prev" "$cur"; then - return 0 - fi - - # Handle flag completion (current word starts with -) - if [[ "$cur" == -* ]]; then - _handle_flag_completion "$cur" "$opts" - return 0 - fi + local stages + stages=$(_get_stages "$files") + COMPREPLY=( $(compgen -W "$stages" -- "$cur") ) - # Handle help command completion - if _handle_help_completion "$cur"; then - return 0 + # Smart UX: If ANY of the offered stages has multiple chunks, disable auto-space + # This allows user to press Tab again after completing a stage name to add "/" and see chunks + # Example: User types "dep" -> Tab -> "deploy" (no space) -> Tab -> "deploy/" -> Tab -> "deploy/0" + if [[ ${#COMPREPLY[@]} -gt 0 ]]; then + local has_multi_chunk=false + local stage + for stage in "${COMPREPLY[@]}"; do + local count + count=$(_count_chunks "$stage" "$files") + [[ $count -gt 1 ]] && { has_multi_chunk=true; break; } + done + [[ "$has_multi_chunk" == true ]] && compopt -o nospace 2>/dev/null fi - - # Handle positional argument completion (files and directories) - _handle_positional_completion "$cur" "$opts" } -# File completion for markdown files -_markdown_runner_file_completion() { - local cur="$1" - - # Use bash's built-in file completion but filter for .md files and directories - # This handles directory traversal automatically - local files=($(compgen -f -- "${cur}")) - local dirs=($(compgen -d -- "${cur}")) - - # Filter files to only include .md files - local md_files=() - for file in "${files[@]}"; do - if _is_markdown_file "$file"; then - md_files+=("$file") +# Complete chunk identifiers for a specific stage +# Provides completions in "stage/chunk" format for all chunks in the stage. +# Args: $1 - stage name +# $2 - partial chunk identifier being typed +# $3 - optional: space-separated list of markdown files (auto-detected if empty) +# Side effects: Sets COMPREPLY array +_complete_chunks() { + local stage="$1" + local chunk_prefix="$2" + local files="$3" + + # If files not provided, get them intelligently + if [[ -z "$files" ]]; then + local target_file + target_file=$(_find_markdown_in_args) + if [[ -n "$target_file" ]]; then + files="$target_file" + else + files=$(_get_executable_files | tr '\n' ' ') fi - done - - # Combine directories (with trailing slash) and .md files - local completions=() - for dir in "${dirs[@]}"; do - completions+=("${dir}/") - done - for file in "${md_files[@]}"; do - completions+=("$file") - done - - COMPREPLY=("${completions[@]}") -} - -# Find markdown files like markdown-runner does -_find_markdown_files() { - local path="$1" - local recursive="$2" - local files=() - - if [[ -f "$path" ]]; then - echo "$path" - return fi - if [[ ! -d "$path" ]]; then - return - fi + local chunks + chunks=$(_get_chunks "$stage" "$files") - # Find files in directory - if [[ "$recursive" == "true" ]]; then - # Recursive search - files=($(find "$path" -name "*.md" -o -name "*.MD" -o -name "*.Markdown" -o -name "*.markdown" 2>/dev/null | sort)) - else - # Non-recursive search - files=($(find "$path" -maxdepth 1 -name "*.md" -o -name "*.MD" -o -name "*.Markdown" -o -name "*.markdown" 2>/dev/null | sort)) - fi + local completions=() + local chunk + while IFS= read -r chunk; do + [[ -n "$chunk" ]] && completions+=("${stage}/${chunk}") + done <<< "$chunks" - printf '%s\n' "${files[@]}" + COMPREPLY=( $(compgen -W "${completions[*]}" -- "${stage}/${chunk_prefix}") ) } -# Get files that would be executed by markdown-runner -_get_executable_files() { - local target_path="" - local recursive=false - - # Check if a specific path is provided in the command line - # But skip arguments that are flag values - for ((i=1; i<${#COMP_WORDS[@]}; i++)); do - local arg="${COMP_WORDS[i]}" - - # Skip flags - if [[ "$arg" == -* ]]; then - _skip_flag_and_value i "$arg" - continue +# Complete file@stage or file@stage/chunk syntax +# Handles completions for the "filename@stage" and "filename@stage/chunk" formats. +# When multiple files have the same basename, chooses the one with the shortest path. +# Args: $1 - current word being completed (contains @) +# Side effects: Sets COMPREPLY array; may disable nospace via compopt +_complete_file_at() { + local cur="$1" + local file_part="${cur%%@*}" # Extract filename before @ + local after_at="${cur#*@}" # Extract everything after @ + + # Smart file resolution: when multiple files have same basename (e.g., tests/test.md and docs/test.md) + # prefer the one with shortest path (closest to project root) as it's more likely to be the intended file + local target_file files candidates=() + files=$(_get_executable_files) + while IFS= read -r f; do + if [[ "$(basename "$f")" == "$file_part" ]]; then + candidates+=("$f") fi + done <<< "$files" - # Skip arguments that contain @ (these are flag values like README.md@stage) - if [[ "$arg" == *@* ]]; then - continue - fi + # Choose file with shortest path (e.g., "test.md" over "subdir/test.md") + if [[ ${#candidates[@]} -gt 0 ]]; then + target_file="${candidates[0]}" + for f in "${candidates[@]}"; do + [[ ${#f} -lt ${#target_file} ]] && target_file="$f" + done + fi - # Skip .md files (these are usually flag values) - if _is_markdown_file "$arg"; then - continue + [[ -z "$target_file" ]] && return + + # Scenario 1: User already has stage and is typing chunk (e.g., "test.md@deploy/chu") + if [[ "$after_at" == */* ]]; then + local stage="${after_at%%/*}" # Extract stage name + local chunk_prefix="${after_at#*/}" # Extract partial chunk ID + local chunks completions=() + chunks=$(_get_chunks "$stage" "$target_file") + while IFS= read -r chunk; do + [[ -n "$chunk" ]] && completions+=("${file_part}@${stage}/${chunk}") + done <<< "$chunks" + COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + else + # Scenario 2 & 3: User is typing stage name after @ (e.g., "test.md@dep" or "test.md@deploy") + # Need to determine if what they've typed is a complete stage name or still partial + local is_complete=false + if [[ -n "$after_at" ]]; then + grep -q '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$after_at"'"' "$target_file" 2>/dev/null && is_complete=true fi - # This should be the target path - only use if it's actually a valid directory or file - if [[ -d "$arg" ]] || [[ -f "$arg" ]]; then - target_path="$arg" - break + if [[ "$is_complete" == true ]]; then + # Scenario 2: Complete stage typed (e.g., "test.md@deploy") + # Show chunks with file@stage/chunk format + local chunks completions=() + chunks=$(_get_chunks "$after_at" "$target_file") + while IFS= read -r chunk; do + [[ -n "$chunk" ]] && completions+=("${file_part}@${after_at}/${chunk}") + done <<< "$chunks" + COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + else + # Scenario 3: Partial stage typed (e.g., "test.md@dep") + # Complete stage names with file@stage format, no auto-space to allow typing more + local stages + stages=$(_get_stages "$target_file") + local completions=() + for stage in $stages; do + completions+=("${file_part}@${stage}") + done + COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + compopt -o nospace 2>/dev/null # No space so user can keep typing or add / fi - done - - # Check for recursive flag - if [[ " ${COMP_WORDS[@]} " =~ " -r " ]] || [[ " ${COMP_WORDS[@]} " =~ " --recursive " ]]; then - recursive=true fi +} - # Default to current directory if no path specified - if [[ -z "$target_path" ]]; then - target_path="." - fi +# Check if we're currently completing a value for a flag +# Returns: 0 if the previous word is a flag that takes a value, 1 otherwise +_is_completing_flag_value() { + [[ $COMP_CWORD -gt 0 ]] && _flag_takes_value "${COMP_WORDS[COMP_CWORD-1]}" +} - _find_markdown_files "$target_path" "$recursive" +# Skip past a flag and its value when iterating through COMP_WORDS +# Args: $1 - name of index variable (passed by reference) +# $2 - flag to check +# Side effects: Increments the index variable if flag takes a value +_skip_flag_and_value() { + local -n idx=$1 + local flag="$2" + _flag_takes_value "$flag" && ((idx++)) } -# Helper function to check if we're completing for a directory context +# Determine if completion should offer file@stage syntax +# In directory context (when a directory is the target or -r is used), completions +# should offer "filename@" syntax to disambiguate files in subdirectories. +# Returns: 0 if in directory context, 1 otherwise _is_directory_context() { # First check if we're completing a flag value if _is_completing_flag_value; then local prev_word="${COMP_WORDS[COMP_CWORD-1]}" # Special cases where flag value completion should be treated as directory context: - if [[ "$prev_word" == "-B" ]] || [[ "$prev_word" == "--break-at" ]] || - [[ "$prev_word" == "-s" ]] || [[ "$prev_word" == "--start-from" ]]; then - # Case 1: Recursive mode - if [[ " ${COMP_WORDS[@]} " =~ " -r " ]] || [[ " ${COMP_WORDS[@]} " =~ " --recursive " ]]; then - return 0 # Recursive mode: treat as directory context - fi + if [[ "$prev_word" == "-B" || "$prev_word" == "--break-at" || "$prev_word" == "-s" || "$prev_word" == "--start-from" ]]; then + # Case 1: Recursive mode + if [[ " ${COMP_WORDS[@]} " =~ " -r " || " ${COMP_WORDS[@]} " =~ " --recursive " ]]; then + return 0 # Recursive mode: treat as directory context + fi - # Case 2: Explicit directory argument present AND we're not completing a complete stage name - local cur="${COMP_WORDS[COMP_CWORD]}" - local is_complete_stage=false + # Case 2: Explicit directory argument present AND we're not completing a complete stage name + local cur="${COMP_WORDS[COMP_CWORD]}" + local is_complete_stage=false - # Check if current word is a complete stage name - if [[ -n "$cur" ]]; then - # Quick check if it looks like a complete stage name (no special chars, reasonable length) - if [[ "$cur" =~ ^[a-zA-Z0-9_-]+$ ]] && _is_valid_stage_name "$cur"; then - is_complete_stage=true - fi - fi + # Check if current word is a complete stage name + if [[ -n "$cur" && "$cur" =~ ^[a-zA-Z0-9_-]+$ ]]; then + _is_valid_stage_name "$cur" && is_complete_stage=true + fi - # Only apply directory context if we're not completing a complete stage name - if [[ "$is_complete_stage" == false ]]; then - for ((i=1; i Tab -> "test.md@setup" (not "test.md@ setup") + [[ "$cur" == *@ ]] && compopt -o nospace 2>/dev/null + return fi -} - -# Completion for --break-at flag -_markdown_runner_break_at_completion() { - local cur="$1" - - # Handle file@stage/chunk format - if [[ "$cur" == *@* ]]; then - local file_part="${cur%@*}" - local debug_part="${cur#*@}" - - if [[ "$debug_part" == */* ]]; then - # file@stage/chunk format - local stage="${debug_part%/*}" - local chunk_prefix="${debug_part##*/}" - _markdown_runner_file_at_chunk_completion "$file_part" "$stage" "$chunk_prefix" - else - # file@stage format - check if stage is complete or needs completion - # First check if this is a complete stage name that should show chunks - local is_complete_stage=false - - # Check if the stage exists in the specified file - local target_file="" - local executable_files - executable_files=$(_get_executable_files) - local candidate_files=() - while IFS= read -r file; do - if [[ -n "$file" ]] && [[ "$(basename "$file")" == "$file_part" ]]; then - candidate_files+=("$file") - fi - done <<< "$executable_files" - - # If multiple files found, prefer the one with the shortest path (closest to root) - if [[ ${#candidate_files[@]} -gt 0 ]]; then - target_file="${candidate_files[0]}" - for file in "${candidate_files[@]}"; do - if [[ ${#file} -lt ${#target_file} ]]; then - target_file="$file" - fi - done - fi - if [[ -n "$target_file" ]]; then - # Check if the debug_part is a complete stage name - local stage_exists=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$debug_part"'"' "$target_file" 2>/dev/null) - if [[ -n "$stage_exists" ]]; then - is_complete_stage=true - fi - fi - - if [[ "$is_complete_stage" == true ]]; then - # Complete stage name, show chunks for this file@stage - _markdown_runner_file_at_chunk_completion "$file_part" "$debug_part" "" - else - # Incomplete stage name, complete stages from that specific file - _markdown_runner_file_at_stage_completion "$file_part" "$debug_part" - fi - fi - elif [[ "$cur" == */* ]]; then - # stage/chunk format (no file specified) - local stage="${cur%/*}" - local chunk_prefix="${cur##*/}" - _markdown_runner_chunk_completion "$stage" "$chunk_prefix" - else - # Check if the current word is a valid stage name - if _is_valid_stage_name "$cur"; then - # Current word is a stage name, show chunks for this stage - _markdown_runner_chunk_completion "$cur" "" - elif _is_directory_context; then - # For directories, only show file@ completions (no individual stage names) - local executable_files - executable_files=$(_get_executable_files) - local file_completions=() - while IFS= read -r file; do - if [[ -n "$file" ]]; then - local basename=$(basename "$file") - file_completions+=("${basename}@") - fi - done <<< "$executable_files" + # stage/chunk format (e.g., "setup/0" or "deploy/init") + if [[ "$cur" == */* ]]; then + # Extract stage and chunk parts using bash parameter expansion + local stage="${cur%%/*}" # Everything before the first / + local chunk_prefix="${cur#*/}" # Everything after the first / + _complete_chunks "$stage" "$chunk_prefix" "" + return + fi - COMPREPLY=( $(compgen -W "${file_completions[*]}" -- "${cur}") ) + # Smart completion when user has typed a complete stage name + if _is_valid_stage "$cur"; then + # Determine which markdown file(s) to search + local target_file files + target_file=$(_find_markdown_in_args) + if [[ -n "$target_file" ]]; then + files="$target_file" # Specific file mentioned in args else - # Check if a specific markdown file is already provided in the command line - local specific_file_provided=false - for arg in "${COMP_WORDS[@]}"; do - if [[ "$arg" == *.md ]] || [[ "$arg" == *.MD ]] || [[ "$arg" == *.Markdown ]] || [[ "$arg" == *.markdown ]]; then - specific_file_provided=true - break - fi - done - - if [[ "$specific_file_provided" == true ]]; then - # Specific file provided, only show stage names from that file - _markdown_runner_stage_completion "$cur" - else - # No specific file, show only stage names (no file@ completions in non-directory context) - _markdown_runner_stage_completion "$cur" - fi + files=$(_get_executable_files | tr '\n' ' ') # All applicable files fi - fi -} -# Check if a given word is a valid stage name -_is_valid_stage_name() { - local stage_name="$1" - local md_files - - # Get files that would actually be executed by markdown-runner - md_files=$(_get_executable_files | tr '\n' ' ') + local chunk_count + chunk_count=$(_count_chunks "$cur" "$files") - if [[ -n "$md_files" ]]; then - # Check if the stage exists in any of the files - local stage_exists=$(echo "$md_files" | xargs grep -l '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage_name"'"' 2>/dev/null) - if [[ -n "$stage_exists" ]]; then - return 0 # Stage exists + if [[ $chunk_count -gt 1 ]]; then + # Smart UX: Multi-chunk stage gets "/" appended with nospace + # This guides the user to continue typing the chunk ID + # Example: "setup" -> Tab -> "setup/" (ready for chunk selection) + COMPREPLY=("${cur}/") + compopt -o nospace 2>/dev/null + else + # Single-chunk stage: show the chunk ID directly (usually "0" or named ID) + _complete_chunks "$cur" "" "" fi - fi - return 1 # Stage doesn't exist -} - -# Get stages from markdown files -_markdown_runner_stage_completion() { - local cur="$1" - local stages - - # Check if a specific markdown file is provided in the command line - local target_file - target_file=$(_find_markdown_file_in_args) - - local md_files - if [[ -n "$target_file" ]]; then - # Use only the specified file - md_files="$target_file" - else - # Get files that would actually be executed by markdown-runner - md_files=$(_get_executable_files | tr '\n' ' ') + return fi - # Extract stages from markdown files - if [[ -n "$md_files" ]]; then - stages=$(echo "$md_files" | xargs grep -h '```[a-zA-Z0-9_. -]*{.*"stage"' 2>/dev/null | \ - sed -n 's/.*"stage"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | \ - sort -u) + # Directory context: offer file@stage syntax for disambiguation + # When -r is used or a directory is specified, multiple files may have same stages + # so we offer "filename@" completions to let user specify which file + if _is_directory_context; then + local files completions=() + files=$(_get_executable_files) + while IFS= read -r f; do + # Add @ suffix to each basename to indicate file@stage format is available + [[ -n "$f" ]] && completions+=("$(basename "$f")@") + done <<< "$files" + COMPREPLY=( $(compgen -W "${completions[*]}" -- "$cur") ) + # Disable space after @ so user can immediately type stage name + compopt -o nospace 2>/dev/null + return fi - COMPREPLY=( $(compgen -W "${stages}" -- ${cur}) ) - - # Smart nospace behavior: only disable space suffix for stages with multiple chunks - if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - local has_multi_chunk_stage=false - - # Check if any of the completed stages has multiple chunks - for stage in "${COMPREPLY[@]}"; do - # Get chunk count for this stage - local chunk_count=0 - if [[ -n "$md_files" ]]; then - chunk_count=$(echo "$md_files" | xargs grep -h "^\`\`\`[a-zA-Z0-9_. -]*{.*\"stage\"[[:space:]]*:[[:space:]]*\"$stage\"" 2>/dev/null | wc -l) - fi - - if [[ $chunk_count -gt 1 ]]; then - has_multi_chunk_stage=true - break - fi - done - - # Only use nospace if there are stages with multiple chunks - if [[ "$has_multi_chunk_stage" == true ]]; then - compopt -o nospace 2>/dev/null || true - fi - fi + # Default: show stages + _complete_stages "$cur" "" } -# Get chunk IDs and indices for a specific stage -_markdown_runner_chunk_completion() { - local stage="$1" - local chunk_prefix="$2" - local chunks="" - - # Check if a specific markdown file is provided in the command line - local target_file - target_file=$(_find_markdown_file_in_args) - - local md_files - if [[ -n "$target_file" ]]; then - # Use only the specified file - md_files="$target_file" - else - # Get files that would actually be executed by markdown-runner - md_files=$(_get_executable_files | tr '\n' ' ') - fi +# Complete markdown files and directories +# Provides file/directory completions, with directories having trailing slashes. +# Args: $1 - current word being completed +# Side effects: Sets COMPREPLY array; disables nospace for directories +_complete_files() { + local cur="$1" - if [[ -n "$md_files" ]]; then - # Extract all chunks for the specific stage from all files - local all_chunks=$(echo "$md_files" | xargs grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' 2>/dev/null) - - # Process chunks and collect IDs and indices - local index=0 - local chunk_list="" - while IFS= read -r line; do - if [[ -n "$line" ]]; then - # Extract ID if present - local chunk_id=$(echo "$line" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') - if [[ -n "$chunk_id" ]]; then - chunk_list="$chunk_list $chunk_id" - else - chunk_list="$chunk_list $index" - fi - ((index++)) - fi - done <<< "$all_chunks" + local files dirs md_files=() completions=() + files=($(compgen -f -- "$cur")) + dirs=($(compgen -d -- "$cur")) - chunks="$chunk_list" - fi + # Filter to .md files + for file in "${files[@]}"; do + _is_markdown_file "$file" && md_files+=("$file") + done - # Format completions as stage/chunk and remove duplicates - local completions="" - for chunk in $chunks; do - completions="$completions ${stage}/${chunk}" + # Add directories with trailing slash + for dir in "${dirs[@]}"; do + completions+=("${dir}/") done - # Remove duplicates by converting to array and back - local unique_completions=($(echo "$completions" | tr ' ' '\n' | sort -u | tr '\n' ' ')) + completions+=("${md_files[@]}") + COMPREPLY=("${completions[@]}") - COMPREPLY=( $(compgen -W "${unique_completions[*]}" -- "${stage}/${chunk_prefix}") ) + # Disable space for directories + [[ ${#dirs[@]} -gt 0 ]] && compopt -o nospace 2>/dev/null } -# File-specific stage completion for file@stage format -_markdown_runner_file_at_stage_completion() { - local file_part="$1" - local stage_prefix="$2" - local stages - - # Find the actual file path - prefer files closer to root when multiple matches exist - local target_file="" - local executable_files - executable_files=$(_get_executable_files) - local candidate_files=() - while IFS= read -r file; do - if [[ -n "$file" ]] && [[ "$(basename "$file")" == "$file_part" ]]; then - candidate_files+=("$file") - fi - done <<< "$executable_files" - - # If multiple files found, prefer the one with the shortest path (closest to root) - if [[ ${#candidate_files[@]} -gt 0 ]]; then - target_file="${candidate_files[0]}" - for file in "${candidate_files[@]}"; do - if [[ ${#file} -lt ${#target_file} ]]; then - target_file="$file" - fi - done - fi - - if [[ -n "$target_file" ]]; then - # Extract stages from the specific file - stages=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"' "$target_file" 2>/dev/null | \ - sed -n 's/.*"stage"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | \ - sort -u) - - # Format completions as file@stage - local completions=() - for stage in $stages; do - completions+=("${file_part}@${stage}") - done +# ============================================================================ +# Main Completion Handler +# ============================================================================ + +# Main entry point for markdown-runner bash completion +# Called by bash whenever user presses Tab after typing "markdown-runner". +# Orchestrates all completion logic: flag values, flags, help, and file/directory completions. +# Uses bash built-in variables: +# - COMP_WORDS: array of all words on the command line +# - COMP_CWORD: index of the word being completed +# - COMPREPLY: array where completion results are stored +_markdown_runner_completion() { + local cur prev + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + prev="${COMP_WORDS[COMP_CWORD-1]}" - COMPREPLY=( $(compgen -W "${completions[*]}" -- "${file_part}@${stage_prefix}") ) + # Handle flag value completion + case "$prev" in + -B|--break-at) + _complete_break_or_start "$cur" true + [[ "$cur" == *@ ]] && compopt -o nospace 2>/dev/null + return + ;; + -s|--start-from) + _complete_break_or_start "$cur" false + [[ "$cur" == *@ ]] && compopt -o nospace 2>/dev/null + return + ;; + -t|--timeout) + COMPREPLY=( $(compgen -W "1 5 10 30 60" -- "$cur") ) + return + ;; + --view) + COMPREPLY=( $(compgen -W "default ci" -- "$cur") ) + return + ;; + -f|--filter) + return # No completion for regex + ;; + esac - # Disable space suffix for file@stage completions since they can be chained with /chunk - if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - compopt -o nospace 2>/dev/null || true + # Handle flag completion + if [[ "$cur" == -* ]]; then + # Enhanced help for bare "-" + if [[ "$cur" == "-" ]]; then + local available + available=$(_get_available_flags) + # Create combined short/long format with descriptions + local items=() + while IFS='|' read -r short long desc; do + [[ -z "$short" && -z "$long" ]] && continue + # Check if both forms are available + if [[ -n "$short" ]] && [[ -n "$long" ]]; then + if [[ " $available " == *" $short "* ]] && [[ " $available " == *" $long "* ]]; then + items+=("$short, $long:$desc") + elif [[ " $available " == *" $short "* ]]; then + items+=("$short:$desc") + elif [[ " $available " == *" $long "* ]]; then + items+=("$long:$desc") + fi + elif [[ -n "$long" ]] && [[ " $available " == *" $long "* ]]; then + items+=("$long:$desc") + fi + done </dev/null + else + # Standard flag completion + local available + available=$(_get_available_flags) + COMPREPLY=( $(compgen -W "$available" -- "$cur") ) fi + return fi -} -# File-specific chunk completion for file@stage/chunk format -_markdown_runner_file_at_chunk_completion() { - local file_part="$1" - local stage="$2" - local chunk_prefix="$3" - - # Find the actual file path - prefer files closer to root when multiple matches exist - local target_file="" - local executable_files - executable_files=$(_get_executable_files) - local candidate_files=() - while IFS= read -r file; do - if [[ -n "$file" ]] && [[ "$(basename "$file")" == "$file_part" ]]; then - candidate_files+=("$file") - fi - done <<< "$executable_files" - - # If multiple files found, prefer the one with the shortest path (closest to root) - if [[ ${#candidate_files[@]} -gt 0 ]]; then - target_file="${candidate_files[0]}" - for file in "${candidate_files[@]}"; do - if [[ ${#file} -lt ${#target_file} ]]; then - target_file="$file" - fi - done + # Handle help command - show full categorized help + if [[ "$cur" == "help" || "$cur" == "hel" || "$cur" == "he" ]]; then + COMPREPLY=( + "Modes:" + " -d, --dry-run Just list what would be executed without doing it" + " -l, --list Just list the files found" + "" + "Execution Control:" + " -i, --interactive Prompt to press enter between each chunk" + " -s, --start-from string Start from a specific stage (stage or file@stage)" + " -B, --break-at string Start debugging from a specific stage or chunk" + " -t, --timeout int The timeout in minutes for every executed command" + " -u, --update-files Update the chunk output section in the markdown files" + " --ignore-breakpoints Ignore the breakpoints" + "" + "File Selection:" + " -f, --filter string Run only the files matching the regex" + " -r, --recursive Search for markdown files recursively" + "" + "Output & Logging:" + " --view string UI to be used, can be 'default' or 'ci'" + " -v, --verbose Print more logs" + " -q, --quiet Disable output" + " --no-styling Disable spinners in CLI" + "" + "Help:" + " -h, --help Show this help message" + ) + compopt -o nospace 2>/dev/null + return fi - if [[ -n "$target_file" ]]; then - # Extract chunks for the specific stage from the specific file - local all_chunks=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' "$target_file" 2>/dev/null) - - # Process chunks and collect IDs and indices - local index=0 - local chunk_list="" - while IFS= read -r line; do - if [[ -n "$line" ]]; then - # Extract ID if present - local chunk_id=$(echo "$line" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') - if [[ -n "$chunk_id" ]]; then - chunk_list="$chunk_list $chunk_id" - else - chunk_list="$chunk_list $index" - fi - ((index++)) - fi - done <<< "$all_chunks" - - # Format completions as file@stage/chunk - local completions=() - for chunk in $chunk_list; do - completions+=("${file_part}@${stage}/${chunk}") - done + # Check if file/dir already provided + local has_file=false + local i + for ((i=1; i` shows chunks (`help/0`) -- Verifies that `markdown-runner -B setup` shows chunks (`setup/init setup/1`) -- Confirms that partial matches work (`hel` → `help`) - -#### `test_flag_completions.sh` -Comprehensive test suite for flag completions: -- Timeout flag completion (`-t` → `1 5 10 30 60`) -- View flag completion (`--view` → `default ci`) -- Filter flag completion (should show no completions) -- Flag name completion (`-` → all flags, `--` → long flags) -- Flag equivalence (short vs long forms) -- Invalid flag handling - -#### `test_flag_filtering.sh` -Test suite for flag filtering (excluding already-used flags): -- Single flag exclusion (`markdown-runner -r file.md -` excludes `-r` and `--recursive`) -- Multiple flag exclusion (multiple used flags excluded) -- Equivalent flag exclusion (short form excludes long form and vice versa) -- Flags with values properly excluded -- Edge cases (different flag positions, partial completions) -- Comprehensive flag pair testing - -#### `test_incompatible_flags.sh` -Test suite for incompatible flag filtering (excluding logically incompatible flags): -- **Unidirectional**: CI mode (`--view ci`) excludes interactive flags (`-i`, `-B`, `-s`) -- **Bidirectional**: Interactive flags (`-i`, `-B`, `-s`) exclude CI mode (`--view`) -- List mode (`-l`) excludes execution flags (`-d`, `-t`, `-u`, `-i`, `-B`, `-s`) -- Interactive flags exclude list mode (`-l`) -- Help mode (`-h`) excludes all other flags -- Dry run (`-d`) excludes file modification flags (`-u`) -- Update-files (`-u`) excludes dry run (`-d`) -- Break-at (`-B`) excludes ignore-breakpoints (`--ignore-breakpoints`) -- Ignore-breakpoints (`--ignore-breakpoints`) excludes break-at (`-B`) -- Verbose (`-v`) and quiet (`-q`) are mutually exclusive -- Complex combinations and edge cases - -#### `test_enhanced_completion.sh` -Test suite for enhanced completion features (categorized help and descriptions): -- **Enhanced help**: `markdown-runner -` shows flags with short descriptions -- **Full categorized help**: `markdown-runner help` shows complete categorized format -- **Standard compatibility**: Partial flags (`-d`) work normally without descriptions -- **Incompatible filtering**: Enhanced help respects incompatible flag filtering -- **Context awareness**: File/value completion still works when appropriate -- **Edge cases**: Enhanced features work with existing arguments and flags - -#### `test_main_file_completion.sh` -Test suite for main file argument completion: -- Basic file completion (`.md` files and directories) -- Directory traversal (`path/`) -- File filtering (excludes non-`.md` files) -- Nospace behavior for directories -- Path traversal and nested directories -- Edge cases (non-existent paths, case sensitivity) - -#### `test_positional_arguments.sh` -Test suite for positional argument completion logic: -- No file provided shows file completions (`markdown-runner `) -- Markdown file provided switches to flag completions (`markdown-runner file.md `) -- Directory provided switches to flag completions (`markdown-runner test/cases/ `) -- File + flag combinations work correctly -- Flag value completion unaffected by file presence -- Edge cases (non-existent files, different extensions, nested directories) - -#### `test_start_from_completion.sh` -Test suite for `-s/--start-from` flag completion: -- Basic start-from completion (stage names, file@stage) -- Directory context behavior -- Equivalence with `-B/--break-at` flag -- Specific file context scenarios -- Complex multi-flag scenarios -- Edge case handling - -#### `test_completion.sh` -Original comprehensive test suite that covers all completion scenarios: -- Basic stage completion (`markdown-runner -B README.md@`) -- Partial stage completion (`markdown-runner -B README.md@test`) -- Chunk completion (`markdown-runner -B README.md@setup/`) -- Stage completion without file prefix (`markdown-runner -B setup`) -- Chunk completion without file prefix (`markdown-runner -B setup/`) -- Internal function testing (`_get_executable_files`) - -#### `test_stage_chunk_completion.sh` -Focused test suite for stage-to-chunk completion functionality: -- Exact stage names showing chunks (`help` → `help/0`) -- Partial stage names showing matching stages (`hel` → `help`) -- Verification that existing formats still work - -#### `test_file_at_completion.sh` -Test suite for file@stage completion functionality: -- `README.md@` shows all stages from the main README.md file -- Correct file selection when multiple files with same name exist -- File@stage/chunk format completion -- Partial file@stage completion -- Proper handling of files not in current directory - -#### `test_smart_nospace.sh` -Test suite for smart nospace behavior: -- Single-chunk stages allow trailing spaces (complete workflow) -- Multi-chunk stages use nospace (enable chaining) -- Mixed completions use nospace if any stage has multiple chunks -- Chunk counting logic verification -- Smart behavior based on actual chunk analysis - -#### `test_file_at_stage_logic.sh` -Test suite for file@stage complete vs partial logic: -- Complete stage names show chunks (e.g., `README.md@inner_test1` → chunks) -- Partial stage names complete to stage names (e.g., `README.md@inner` → `README.md@inner_test1`) -- Non-existent stages show no completions -- File@stage/chunk format works correctly -- Recursive vs non-recursive consistency - -#### `test_directory_partial_completion.sh` -Test suite for directory partial completion fix: -- Directory empty completion shows file@ completions (`test/cases/ -B `) -- Directory partial completion shows filtered file@ completions (`test/cases/ -B p` → `parallel.md@`) -- Complete stage names in directory context show chunks -- Multi-character partial matches work correctly -- Non-directory and recursive contexts remain unaffected - -#### `test_recursive_behavior.sh` -Test suite for recursive vs non-recursive completion behavior: -- Non-recursive mode shows stage names + file@ completions -- Recursive mode shows ONLY file@ completions -- Partial matching behavior in both modes -- Support for both `-r` and `--recursive` flags -- Recursive behavior with different completion flags (`-B`, `-s`) - -#### `test_file_discovery.sh` -Test suite for the `_get_executable_files` function: -- File discovery in different completion contexts -- Handling of flag values vs directory arguments -- Edge cases with multiple flags and values - -#### `test_stage_validation.sh` -Test suite for the `_is_valid_stage_name` function: -- Valid stage name detection -- Invalid stage name rejection -- Edge cases with partial matches and empty strings - -#### `test_directory_context.sh` -Test suite for the `_is_directory_context` function: -- Flag value contexts (should not be directory contexts) -- Actual directory arguments (should be directory contexts) -- Mixed scenarios with flags and paths - -### Debug Scripts - -#### `debug_get_executable_files.sh` -Debug script for the `_get_executable_files` function. Helps troubleshoot: -- File discovery logic -- Argument parsing from `COMP_WORDS` -- Path resolution -- Recursive flag handling - -#### `debug_stage_extraction.sh` -Debug script for stage extraction from markdown files. Helps troubleshoot: -- Stage discovery from markdown files -- Regex patterns for stage extraction -- File@stage completion logic -- Stage counting and sorting - -#### `debug_chunk_completion.sh` -Debug script for chunk completion. Helps troubleshoot: -- Chunk discovery within stages -- ID extraction vs index assignment -- File@stage/chunk completion logic -- Chunk list building - -## Running Tests - -### Run All Tests ```bash -cd completion/tests +# Run all tests ./run_all_tests.sh ``` -### Run Individual Test Suites +**Requirements:** `jq` (JSON processor) ```bash -cd completion/tests -./test_completion.sh # Original comprehensive tests -./test_stage_chunk_completion.sh # Stage-to-chunk completion tests -./test_file_discovery.sh # File discovery tests -./test_stage_validation.sh # Stage validation tests -./test_directory_context.sh # Directory context tests +sudo dnf install jq # Fedora/RHEL +sudo apt install jq # Debian/Ubuntu +brew install jq # macOS ``` -### Run Individual Debug Scripts -```bash -cd completion/tests -./debug_get_executable_files.sh -./debug_stage_extraction.sh -./debug_chunk_completion.sh -``` +## Architecture -## Test Output +### Files -The main test suite provides colored output: -- 🟢 **Green**: Passed tests -- 🔴 **Red**: Failed tests -- 🟡 **Yellow**: Test names and descriptions +- **`test_data.json`** - All test cases in JSON format (~170 lines) +- **`test_framework.sh`** - Test engine and assertion functions (~320 lines) +- **`run_all_tests.sh`** - Main entry point (~50 lines) -Example output: -``` -=== Markdown Runner Bash Completion Test Suite === +**Total: ~540 lines** (previously ~4,700 lines - **89% reduction!**) -Testing: Basic stage completion - Command: markdown-runner -B README.md@ - Found 13 completions - PASS +### Test Data Format -Testing: Partial stage completion - Command: markdown-runner -B README.md@test - Found 6 completions - PASS +Each test is a JSON object: -... - -=== Test Results === -Tests run: 6 -Tests passed: 6 -Tests failed: 0 -All tests passed! +```json +{ + "name": "test description", + "comp_words": ["markdown-runner", "-t", "1"], + "assertion": "exact", + "expected": ["1", "10"] +} ``` -## Adding New Tests - -To add a new test to the main test suite: - -1. Use the `run_completion_test` helper function: - ```bash - run_completion_test \ - "Test description" \ - "command line to test" \ - "regex pattern for validation" \ - "specific completion that should be present" - ``` - -2. For debugging specific functionality, create a new debug script following the pattern of existing ones. - -## Troubleshooting - -If tests fail: - -1. **Check file discovery**: Run `debug_get_executable_files.sh` to ensure markdown files are being found correctly. - -2. **Check stage extraction**: Run `debug_stage_extraction.sh` to verify stages are being extracted from markdown files. +### Assertions + +| Type | Description | Expected Type | +|------|-------------|---------------| +| `exact` | Exact array match (order matters) | array | +| `contains` | Contains all specified items | array | +| `excludes` | Excludes all specified items | array | +| `count_gt` | Count > N | number | +| `count_eq` | Count == N | number | +| `all_match` | All completions match pattern | array with pattern | +| `any_match` | At least one matches pattern | array with pattern | + +## Test Suites + +Current test suites (auto-discovered from JSON keys): + +- **`filter_tests`** - Filter flag behavior (no completion for `-f`) +- **`flag_equiv_tests`** - Flag equivalents (short/long forms) +- **`incompatible_flag_tests`** - Mutually exclusive flags +- **`positional_tests`** - Positional argument completion +- **`recursive_tests`** - Recursive flag behavior +- **`stage_completion_tests`** - Stage/chunk completion +- **`timeout_tests`** - Timeout value completion +- **`view_tests`** - View mode completion + +## Adding Tests + +Simply add a new object to the appropriate suite in `test_data.json`: + +```json +{ + "timeout_tests": [ + { + "name": "new timeout test", + "comp_words": ["markdown-runner", "-t", ""], + "assertion": "exact", + "expected": ["1", "5", "10", "30", "60"] + } + ] +} +``` -3. **Check chunk completion**: Run `debug_chunk_completion.sh` to debug chunk-specific completion issues. +Or create a new test suite: + +```json +{ + "my_new_tests": [ + { + "name": "first test", + "comp_words": ["markdown-runner", "-d", "-"], + "assertion": "contains", + "expected": ["-l", "--list"] + } + ] +} +``` -4. **Verify completion script**: Ensure the completion script is sourced correctly and all functions are available. +## Example Test Output -## Integration with CI +``` +╔════════════════════════════════════════════════════════════════╗ +║ Bash Completion Test Suite ║ +╚════════════════════════════════════════════════════════════════╝ + +=== Timeout Tests === +✓ timeout shows all values +✓ timeout filters '1' prefix +✓ timeout filters '3' prefix +✓ timeout filters '5' prefix +✓ timeout filters '6' prefix + +=== View Tests === +✓ view mode shows options +✓ view mode filters 'c' +✓ view mode filters 'd' -These tests can be integrated into CI/CD pipelines to ensure completion functionality doesn't regress: +... -```bash -# In your CI script -cd completion/tests -./test_completion.sh +============================================ +Test Results: + Total: 31 + Passed: 31 + Failed: 0 +============================================ ``` - -The test script exits with code 0 on success and 1 on failure, making it suitable for automated testing. diff --git a/completion/tests/debug_chunk_completion.sh b/completion/tests/debug_chunk_completion.sh deleted file mode 100755 index b7a0ce5..0000000 --- a/completion/tests/debug_chunk_completion.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/bin/bash - -# Debug script for chunk completion -# This script helps debug issues with chunk discovery in completion - -source "$(dirname "$0")/../bash_completion.sh" - -echo "=== Debug: Chunk completion ===" -echo - -# Test chunk completion for setup stage -echo "=== Testing chunk completion for setup stage ===" -stage="setup" -target_file="README.md" - -if [[ -f "$target_file" ]]; then - echo "File exists: $target_file" - - echo - echo "=== Raw grep output for stage '$stage' ===" - grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' "$target_file" 2>/dev/null - - echo - echo "=== Processing chunks ===" - all_chunks=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' "$target_file" 2>/dev/null) - - index=0 - chunk_list="" - while IFS= read -r line; do - if [[ -n "$line" ]]; then - echo "Processing line: $line" - - # Extract ID if present - chunk_id=$(echo "$line" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') - if [[ -n "$chunk_id" ]]; then - chunk_list="$chunk_list $chunk_id" - echo " Found chunk ID: $chunk_id" - else - chunk_list="$chunk_list $index" - echo " Using index: $index" - fi - ((index++)) - fi - done <<< "$all_chunks" - - echo - echo "=== Final chunk list ===" - echo "Chunks: $chunk_list" - - echo - echo "=== Building completions ===" - completions=() - for chunk in $chunk_list; do - completions+=("${stage}/${chunk}") - echo "Added completion: ${stage}/${chunk}" - done - - echo - echo "=== Final completions ===" - printf '%s\n' "${completions[@]}" - -else - echo "$target_file not found!" -fi - -echo -echo "=== Testing file@stage/chunk completion ===" -file_part="README.md" -stage="setup" -chunk_prefix="" - -echo "Testing: ${file_part}@${stage}/" - -# Find the actual file path -target_file="" -executable_files=$(_get_executable_files) -while IFS= read -r file; do - if [[ -n "$file" ]] && [[ "$(basename "$file")" == "$file_part" ]]; then - target_file="$file" - break - fi -done <<< "$executable_files" - -if [[ -n "$target_file" ]]; then - echo "Found target file: $target_file" - - # Extract chunks for the specific stage from the specific file - all_chunks=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' "$target_file" 2>/dev/null) - - # Process chunks and collect IDs and indices - index=0 - chunk_list="" - while IFS= read -r line; do - if [[ -n "$line" ]]; then - # Extract ID if present - chunk_id=$(echo "$line" | sed -n 's/.*"id"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p') - if [[ -n "$chunk_id" ]]; then - chunk_list="$chunk_list $chunk_id" - else - chunk_list="$chunk_list $index" - fi - ((index++)) - fi - done <<< "$all_chunks" - - # Format completions as file@stage/chunk - completions=() - for chunk in $chunk_list; do - completions+=("${file_part}@${stage}/${chunk}") - done - - echo "File@stage/chunk completions:" - printf '%s\n' "${completions[@]}" -fi diff --git a/completion/tests/debug_get_executable_files.sh b/completion/tests/debug_get_executable_files.sh deleted file mode 100755 index c533bf5..0000000 --- a/completion/tests/debug_get_executable_files.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/bash - -# Debug script for _get_executable_files function -# This script helps debug issues with file discovery in completion - -source "$(dirname "$0")/../bash_completion.sh" - -echo "=== Debug: _get_executable_files function ===" -echo - -# Test case 1: Normal completion scenario -echo "Test 1: Normal completion with -B README.md@" -COMP_WORDS=("markdown-runner" "-B" "README.md@") -COMP_CWORD=2 - -echo "COMP_WORDS: ${COMP_WORDS[@]}" - -echo "=== Checking arguments in COMP_WORDS (with @ filter) ===" -for arg in "${COMP_WORDS[@]}"; do - echo "Arg: '$arg'" - if [[ "$arg" != -* ]] && [[ "$arg" != "markdown-runner" ]] && [[ "$arg" != *.md ]] && [[ "$arg" != *@* ]]; then - echo " -> Would be selected as target_path" - else - echo " -> Skipped (is flag, is markdown-runner, is *.md, or contains @)" - fi -done - -echo -echo "=== Calling _get_executable_files ===" -files=$(_get_executable_files) -echo "Files found: '$files'" - -echo -echo "=== Manual test of _find_markdown_files ===" -manual_files=$(_find_markdown_files "." "false") -echo "Manual files: '$manual_files'" - -echo -echo "Test 2: With directory argument" -COMP_WORDS=("markdown-runner" "-B" "test/" "README.md@") -echo "COMP_WORDS: ${COMP_WORDS[@]}" -files2=$(_get_executable_files) -echo "Files found: '$files2'" - -echo -echo "Test 3: With recursive flag" -COMP_WORDS=("markdown-runner" "-r" "-B" "README.md@") -echo "COMP_WORDS: ${COMP_WORDS[@]}" -files3=$(_get_executable_files) -echo "Files found: '$files3'" diff --git a/completion/tests/debug_stage_extraction.sh b/completion/tests/debug_stage_extraction.sh deleted file mode 100755 index a7246fc..0000000 --- a/completion/tests/debug_stage_extraction.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/bash - -# Debug script for stage extraction from markdown files -# This script helps debug issues with stage discovery in completion - -source "$(dirname "$0")/../bash_completion.sh" - -echo "=== Debug: Stage extraction from markdown files ===" -echo - -# Test stage extraction from README.md -echo "=== Testing stage extraction from README.md ===" -if [[ -f "README.md" ]]; then - echo "File exists: README.md" - - echo - echo "=== Raw grep output ===" - grep -h '```[a-zA-Z0-9_. -]*{.*"stage"' "README.md" 2>/dev/null | head -10 - - echo - echo "=== Extracted stages ===" - stages=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"' "README.md" 2>/dev/null | \ - sed -n 's/.*"stage"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | \ - sort -u) - echo "$stages" - - echo - echo "=== Stage count ===" - stage_count=$(echo "$stages" | wc -l) - echo "Found $stage_count stages" - -else - echo "README.md not found!" -fi - -echo -echo "=== Testing file@stage completion logic ===" -file_part="README.md" -stage_prefix="" - -# Find the actual file path -target_file="" -executable_files=$(_get_executable_files) -echo "Executable files: $executable_files" - -while IFS= read -r file; do - echo "Checking file: '$file' vs basename: '$(basename "$file")'" - if [[ -n "$file" ]] && [[ "$(basename "$file")" == "$file_part" ]]; then - target_file="$file" - echo "Found target file: '$target_file'" - break - fi -done <<< "$executable_files" - -if [[ -n "$target_file" ]]; then - echo - echo "=== Building completions for $target_file ===" - stages=$(grep -h '```[a-zA-Z0-9_. -]*{.*"stage"' "$target_file" 2>/dev/null | \ - sed -n 's/.*"stage"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | \ - sort -u) - - completions=() - for stage in $stages; do - completions+=("${file_part}@${stage}") - echo "Added completion: ${file_part}@${stage}" - done - - echo - echo "=== Final completions ===" - printf '%s\n' "${completions[@]}" -else - echo "No target file found!" -fi diff --git a/completion/tests/run_all_tests.sh b/completion/tests/run_all_tests.sh index 1f8b833..b710fff 100755 --- a/completion/tests/run_all_tests.sh +++ b/completion/tests/run_all_tests.sh @@ -1,127 +1,50 @@ #!/bin/bash -# Master test runner for all completion tests -# Runs all test suites and provides a summary - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -NC='\033[0m' # No Color +# Main Test Runner for Bash Completion Tests +# Runs all declarative tests from test_data.json + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Change to repository root so completion can find markdown files +# The completion script looks for .md files in the current directory +cd "$SCRIPT_DIR/../.." + +# Check if jq is available +if ! command -v jq &> /dev/null; then + echo "Error: jq is required but not installed" + echo "Install with:" + echo " Fedora/RHEL: sudo dnf install jq" + echo " Debian/Ubuntu: sudo apt-get install jq" + echo " macOS: brew install jq" + exit 1 +fi -# Test suite counters -SUITES_RUN=0 -SUITES_PASSED=0 -SUITES_FAILED=0 +# Source the test framework +source "$SCRIPT_DIR/test_framework.sh" -# Overall test counters -TOTAL_TESTS=0 -TOTAL_PASSED=0 -TOTAL_FAILED=0 +# Path to test data +TEST_DATA="$SCRIPT_DIR/test_data.json" -# Function to run a test suite -run_test_suite() { - local suite_name="$1" - local script_path="$2" - - echo -e "${BLUE}=== Running $suite_name ===${NC}" - SUITES_RUN=$((SUITES_RUN + 1)) - - if [[ ! -f "$script_path" ]]; then - echo -e "${RED}ERROR: Test script not found: $script_path${NC}" - SUITES_FAILED=$((SUITES_FAILED + 1)) - return 1 - fi - - if [[ ! -x "$script_path" ]]; then - chmod +x "$script_path" - fi - - # Run the test suite and capture output - local output - local exit_code - output=$("$script_path" 2>&1) - exit_code=$? - - # Extract test counts from output - local tests_run=$(echo "$output" | grep "Tests run:" | sed 's/Tests run: //') - local tests_passed=$(echo "$output" | grep "Tests passed:" | sed 's/.*Tests passed: [^0-9]*\([0-9]*\).*/\1/') - local tests_failed=$(echo "$output" | grep "Tests failed:" | sed 's/.*Tests failed: [^0-9]*\([0-9]*\).*/\1/') - - # Update totals - if [[ -n "$tests_run" ]]; then - TOTAL_TESTS=$((TOTAL_TESTS + tests_run)) - fi - if [[ -n "$tests_passed" ]]; then - TOTAL_PASSED=$((TOTAL_PASSED + tests_passed)) - fi - if [[ -n "$tests_failed" ]]; then - TOTAL_FAILED=$((TOTAL_FAILED + tests_failed)) - fi - - # Show results - if [[ $exit_code -eq 0 ]]; then - echo -e "${GREEN}✓ $suite_name PASSED${NC} ($tests_passed/$tests_run tests)" - SUITES_PASSED=$((SUITES_PASSED + 1)) - else - echo -e "${RED}✗ $suite_name FAILED${NC} ($tests_failed/$tests_run tests failed)" - SUITES_FAILED=$((SUITES_FAILED + 1)) - - # Show detailed output for failed suites - echo -e "${YELLOW}Detailed output:${NC}" - echo "$output" | sed 's/^/ /' - fi - echo -} +# Check if test data exists +if [[ ! -f "$TEST_DATA" ]]; then + echo "Error: Test data file not found: $TEST_DATA" + exit 1 +fi -echo -e "${BLUE}=== Markdown Runner Completion Test Suite ===${NC}" -echo "Running all completion tests..." -echo +# Banner +echo "╔════════════════════════════════════════════════════════════════╗" +echo "║ Bash Completion Test Suite ║" +echo "╚════════════════════════════════════════════════════════════════╝" -# Get the directory of this script -SCRIPT_DIR="$(dirname "$0")" +# Run all test suites (automatically discover from JSON keys) +test_suites=$(jq -r 'keys[] | select(endswith("_tests"))' "$TEST_DATA" 2>/dev/null) -# Run all test suites -run_test_suite "Integration Test" "$SCRIPT_DIR/test_integration.sh" -run_test_suite "Flag Completions" "$SCRIPT_DIR/test_flag_completions.sh" -run_test_suite "Flag Filtering" "$SCRIPT_DIR/test_flag_filtering.sh" -run_test_suite "Incompatible Flags" "$SCRIPT_DIR/test_incompatible_flags.sh" -run_test_suite "Enhanced Completion" "$SCRIPT_DIR/test_enhanced_completion.sh" -run_test_suite "Main File Completion" "$SCRIPT_DIR/test_main_file_completion.sh" -run_test_suite "Positional Arguments" "$SCRIPT_DIR/test_positional_arguments.sh" -run_test_suite "Start-From Completion" "$SCRIPT_DIR/test_start_from_completion.sh" -run_test_suite "Stage Chunk Completion" "$SCRIPT_DIR/test_stage_chunk_completion.sh" -run_test_suite "Specific File Completion" "$SCRIPT_DIR/test_specific_file_completion.sh" -run_test_suite "File@Stage Completion" "$SCRIPT_DIR/test_file_at_completion.sh" -run_test_suite "Smart Nospace Behavior" "$SCRIPT_DIR/test_smart_nospace.sh" -run_test_suite "File@Stage Logic" "$SCRIPT_DIR/test_file_at_stage_logic.sh" -run_test_suite "Directory Partial Completion" "$SCRIPT_DIR/test_directory_partial_completion.sh" -run_test_suite "Recursive Behavior" "$SCRIPT_DIR/test_recursive_behavior.sh" -run_test_suite "File Discovery" "$SCRIPT_DIR/test_file_discovery.sh" -run_test_suite "Stage Validation" "$SCRIPT_DIR/test_stage_validation.sh" -run_test_suite "Directory Context" "$SCRIPT_DIR/test_directory_context.sh" -# NOTE: Skipping original test_completion.sh due to hanging issues -# run_test_suite "Completion Tests" "$SCRIPT_DIR/test_completion.sh" +while IFS= read -r suite; do + run_test_suite_json "$suite" "$TEST_DATA" +done <<< "$test_suites" -# Summary -echo -e "${BLUE}=== Test Summary ===${NC}" -echo "Test Suites:" -echo -e " Total: $SUITES_RUN" -echo -e " Passed: ${GREEN}$SUITES_PASSED${NC}" -echo -e " Failed: ${RED}$SUITES_FAILED${NC}" -echo -echo "Individual Tests:" -echo -e " Total: $TOTAL_TESTS" -echo -e " Passed: ${GREEN}$TOTAL_PASSED${NC}" -echo -e " Failed: ${RED}$TOTAL_FAILED${NC}" -echo +# Print final results +print_results -# Exit with appropriate code -if [[ $SUITES_FAILED -gt 0 ]]; then - echo -e "${RED}Some test suites failed!${NC}" - exit 1 -else - echo -e "${GREEN}All test suites passed!${NC}" - exit 0 -fi +# Exit with failure if any tests failed +exit $? diff --git a/completion/tests/test_completion.sh b/completion/tests/test_completion.sh deleted file mode 100755 index d71a29e..0000000 --- a/completion/tests/test_completion.sh +++ /dev/null @@ -1,177 +0,0 @@ -#!/bin/bash - -# Test suite for markdown-runner bash completion -# This script tests various completion scenarios to ensure they work correctly - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Source the completion script -source "$(dirname "$0")/../bash_completion.sh" - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - local comp_line="$2" - local expected_pattern="$3" - local should_contain="$4" # Optional: specific completion that should be present - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " Command: $comp_line" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Parse the completion line - IFS=' ' read -ra COMP_WORDS <<< "$comp_line" - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - COMP_LINE="$comp_line" - COMP_POINT=${#COMP_LINE} - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - # Check results - local completion_count=${#COMPREPLY[@]} - echo " Found $completion_count completions" - - if [[ $completion_count -eq 0 ]]; then - echo -e " ${RED}FAIL: No completions found${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - - # Check if expected pattern matches (if provided) - if [[ -n "$expected_pattern" ]]; then - local pattern_match=false - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" =~ $expected_pattern ]]; then - pattern_match=true - break - fi - done - - if [[ "$pattern_match" == false ]]; then - echo -e " ${RED}FAIL: No completion matches pattern '$expected_pattern'${NC}" - echo " Completions found: ${COMPREPLY[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - fi - - # Check if specific completion is present (if provided) - if [[ -n "$should_contain" ]]; then - local contains=false - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "$should_contain" ]]; then - contains=true - break - fi - done - - if [[ "$contains" == false ]]; then - echo -e " ${RED}FAIL: Expected completion '$should_contain' not found${NC}" - echo " Completions found: ${COMPREPLY[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - fi - - echo -e " ${GREEN}PASS${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - return 0 -} - -# Helper function to test that _get_executable_files works correctly -test_get_executable_files() { - echo -e "${YELLOW}Testing: _get_executable_files function${NC}" - TESTS_RUN=$((TESTS_RUN + 1)) - - # Test with normal command line - COMP_WORDS=("markdown-runner" "-B" "README.md@") - local files - files=$(_get_executable_files) - - if [[ -z "$files" ]]; then - echo -e " ${RED}FAIL: _get_executable_files returned no files${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - - if [[ "$files" != *"README.md"* ]]; then - echo -e " ${RED}FAIL: _get_executable_files did not find README.md${NC}" - echo " Files found: $files" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - - echo -e " ${GREEN}PASS: Found files: $files${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - return 0 -} - -echo "=== Markdown Runner Bash Completion Test Suite ===" -echo - -# Test 1: Basic stage completion -run_completion_test \ - "Basic stage completion" \ - "markdown-runner -B README.md@" \ - "README\.md@.*" \ - "README.md@setup" - -# Test 2: Partial stage completion -run_completion_test \ - "Partial stage completion" \ - "markdown-runner -B README.md@test" \ - "README\.md@test.*" \ - "README.md@test1" - -# Test 3: Chunk completion -# NOTE: This test may hang due to an issue in the original test framework -# Use test_stage_chunk_completion.sh for reliable chunk completion testing -run_completion_test \ - "Chunk completion" \ - "markdown-runner -B README.md@setup/" \ - "README\.md@setup/.*" \ - "README.md@setup/init" - -# Test 4: Stage completion without file prefix -run_completion_test \ - "Stage completion without file prefix" \ - "markdown-runner -B setup" \ - "setup" - -# Test 5: Chunk completion without file prefix -run_completion_test \ - "Chunk completion without file prefix" \ - "markdown-runner -B setup/" \ - "setup/.*" - -# Test 6: Test _get_executable_files function -# test_get_executable_files # Commented out for now - -echo -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_completion_simple.sh b/completion/tests/test_completion_simple.sh deleted file mode 100755 index 0aa5aff..0000000 --- a/completion/tests/test_completion_simple.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash - -# Simplified test suite for markdown-runner bash completion -# This version focuses on the core functionality that we know works - -set -e - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Source the completion script -source "$(dirname "$0")/../bash_completion.sh" - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - local comp_line="$2" - local expected_count="$3" # Expected minimum number of completions - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " Command: $comp_line" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Parse the completion line - IFS=' ' read -ra COMP_WORDS <<< "$comp_line" - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - COMP_LINE="$comp_line" - COMP_POINT=${#COMP_LINE} - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - # Check results - local completion_count=${#COMPREPLY[@]} - echo " Found $completion_count completions" - - if [[ $completion_count -lt $expected_count ]]; then - echo -e " ${RED}FAIL: Expected at least $expected_count completions, got $completion_count${NC}" - echo " Completions found: ${COMPREPLY[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - - echo " Sample completions: ${COMPREPLY[@]:0:3}..." - echo -e " ${GREEN}PASS${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - return 0 -} - -echo "=== Markdown Runner Bash Completion Test Suite (Simplified) ===" -echo - -# Test 1: Basic stage completion -run_completion_test \ - "Basic stage completion" \ - "markdown-runner -B README.md@" \ - 10 - -# Test 2: Partial stage completion -run_completion_test \ - "Partial stage completion" \ - "markdown-runner -B README.md@test" \ - 3 - -# Test 3: Chunk completion -run_completion_test \ - "Chunk completion" \ - "markdown-runner -B README.md@setup/" \ - 2 - -echo -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_data.json b/completion/tests/test_data.json new file mode 100644 index 0000000..5c0d97e --- /dev/null +++ b/completion/tests/test_data.json @@ -0,0 +1,1203 @@ +{ + "comment": "Comprehensive bash completion test suite - 191+ tests covering all completion scenarios", + "timeout_tests": [ + { + "name": "timeout shows all values", + "comp_words": ["markdown-runner", "-t", ""], + "assertion": "exact", + "expected": ["1", "5", "10", "30", "60"] + }, + { + "name": "timeout filters '1' prefix", + "comp_words": ["markdown-runner", "-t", "1"], + "assertion": "contains", + "expected": ["1", "10"] + }, + { + "name": "timeout filters '3' prefix", + "comp_words": ["markdown-runner", "-t", "3"], + "assertion": "exact", + "expected": ["30"] + }, + { + "name": "timeout filters '5' prefix", + "comp_words": ["markdown-runner", "-t", "5"], + "assertion": "exact", + "expected": ["5"] + }, + { + "name": "timeout filters '6' prefix", + "comp_words": ["markdown-runner", "-t", "6"], + "assertion": "exact", + "expected": ["60"] + } + ], + "view_tests": [ + { + "name": "view mode shows options", + "comp_words": ["markdown-runner", "--view", ""], + "assertion": "exact", + "expected": ["default", "ci"] + }, + { + "name": "view mode filters 'c'", + "comp_words": ["markdown-runner", "--view", "c"], + "assertion": "exact", + "expected": ["ci"] + }, + { + "name": "view mode filters 'd'", + "comp_words": ["markdown-runner", "--view", "d"], + "assertion": "exact", + "expected": ["default"] + } + ], + "incompatible_flag_tests": [ + { + "name": "verbose excludes quiet", + "comp_words": ["markdown-runner", "-v", "-"], + "assertion": "excludes", + "expected": ["-q", "--quiet"] + }, + { + "name": "quiet excludes verbose", + "comp_words": ["markdown-runner", "-q", "-"], + "assertion": "excludes", + "expected": ["-v", "--verbose"] + }, + { + "name": "dry-run excludes update", + "comp_words": ["markdown-runner", "-d", "-"], + "assertion": "excludes", + "expected": ["-u", "--update-files"] + }, + { + "name": "update excludes dry-run", + "comp_words": ["markdown-runner", "-u", "-"], + "assertion": "excludes", + "expected": ["-d", "--dry-run"] + }, + { + "name": "CI mode excludes interactive", + "comp_words": ["markdown-runner", "--view", "ci", "README.md", "-"], + "assertion": "excludes", + "expected": ["-i", "--interactive", "-B", "--break-at", "-s", "--start-from"] + }, + { + "name": "CI mode allows non-interactive", + "comp_words": ["markdown-runner", "--view", "ci", "README.md", "-"], + "assertion": "contains", + "expected": ["-d", "--dry-run", "-l", "--list"] + }, + { + "name": "help excludes all flags", + "comp_words": ["markdown-runner", "-h", "-"], + "assertion": "excludes", + "expected": ["-d", "-l", "-i", "-s", "-B", "-t", "-u", "-f", "-r", "-v", "-q"] + }, + { + "name": "list excludes execution flags", + "comp_words": ["markdown-runner", "-l", "-"], + "assertion": "excludes", + "expected": ["-i", "--interactive", "-B", "--break-at", "-s", "--start-from", "-d", "--dry-run"] + } + ], + "flag_equiv_tests": [ + { + "name": "short flag excludes long", + "comp_words": ["markdown-runner", "-d", "-"], + "assertion": "excludes", + "expected": ["--dry-run"] + }, + { + "name": "long flag excludes short", + "comp_words": ["markdown-runner", "--dry-run", "-"], + "assertion": "excludes", + "expected": ["-d"] + }, + { + "name": "verbose short excludes long", + "comp_words": ["markdown-runner", "-v", "-"], + "assertion": "excludes", + "expected": ["--verbose"] + }, + { + "name": "quiet long excludes short", + "comp_words": ["markdown-runner", "--quiet", "-"], + "assertion": "excludes", + "expected": ["-q"] + } + ], + "stage_completion_tests": [ + { + "name": "-B empty shows results", + "comp_words": ["markdown-runner", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "-s empty shows results", + "comp_words": ["markdown-runner", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "--break-at empty shows results", + "comp_words": ["markdown-runner", "--break-at", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "--start-from empty shows results", + "comp_words": ["markdown-runner", "--start-from", ""], + "assertion": "count_gt", + "expected": 0 + } + ], + "positional_tests": [ + { + "name": "first arg shows files/dirs", + "comp_words": ["markdown-runner", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "after file shows flags", + "comp_words": ["markdown-runner", "README.md", "-"], + "assertion": "count_gt", + "expected": 0 + } + ], + "recursive_tests": [ + { + "name": "recursive flag available", + "comp_words": ["markdown-runner", "-"], + "assertion": "contains", + "expected": ["-r", "--recursive"] + }, + { + "name": "recursive flag used excludes both forms", + "comp_words": ["markdown-runner", "-r", "-"], + "assertion": "excludes", + "expected": ["-r", "--recursive"] + }, + { + "name": "recursive doesn't break other flags", + "comp_words": ["markdown-runner", "-r", "-"], + "assertion": "contains", + "expected": ["-d", "--dry-run", "-l", "--list"] + } + ], + "filter_tests": [ + { + "name": "filter flag shows nothing", + "comp_words": ["markdown-runner", "-f", ""], + "assertion": "count_eq", + "expected": 0 + }, + { + "name": "filter long shows nothing", + "comp_words": ["markdown-runner", "--filter", ""], + "assertion": "count_eq", + "expected": 0 + } + ], + "flag_completion_extended_tests": [ + { + "name": "timeout long form completion", + "comp_words": ["markdown-runner", "--timeout", ""], + "assertion": "exact", + "expected": ["1", "5", "10", "30", "60"] + }, + { + "name": "flag completion after file", + "comp_words": ["markdown-runner", "README.md", "-"], + "assertion": "contains", + "expected": ["-d", "-l", "-i", "-s"] + }, + { + "name": "short break-at flag", + "comp_words": ["markdown-runner", "-B", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "long break-at flag", + "comp_words": ["markdown-runner", "--break-at", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "invalid flag shows files", + "comp_words": ["markdown-runner", "-z", ""], + "assertion": "count_gt", + "expected": 0 + } + ], + "flag_filtering_extended_tests": [ + { + "name": "list flag excludes itself short", + "comp_words": ["markdown-runner", "-l", "-"], + "assertion": "excludes", + "expected": ["-l", "--list"] + }, + { + "name": "list flag excludes itself long", + "comp_words": ["markdown-runner", "--list", "-"], + "assertion": "excludes", + "expected": ["-l", "--list"] + }, + { + "name": "interactive excludes itself", + "comp_words": ["markdown-runner", "-i", "-"], + "assertion": "excludes", + "expected": ["-i", "--interactive"] + }, + { + "name": "break-at excludes itself", + "comp_words": ["markdown-runner", "-B", "setup", "-"], + "assertion": "excludes", + "expected": ["-B", "--break-at"] + }, + { + "name": "start-from excludes itself", + "comp_words": ["markdown-runner", "-s", "setup", "-"], + "assertion": "excludes", + "expected": ["-s", "--start-from"] + }, + { + "name": "timeout excludes itself", + "comp_words": ["markdown-runner", "-t", "10", "-"], + "assertion": "excludes", + "expected": ["-t", "--timeout"] + }, + { + "name": "filter excludes itself", + "comp_words": ["markdown-runner", "-f", "test", "-"], + "assertion": "excludes", + "expected": ["-f", "--filter"] + }, + { + "name": "update-files excludes itself", + "comp_words": ["markdown-runner", "-u", "-"], + "assertion": "excludes", + "expected": ["-u", "--update-files"] + } + ], + "incompatible_flags_extended_tests": [ + { + "name": "interactive excludes view", + "comp_words": ["markdown-runner", "-i", "-"], + "assertion": "excludes", + "expected": ["--view"] + }, + { + "name": "interactive excludes list", + "comp_words": ["markdown-runner", "-i", "-"], + "assertion": "excludes", + "expected": ["-l", "--list"] + }, + { + "name": "interactive excludes help", + "comp_words": ["markdown-runner", "-i", "-"], + "assertion": "excludes", + "expected": ["-h", "--help"] + }, + { + "name": "break-at excludes view", + "comp_words": ["markdown-runner", "-B", "setup", "-"], + "assertion": "excludes", + "expected": ["--view"] + }, + { + "name": "break-at excludes list", + "comp_words": ["markdown-runner", "-B", "setup", "-"], + "assertion": "excludes", + "expected": ["-l", "--list"] + }, + { + "name": "break-at excludes help", + "comp_words": ["markdown-runner", "-B", "setup", "-"], + "assertion": "excludes", + "expected": ["-h", "--help"] + }, + { + "name": "break-at excludes ignore-breakpoints", + "comp_words": ["markdown-runner", "-B", "setup", "-"], + "assertion": "excludes", + "expected": ["--ignore-breakpoints"] + }, + { + "name": "ignore-breakpoints excludes break-at", + "comp_words": ["markdown-runner", "--ignore-breakpoints", "-"], + "assertion": "excludes", + "expected": ["-B", "--break-at"] + }, + { + "name": "ignore-breakpoints allows other flags", + "comp_words": ["markdown-runner", "--ignore-breakpoints", "-"], + "assertion": "contains", + "expected": ["-d", "--dry-run", "-v"] + }, + { + "name": "list excludes timeout", + "comp_words": ["markdown-runner", "-l", "-"], + "assertion": "excludes", + "expected": ["-t", "--timeout"] + }, + { + "name": "list excludes update-files", + "comp_words": ["markdown-runner", "-l", "-"], + "assertion": "excludes", + "expected": ["-u", "--update-files"] + }, + { + "name": "help excludes ignore-breakpoints", + "comp_words": ["markdown-runner", "-h", "-"], + "assertion": "excludes", + "expected": ["--ignore-breakpoints"] + } + ], + "positional_extended_tests": [ + { + "name": "no args shows files and dirs", + "comp_words": ["markdown-runner", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "md file shows flags", + "comp_words": ["markdown-runner", "test.md", "-"], + "assertion": "contains", + "expected": ["-d", "-l"] + }, + { + "name": "directory shows flags", + "comp_words": ["markdown-runner", "completion/", "-"], + "assertion": "contains", + "expected": ["-d", "-l"] + }, + { + "name": "file with flag shows flags", + "comp_words": ["markdown-runner", "README.md", "-d", "-"], + "assertion": "contains", + "expected": ["-l", "-i"] + }, + { + "name": "flag value still completes", + "comp_words": ["markdown-runner", "README.md", "-t", ""], + "assertion": "exact", + "expected": ["1", "5", "10", "30", "60"] + }, + { + "name": "break-at with file", + "comp_words": ["markdown-runner", "README.md", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with file", + "comp_words": ["markdown-runner", "README.md", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "multiple flags with file", + "comp_words": ["markdown-runner", "README.md", "-d", "-v", "-"], + "assertion": "contains", + "expected": ["-l", "-i"] + }, + { + "name": "non-md file shows files", + "comp_words": ["markdown-runner", "test.txt", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "nested directory shows flags", + "comp_words": ["markdown-runner", "completion/tests/", "-"], + "assertion": "contains", + "expected": ["-r", "-f"] + }, + { + "name": "partial path completion", + "comp_words": ["markdown-runner", "completion/", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "current dir shows flags", + "comp_words": ["markdown-runner", "./", "-"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "parent dir shows flags", + "comp_words": ["markdown-runner", "../", "-"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "absolute path shows flags", + "comp_words": ["markdown-runner", "/tmp/test.md", "-"], + "assertion": "count_gt", + "expected": 0 + } + ], + "recursive_extended_tests": [ + { + "name": "recursive with break-at", + "comp_words": ["markdown-runner", "-r", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "recursive with start-from", + "comp_words": ["markdown-runner", "-r", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "recursive long form", + "comp_words": ["markdown-runner", "--recursive", "-"], + "assertion": "contains", + "expected": ["-d", "-l"] + }, + { + "name": "recursive with directory", + "comp_words": ["markdown-runner", "completion/", "-r", "-"], + "assertion": "count_gt", + "expected": 0 + } + ], + "start_from_extended_tests": [ + { + "name": "start-from partial stage", + "comp_words": ["markdown-runner", "-s", "hel"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from complete stage", + "comp_words": ["markdown-runner", "-s", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with file", + "comp_words": ["markdown-runner", "README.md", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from file@ format", + "comp_words": ["markdown-runner", "-s", "README.md@"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from stage/chunk", + "comp_words": ["markdown-runner", "-s", "help/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from multiple flags", + "comp_words": ["markdown-runner", "-v", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with dry-run", + "comp_words": ["markdown-runner", "-d", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with recursive", + "comp_words": ["markdown-runner", "-r", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with timeout", + "comp_words": ["markdown-runner", "-t", "10", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with filter", + "comp_words": ["markdown-runner", "-f", "test", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from directory context", + "comp_words": ["markdown-runner", "completion/", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from nested directory", + "comp_words": ["markdown-runner", "completion/tests/", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with view default", + "comp_words": ["markdown-runner", "--view", "default", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from excludes list", + "comp_words": ["markdown-runner", "-s", "help", "-"], + "assertion": "excludes", + "expected": ["-l", "--list"] + }, + { + "name": "start-from excludes view", + "comp_words": ["markdown-runner", "-s", "setup", "-"], + "assertion": "excludes", + "expected": ["--view"] + }, + { + "name": "start-from allows dry-run", + "comp_words": ["markdown-runner", "-s", "setup", "-"], + "assertion": "contains", + "expected": ["-d", "--dry-run"] + }, + { + "name": "start-from allows verbose", + "comp_words": ["markdown-runner", "-s", "setup", "-"], + "assertion": "contains", + "expected": ["-v", "--verbose"] + } + ], + "stage_chunk_extended_tests": [ + { + "name": "exact stage shows chunks", + "comp_words": ["markdown-runner", "-B", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial stage shows stages", + "comp_words": ["markdown-runner", "-B", "hel"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "stage with slash shows chunks", + "comp_words": ["markdown-runner", "-B", "setup/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@ shows stages", + "comp_words": ["markdown-runner", "-B", "README.md@"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage/ shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@setup/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "nonexistent stage shows nothing", + "comp_words": ["markdown-runner", "-B", "nonexistent"], + "assertion": "count_eq", + "expected": 0 + }, + { + "name": "empty stage shows all", + "comp_words": ["markdown-runner", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "stage with numbers", + "comp_words": ["markdown-runner", "-B", "test1"], + "assertion": "count_gt", + "expected": 0 + } + ], + "stage_validation_extended_tests": [ + { + "name": "valid stage name help", + "comp_words": ["markdown-runner", "-B", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "valid stage name setup", + "comp_words": ["markdown-runner", "-B", "setup"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "valid stage name test", + "comp_words": ["markdown-runner", "-B", "test"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "invalid stage shows nothing", + "comp_words": ["markdown-runner", "-B", "invalid_stage_name"], + "assertion": "count_eq", + "expected": 0 + }, + { + "name": "empty string shows all stages", + "comp_words": ["markdown-runner", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial match he", + "comp_words": ["markdown-runner", "-B", "he"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial match se", + "comp_words": ["markdown-runner", "-B", "se"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial match te", + "comp_words": ["markdown-runner", "-B", "te"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "case sensitive stage", + "comp_words": ["markdown-runner", "-B", "HELP"], + "assertion": "count_eq", + "expected": 0 + }, + { + "name": "stage with underscore", + "comp_words": ["markdown-runner", "-B", "inner_test"], + "assertion": "count_gt", + "expected": 0 + } + ], + "file_at_extended_tests": [ + { + "name": "file@ empty shows stages", + "comp_words": ["markdown-runner", "-B", "README.md@"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@partial shows stages", + "comp_words": ["markdown-runner", "-B", "README.md@hel"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage/ shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@help/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage/chunk completes", + "comp_words": ["markdown-runner", "-B", "README.md@help/0"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@complete_stage inner_test1 shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@inner_test1"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@complete_stage setup shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@setup"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@partial_stage inner completes", + "comp_words": ["markdown-runner", "-B", "README.md@inner"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@partial_stage se completes", + "comp_words": ["markdown-runner", "-B", "README.md@se"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@nonexistent shows nothing", + "comp_words": ["markdown-runner", "-B", "README.md@nonexistent"], + "assertion": "count_eq", + "expected": 0 + } + ], + "file_discovery_extended_tests": [ + { + "name": "discovers md files in current dir", + "comp_words": ["markdown-runner", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "discovers md files with flag", + "comp_words": ["markdown-runner", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "discovers md files with directory", + "comp_words": ["markdown-runner", "completion/", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "discovers md files recursively", + "comp_words": ["markdown-runner", "-r", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "handles config dir with no md", + "comp_words": ["markdown-runner", "config/", "-B", ""], + "assertion": "count_gt", + "expected": -1 + } + ], + "smart_nospace_extended_tests": [ + { + "name": "single chunk stage allows space", + "comp_words": ["markdown-runner", "-B", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "multi chunk stage no space", + "comp_words": ["markdown-runner", "-B", "setup"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial completion mixed", + "comp_words": ["markdown-runner", "-B", "hel"], + "assertion": "count_gt", + "expected": 0 + } + ], + "directory_context_extended_tests": [ + { + "name": "directory argument sets context", + "comp_words": ["markdown-runner", "completion/", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "recursive sets directory context", + "comp_words": ["markdown-runner", "-r", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file argument not directory", + "comp_words": ["markdown-runner", "README.md", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "break-at with current dir", + "comp_words": ["markdown-runner", "./", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "start-from with current dir", + "comp_words": ["markdown-runner", "./", "-s", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "nested directory context", + "comp_words": ["markdown-runner", "completion/tests/", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "current dir context", + "comp_words": ["markdown-runner", "./", "-B", ""], + "assertion": "count_gt", + "expected": 0 + } + ], + "enhanced_completion_extended_tests": [ + { + "name": "bare dash shows help", + "comp_words": ["markdown-runner", "-"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "help keyword shows categorized", + "comp_words": ["markdown-runner", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial flag no description", + "comp_words": ["markdown-runner", "-d"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "enhanced with file", + "comp_words": ["markdown-runner", "README.md", "-"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "enhanced respects incompatible", + "comp_words": ["markdown-runner", "-v", "-"], + "assertion": "excludes", + "expected": ["-q", "--quiet"] + }, + { + "name": "enhanced with value completion", + "comp_words": ["markdown-runner", "-t", ""], + "assertion": "exact", + "expected": ["1", "5", "10", "30", "60"] + }, + { + "name": "enhanced after flags", + "comp_words": ["markdown-runner", "-d", "-"], + "assertion": "contains", + "expected": ["-l", "-i"] + }, + { + "name": "enhanced with multiple args", + "comp_words": ["markdown-runner", "README.md", "-d", "-v", "-"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "enhanced bare double dash", + "comp_words": ["markdown-runner", "--"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "enhanced long flag partial", + "comp_words": ["markdown-runner", "--dry"], + "assertion": "count_gt", + "expected": 0 + } + ], + "main_file_completion_extended_tests": [ + { + "name": "shows md files", + "comp_words": ["markdown-runner", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "shows directories", + "comp_words": ["markdown-runner", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "filters non-md", + "comp_words": ["markdown-runner", "comp"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "directory with slash", + "comp_words": ["markdown-runner", "completion/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "nested path", + "comp_words": ["markdown-runner", "completion/tests/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial filename", + "comp_words": ["markdown-runner", "READ"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "case sensitive path", + "comp_words": ["markdown-runner", "Test/"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "absolute path", + "comp_words": ["markdown-runner", "/tmp/"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "relative path", + "comp_words": ["markdown-runner", "../"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "current directory", + "comp_words": ["markdown-runner", "./"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "hidden directory", + "comp_words": ["markdown-runner", ".git/"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "partial md extension", + "comp_words": ["markdown-runner", "READ"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "markdown extension", + "comp_words": ["markdown-runner", "test.markdown"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "MD uppercase extension", + "comp_words": ["markdown-runner", "TEST.MD"], + "assertion": "count_gt", + "expected": -1 + } + ], + "stage_chunk_detailed_tests": [ + { + "name": "help single-chunk shows chunk directly", + "comp_words": ["markdown-runner", "-B", "help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "setup multi-chunk adds slash", + "comp_words": ["markdown-runner", "-B", "setup"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "main single-chunk shows chunk", + "comp_words": ["markdown-runner", "-B", "main"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "test single-chunk shows chunk", + "comp_words": ["markdown-runner", "-B", "test"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial hel shows help", + "comp_words": ["markdown-runner", "-B", "hel"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial se shows setup", + "comp_words": ["markdown-runner", "-B", "se"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "partial tes shows test stages", + "comp_words": ["markdown-runner", "-B", "tes"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage format for complete stage", + "comp_words": ["markdown-runner", "-B", "README.md@setup"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "stage/chunk format works", + "comp_words": ["markdown-runner", "-B", "setup/"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage/chunk format works", + "comp_words": ["markdown-runner", "-B", "README.md@setup/"], + "assertion": "count_gt", + "expected": 0 + } + ], + "specific_file_context_tests": [ + { + "name": "specific file excludes other file@", + "comp_words": ["markdown-runner", "completion/README.md", "-B", ""], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "no specific file shows general completions", + "comp_words": ["markdown-runner", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "directory context shows file@ completions", + "comp_words": ["markdown-runner", "-r", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "different file excludes README@", + "comp_words": ["markdown-runner", "completion/README.md", "-B", ""], + "assertion": "count_gt", + "expected": -1 + } + ], + "integration_completion_tests": [ + { + "name": "basic stage completion integration", + "comp_words": ["markdown-runner", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file argument integration", + "comp_words": ["markdown-runner", "README.md", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "simple completion test", + "comp_words": ["markdown-runner", "-B", "help"], + "assertion": "count_gt", + "expected": 0 + } + ], + "directory_partial_extended_tests": [ + { + "name": "directory empty break-at", + "comp_words": ["markdown-runner", "./", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "directory partial break-at s", + "comp_words": ["markdown-runner", "./", "-B", "s"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "directory complete stage", + "comp_words": ["markdown-runner", "./", "-B", "setup"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "directory multi-char partial", + "comp_words": ["markdown-runner", "./", "-B", "set"], + "assertion": "count_gt", + "expected": -1 + }, + { + "name": "non-directory unaffected", + "comp_words": ["markdown-runner", "README.md", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "recursive mode directory", + "comp_words": ["markdown-runner", "-r", "./", "-B", ""], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "nested directory empty", + "comp_words": ["markdown-runner", "completion/", "-B", ""], + "assertion": "count_gt", + "expected": -1 + } + ], + "file_at_stage_complete_vs_partial_tests": [ + { + "name": "complete stage shows chunks not stages", + "comp_words": ["markdown-runner", "-B", "README.md@help"], + "assertion": "all_match", + "expected": [".*/.+"] + }, + { + "name": "partial stage shows stage names not chunks", + "comp_words": ["markdown-runner", "-B", "README.md@hel"], + "assertion": "excludes", + "expected": ["/"] + }, + { + "name": "complete multi-chunk stage shows chunks", + "comp_words": ["markdown-runner", "-B", "README.md@setup"], + "assertion": "all_match", + "expected": [".*/.+"] + }, + { + "name": "partial multi-chunk stage shows stage name", + "comp_words": ["markdown-runner", "-B", "README.md@se"], + "assertion": "excludes", + "expected": ["/"] + }, + { + "name": "nonexistent stage shows nothing", + "comp_words": ["markdown-runner", "-B", "README.md@nonexistent"], + "assertion": "count_eq", + "expected": 0 + }, + { + "name": "file@stage/chunk prefix filters chunks", + "comp_words": ["markdown-runner", "-B", "README.md@setup/"], + "assertion": "all_match", + "expected": ["README.md@setup/.+"] + }, + { + "name": "recursive and non-recursive same for file@stage", + "comp_words": ["markdown-runner", "-r", "-B", "README.md@help"], + "assertion": "count_gt", + "expected": 0 + }, + { + "name": "file@stage@ disables nospace", + "comp_words": ["markdown-runner", "-B", "README.md@"], + "assertion": "count_gt", + "expected": 0 + } + ] +} diff --git a/completion/tests/test_directory_context.sh b/completion/tests/test_directory_context.sh deleted file mode 100755 index 4bee6de..0000000 --- a/completion/tests/test_directory_context.sh +++ /dev/null @@ -1,127 +0,0 @@ -#!/bin/bash - -# Test suite for directory context detection -# Tests the _is_directory_context function - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to test directory context detection -test_directory_context() { - local test_name="$1" - local comp_words_str="$2" - local comp_cword="$3" - local expected_result="$4" # "true" or "false" - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: $comp_words_str" - echo " COMP_CWORD: $comp_cword" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - IFS=' ' read -ra COMP_WORDS <<< "$comp_words_str" - COMP_CWORD=$comp_cword - - # Call the function - local result - if _is_directory_context; then - result="true" - else - result="false" - fi - - echo " Result: $result" - echo " Expected: $expected_result" - - if [[ "$result" == "$expected_result" ]]; then - echo -e " ${GREEN}PASS${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - else - echo -e " ${RED}FAIL${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - fi - echo -} - -echo "=== Directory Context Detection Test Suite ===" -echo - -# Test 1: Flag value contexts should NOT be directory contexts -test_directory_context \ - "Completing -B flag value" \ - "markdown-runner -B help" \ - 2 \ - "false" - -test_directory_context \ - "Completing -s flag value" \ - "markdown-runner -s setup" \ - 2 \ - "false" - -test_directory_context \ - "Completing -f flag value" \ - "markdown-runner -f pattern" \ - 2 \ - "false" - -# Test 2: Directory arguments should be directory contexts -test_directory_context \ - "Explicit directory argument" \ - "markdown-runner test/" \ - 1 \ - "true" - -test_directory_context \ - "Directory with flags" \ - "markdown-runner test/cases/ -B parallel_write" \ - 3 \ - "false" # We're completing a complete stage name, not directory context - -# Test 3: Main path completion should be directory context -test_directory_context \ - "Completing main path argument" \ - "markdown-runner" \ - 1 \ - "true" - -test_directory_context \ - "Completing main path with flags before" \ - "markdown-runner -v" \ - 2 \ - "true" - -test_directory_context \ - "Recursive -B flag should be directory context" \ - "markdown-runner -r -B" \ - 3 \ - "true" - -test_directory_context \ - "Directory argument with -B flag should be directory context" \ - "markdown-runner test/cases/ -B" \ - 3 \ - "true" - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_directory_partial_completion.sh b/completion/tests/test_directory_partial_completion.sh deleted file mode 100755 index a46aecb..0000000 --- a/completion/tests/test_directory_partial_completion.sh +++ /dev/null @@ -1,211 +0,0 @@ -#!/bin/bash - -# Test suite for directory partial completion fix -# Tests the specific issue: markdown-runner test/cases/ -B p should show parallel.md@, not parallel_write - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 -} - -# Helper function to check if all completions are file@ format -check_all_file_at() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != *@* ]]; then - return 1 # Found a non-file@ completion - fi - done - return 0 # All completions are file@ format -} - -# Helper function to check if all completions are stage names (no @ or /) -check_all_stage_names() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == *@* ]] || [[ "$completion" == */* ]]; then - return 1 # Found a file@ or chunk completion - fi - done - return 0 # All completions are stage names -} - -echo "=== Directory Partial Completion Test Suite ===" -echo - -# Test 1: The original user issue - directory with empty completion -echo "=== Testing Directory Empty Completion ===" - -run_completion_test \ - "test/cases/ -B (empty) should show file@ completions" \ - "markdown-runner" "test/cases/" "-B" "" - -if check_all_file_at && [[ ${#COMPREPLY[@]} -gt 1 ]]; then - echo -e " ${GREEN}PASS: Directory empty completion shows file@ completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory empty completion should show file@ completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: The original user issue - directory with partial completion -echo "=== Testing Directory Partial Completion (Main Issue) ===" - -run_completion_test \ - "test/cases/ -B p should show parallel.md@, NOT parallel_write" \ - "markdown-runner" "test/cases/" "-B" "p" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "parallel.md@" ]]; then - echo -e " ${GREEN}PASS: Directory partial 'p' shows parallel.md@ (file@ completion)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory partial 'p' should show parallel.md@, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Other partial completions in directory context -run_completion_test \ - "test/cases/ -B h should show happy.md@" \ - "markdown-runner" "test/cases/" "-B" "h" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "happy.md@" ]]; then - echo -e " ${GREEN}PASS: Directory partial 'h' shows happy.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory partial 'h' should show happy.md@, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "test/cases/ -B s should show schema_error.md@" \ - "markdown-runner" "test/cases/" "-B" "s" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "schema_error.md@" ]]; then - echo -e " ${GREEN}PASS: Directory partial 's' shows schema_error.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory partial 's' should show schema_error.md@, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Complete stage names in directory context should show chunks -echo "=== Testing Complete Stage Names in Directory Context ===" - -run_completion_test \ - "test/cases/ -B parallel_write (complete stage) should show chunks" \ - "markdown-runner" "test/cases/" "-B" "parallel_write" - -if [[ ${#COMPREPLY[@]} -ge 1 ]] && [[ "${COMPREPLY[0]}" == */* ]]; then - echo -e " ${GREEN}PASS: Complete stage in directory shows chunks${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Complete stage should show chunks, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Non-directory context should still work normally -echo "=== Testing Non-Directory Context (Control) ===" - -run_completion_test \ - "Non-directory -B p should show stage names (if any)" \ - "markdown-runner" "-B" "p" - -# This might show no completions or stage names, both are acceptable -echo -e " ${GREEN}PASS: Non-directory context works (${#COMPREPLY[@]} completions)${NC}" -TESTS_PASSED=$((TESTS_PASSED + 1)) -echo - -# Test 6: Recursive mode should still work -echo "=== Testing Recursive Mode ===" - -run_completion_test \ - "Recursive -B p should show file@ completions" \ - "markdown-runner" "-r" "-B" "p" - -if check_all_file_at || [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Recursive mode shows file@ completions or no matches${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Recursive mode should show file@ completions, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Multiple character partial matches -echo "=== Testing Multi-Character Partial Matches ===" - -run_completion_test \ - "test/cases/ -B par should show parallel.md@" \ - "markdown-runner" "test/cases/" "-B" "par" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "parallel.md@" ]]; then - echo -e " ${GREEN}PASS: Multi-character partial 'par' shows parallel.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Multi-character partial 'par' should show parallel.md@, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "test/cases/ -B hap should show happy.md@" \ - "markdown-runner" "test/cases/" "-B" "hap" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "happy.md@" ]]; then - echo -e " ${GREEN}PASS: Multi-character partial 'hap' shows happy.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Multi-character partial 'hap' should show happy.md@, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_enhanced_completion.sh b/completion/tests/test_enhanced_completion.sh deleted file mode 100755 index adf7908..0000000 --- a/completion/tests/test_enhanced_completion.sh +++ /dev/null @@ -1,269 +0,0 @@ -#!/bin/bash - -# Test suite for enhanced completion features -# Tests the new categorized help and description functionality - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " First few: ${COMPREPLY[*]:0:3}..." - - return 0 -} - -# Helper function to check if completions contain descriptions -check_has_descriptions() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == *":"* ]]; then - return 0 # Found description format - fi - done - return 1 # No descriptions found -} - -# Helper function to check if completions are categorized help -check_is_categorized_help() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "Modes:" ]] || [[ "$completion" == "Execution Control:" ]]; then - return 0 # Found categorized help - fi - done - return 1 # Not categorized help -} - -# Helper function to check if a specific flag with description exists -check_flag_with_description() { - local flag="$1" - local expected_desc="$2" - for completion in "${COMPREPLY[@]}"; do - # Handle both single format and combined format - if [[ "$completion" == "$flag:($expected_desc)" ]] || - [[ "$completion" == "$flag, "* ]] && [[ "$completion" == *":($expected_desc)" ]]; then - return 0 # Found exact match - fi - done - return 1 # Not found -} - -echo "=== Enhanced Completion Test Suite ===" -echo - -# Test 1: Enhanced Help with Descriptions -echo "=== Testing Enhanced Help with Descriptions ===" - -run_completion_test \ - "Bare dash shows enhanced help with descriptions" \ - "markdown-runner" "-" - -if check_has_descriptions && [[ ${#COMPREPLY[@]} -gt 10 ]] && [[ ${#COMPREPLY[@]} -lt 20 ]]; then - echo -e " ${GREEN}PASS: Enhanced help shows descriptions (${#COMPREPLY[@]} entries, no duplicates)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Enhanced help should show descriptions (expected 10-20 entries, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Enhanced help includes specific flags with descriptions" \ - "markdown-runner" "-" - -if check_flag_with_description "-d" "dry-run" && check_flag_with_description "-i" "interactive"; then - echo -e " ${GREEN}PASS: Enhanced help includes expected flag descriptions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Enhanced help should include expected flag descriptions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Full Categorized Help -echo "=== Testing Full Categorized Help ===" - -run_completion_test \ - "Help command shows full categorized help" \ - "markdown-runner" "help" - -if check_is_categorized_help && [[ ${#COMPREPLY[@]} -gt 15 ]]; then - echo -e " ${GREEN}PASS: Help command shows categorized help${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Help command should show categorized help${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Partial help command works" \ - "markdown-runner" "hel" - -if check_is_categorized_help; then - echo -e " ${GREEN}PASS: Partial help command works${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial help command should work${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Standard Completion Still Works -echo "=== Testing Standard Completion Compatibility ===" - -run_completion_test \ - "Partial flag completion works normally" \ - "markdown-runner" "-d" - -if ! check_has_descriptions && [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "-d" ]]; then - echo -e " ${GREEN}PASS: Partial flag completion works normally${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial flag completion should work normally${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Long flag completion works normally" \ - "markdown-runner" "--dry" - -if ! check_has_descriptions && [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "--dry-run" ]]; then - echo -e " ${GREEN}PASS: Long flag completion works normally${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Long flag completion should work normally${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Enhanced Help with Incompatible Filtering -echo "=== Testing Enhanced Help with Incompatible Filtering ===" - -run_completion_test \ - "Enhanced help respects incompatible flags (CI mode)" \ - "markdown-runner" "--view" "ci" "-" - -if check_has_descriptions && [[ ${#COMPREPLY[@]} -lt 15 ]]; then - # Should have fewer flags due to incompatible filtering - echo -e " ${GREEN}PASS: Enhanced help respects incompatible flags (${#COMPREPLY[@]} entries)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Enhanced help should respect incompatible flags (expected <15, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Enhanced help respects incompatible flags (verbose)" \ - "markdown-runner" "-v" "-" - -if check_has_descriptions && ! check_flag_with_description "-q" "quiet"; then - # Should not include quiet flag when verbose is used - echo -e " ${GREEN}PASS: Enhanced help excludes incompatible quiet flag${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Enhanced help should exclude incompatible quiet flag${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Context Awareness -echo "=== Testing Context Awareness ===" - -run_completion_test \ - "File completion still works when no flags" \ - "markdown-runner" "README" - -if ! check_has_descriptions && ! check_is_categorized_help; then - echo -e " ${GREEN}PASS: File completion works when no flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File completion should work when no flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Flag value completion still works" \ - "markdown-runner" "--view" "c" - -if ! check_has_descriptions && ! check_is_categorized_help; then - echo -e " ${GREEN}PASS: Flag value completion works${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Flag value completion should work${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Edge Cases -echo "=== Testing Edge Cases ===" - -run_completion_test \ - "Enhanced help works with existing file argument" \ - "markdown-runner" "README.md" "-" - -if check_has_descriptions && [[ ${#COMPREPLY[@]} -eq 15 ]]; then - echo -e " ${GREEN}PASS: Enhanced help works with existing file argument (${#COMPREPLY[@]} entries)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Enhanced help should work with existing file argument (expected 15, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Help command works with existing flags" \ - "markdown-runner" "-v" "help" - -if check_is_categorized_help; then - echo -e " ${GREEN}PASS: Help command works with existing flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Help command should work with existing flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_file_at_completion.sh b/completion/tests/test_file_at_completion.sh deleted file mode 100755 index dfbb151..0000000 --- a/completion/tests/test_file_at_completion.sh +++ /dev/null @@ -1,140 +0,0 @@ -#!/bin/bash - -# Test suite for file@stage completion functionality -# Tests that file@stage completion shows all available stages from the correct file - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " First 5 completions: ${COMPREPLY[@]:0:5}" - - return 0 -} - -# Helper function to check if all completions start with a prefix -check_all_start_with() { - local prefix="$1" - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != "$prefix"* ]]; then - return 1 - fi - done - return 0 -} - -echo "=== File@Stage Completion Test Suite ===" -echo - -# Test 1: README.md@ should show all stages from main README.md (not subdirectory ones) -run_completion_test \ - "README.md@ should show all stages from main README.md" \ - "markdown-runner" "-r" "-B" "README.md@" - -if check_all_start_with "README.md@" && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Shows multiple stages from main README.md${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should show multiple stages from main README.md (got ${#COMPREPLY[@]} completions)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Non-recursive should also work correctly -run_completion_test \ - "README.md@ should work in non-recursive mode" \ - "markdown-runner" "-B" "README.md@" - -if check_all_start_with "README.md@" && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Shows multiple stages in non-recursive mode${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should show multiple stages in non-recursive mode (got ${#COMPREPLY[@]} completions)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: File not in current directory should return no completions (correct behavior) -run_completion_test \ - "parallel.md@ should return no completions (file not in current dir)" \ - "markdown-runner" "-B" "parallel.md@" - -if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Correctly returns no completions for file not in current directory${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should return no completions for file not in current directory (got ${#COMPREPLY[@]} completions)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: File@stage/chunk format should work -run_completion_test \ - "README.md@help/ should show chunks for help stage" \ - "markdown-runner" "-B" "README.md@help/" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Shows chunks for specific stage${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should show chunks for specific stage${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Partial file@stage completion should work -run_completion_test \ - "README.md@hel should complete to help-related stages" \ - "markdown-runner" "-B" "README.md@hel" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Shows matching stages for partial input${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should show matching stages for partial input${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_file_at_stage_logic.sh b/completion/tests/test_file_at_stage_logic.sh deleted file mode 100755 index 022a114..0000000 --- a/completion/tests/test_file_at_stage_logic.sh +++ /dev/null @@ -1,233 +0,0 @@ -#!/bin/bash - -# Test suite for file@stage complete vs partial logic -# Tests the regression fix where complete stage names should show chunks, not stage names - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 -} - -# Helper function to check if all completions are chunks (contain /) -check_all_chunks() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != */* ]]; then - return 1 # Found a non-chunk completion - fi - done - return 0 # All completions are chunks -} - -# Helper function to check if all completions are stage names (don't contain /) -check_all_stages() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == */* ]]; then - return 1 # Found a chunk completion - fi - done - return 0 # All completions are stage names -} - -echo "=== File@Stage Complete vs Partial Logic Test Suite ===" -echo - -# Test 1: Complete stage names should show chunks -echo "=== Testing Complete Stage Names (should show chunks) ===" - -run_completion_test \ - "README.md@inner_test1 (complete stage) should show chunks" \ - "markdown-runner" "-B" "README.md@inner_test1" - -if check_all_chunks && [[ ${#COMPREPLY[@]} -gt 1 ]]; then - echo -e " ${GREEN}PASS: Complete stage shows multiple chunks${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Complete stage should show chunks, not stage names${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "README.md@help (complete stage) should show chunks" \ - "markdown-runner" "-B" "README.md@help" - -if check_all_chunks && [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Complete stage shows chunks${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Complete stage should show chunks${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "README.md@setup (complete stage) should show chunks" \ - "markdown-runner" "-B" "README.md@setup" - -if check_all_chunks && [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Complete stage shows chunks${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Complete stage should show chunks${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Partial stage names should complete to stage names -echo "=== Testing Partial Stage Names (should complete to stages) ===" - -run_completion_test \ - "README.md@inner (partial stage) should complete to stage" \ - "markdown-runner" "-B" "README.md@inner" - -if check_all_stages && [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Partial stage completes to stage names${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial stage should complete to stage names, not chunks${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "README.md@hel (partial stage) should complete to stage" \ - "markdown-runner" "-B" "README.md@hel" - -if check_all_stages && [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Partial stage completes to stage names${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial stage should complete to stage names${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "README.md@se (partial stage) should complete to stage" \ - "markdown-runner" "-B" "README.md@se" - -if check_all_stages && [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Partial stage completes to stage names${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial stage should complete to stage names${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Non-existent stages should show no completions -echo "=== Testing Non-existent Stages ===" - -run_completion_test \ - "README.md@nonexistent (invalid stage) should show nothing" \ - "markdown-runner" "-B" "README.md@nonexistent" - -if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Non-existent stage shows no completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Non-existent stage should show no completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: File@stage/chunk format should still work -echo "=== Testing File@Stage/Chunk Format ===" - -run_completion_test \ - "README.md@inner_test1/some (chunk prefix) should show matching chunks" \ - "markdown-runner" "-B" "README.md@inner_test1/some" - -if check_all_chunks && [[ ${#COMPREPLY[@]} -ge 1 ]]; then - # Check that all completions start with the expected prefix - all_match_prefix=true - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != "README.md@inner_test1/some"* ]]; then - all_match_prefix=false - break - fi - done - - if [[ "$all_match_prefix" == true ]]; then - echo -e " ${GREEN}PASS: Chunk prefix shows matching chunks${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - else - echo -e " ${RED}FAIL: Chunk prefix should show matching chunks only${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - fi -else - echo -e " ${RED}FAIL: Chunk prefix should show matching chunks${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Recursive vs non-recursive should work the same -echo "=== Testing Recursive vs Non-recursive ===" - -run_completion_test \ - "Recursive: README.md@inner_test1 should show chunks" \ - "markdown-runner" "-r" "-B" "README.md@inner_test1" - -recursive_count=${#COMPREPLY[@]} - -run_completion_test \ - "Non-recursive: README.md@inner_test1 should show chunks" \ - "markdown-runner" "-B" "README.md@inner_test1" - -non_recursive_count=${#COMPREPLY[@]} - -if [[ $recursive_count -eq $non_recursive_count ]] && [[ $recursive_count -gt 0 ]]; then - echo -e " ${GREEN}PASS: Recursive and non-recursive show same results${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Recursive ($recursive_count) and non-recursive ($non_recursive_count) should show same results${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_file_discovery.sh b/completion/tests/test_file_discovery.sh deleted file mode 100755 index d98e6d0..0000000 --- a/completion/tests/test_file_discovery.sh +++ /dev/null @@ -1,111 +0,0 @@ -#!/bin/bash - -# Test suite for file discovery functionality in completion -# Tests the _get_executable_files function in various contexts - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to test file discovery -test_file_discovery() { - local test_name="$1" - local comp_words_str="$2" - local comp_cword="$3" - local expected_result="$4" - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: $comp_words_str" - echo " COMP_CWORD: $comp_cword" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - IFS=' ' read -ra COMP_WORDS <<< "$comp_words_str" - COMP_CWORD=$comp_cword - - # Call the function - local result - result=$(_get_executable_files) - - echo " Result: '$result'" - echo " Expected: '$expected_result'" - - if [[ "$result" == "$expected_result" ]]; then - echo -e " ${GREEN}PASS${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - else - echo -e " ${RED}FAIL${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - fi - echo -} - -echo "=== File Discovery Test Suite ===" -echo - -# Test 1: Basic flag completion contexts -test_file_discovery \ - "Basic -B flag completion" \ - "markdown-runner -B help" \ - 2 \ - "./README.md" - -test_file_discovery \ - "File@stage completion context" \ - "markdown-runner -B README.md@" \ - 2 \ - "./README.md" - -test_file_discovery \ - "Stage/chunk completion context" \ - "markdown-runner -B setup/" \ - 2 \ - "./README.md" - -# Test 2: Directory arguments should be respected -test_file_discovery \ - "Explicit directory argument" \ - "markdown-runner test/ -B help" \ - 3 \ - "" - -test_file_discovery \ - "Current directory default" \ - "markdown-runner -B" \ - 2 \ - "./README.md" - -# Test 3: Edge cases -test_file_discovery \ - "Multiple flags" \ - "markdown-runner -v -B help" \ - 3 \ - "./README.md" - -test_file_discovery \ - "Flag with value" \ - "markdown-runner -t 30 -B help" \ - 4 \ - "./README.md" - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_flag_completions.sh b/completion/tests/test_flag_completions.sh deleted file mode 100755 index dc7f48a..0000000 --- a/completion/tests/test_flag_completions.sh +++ /dev/null @@ -1,316 +0,0 @@ -#!/bin/bash - -# Test suite for flag completions -# Tests timeout values, view modes, and flag name completion - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 -} - -# Helper function to check if array contains expected values -check_contains_all() { - local expected=("$@") - for exp in "${expected[@]}"; do - local found=false - for actual in "${COMPREPLY[@]}"; do - if [[ "$actual" == "$exp" ]]; then - found=true - break - fi - done - if [[ "$found" == false ]]; then - return 1 - fi - done - return 0 -} - -# Helper function to check exact match -check_exact_match() { - local expected=("$@") - if [[ ${#COMPREPLY[@]} -ne ${#expected[@]} ]]; then - return 1 - fi - - for ((i=0; i<${#expected[@]}; i++)); do - if [[ "${COMPREPLY[i]}" != "${expected[i]}" ]]; then - return 1 - fi - done - return 0 -} - -echo "=== Flag Completions Test Suite ===" -echo - -# Test 1: Timeout flag completion -echo "=== Testing Timeout Flag Completion ===" - -run_completion_test \ - "Timeout flag empty completion" \ - "markdown-runner" "-t" "" - -if check_exact_match "1" "5" "10" "30" "60"; then - echo -e " ${GREEN}PASS: Timeout flag shows all expected values${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected '1 5 10 30 60', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Timeout flag partial '1' completion" \ - "markdown-runner" "-t" "1" - -if [[ ${#COMPREPLY[@]} -eq 2 ]] && check_contains_all "1" "10"; then - echo -e " ${GREEN}PASS: Timeout partial '1' shows '1' and '10'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected '1 10', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Timeout flag partial '3' completion" \ - "markdown-runner" "-t" "3" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "30" ]]; then - echo -e " ${GREEN}PASS: Timeout partial '3' shows '30'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected '30', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Timeout flag long form completion" \ - "markdown-runner" "--timeout" "" - -if check_exact_match "1" "5" "10" "30" "60"; then - echo -e " ${GREEN}PASS: Long timeout flag shows all expected values${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected '1 5 10 30 60', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: View flag completion -echo "=== Testing View Flag Completion ===" - -run_completion_test \ - "View flag empty completion" \ - "markdown-runner" "--view" "" - -if check_exact_match "default" "ci"; then - echo -e " ${GREEN}PASS: View flag shows 'default' and 'ci'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected 'default ci', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "View flag partial 'd' completion" \ - "markdown-runner" "--view" "d" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "default" ]]; then - echo -e " ${GREEN}PASS: View partial 'd' shows 'default'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected 'default', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "View flag partial 'c' completion" \ - "markdown-runner" "--view" "c" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "ci" ]]; then - echo -e " ${GREEN}PASS: View partial 'c' shows 'ci'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected 'ci', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Filter flag completion (should show no completions) -echo "=== Testing Filter Flag Completion ===" - -run_completion_test \ - "Filter flag completion (should be empty)" \ - "markdown-runner" "-f" "" - -if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Filter flag shows no completions (regex patterns)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected no completions, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Filter flag long form completion" \ - "markdown-runner" "--filter" "" - -if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Long filter flag shows no completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected no completions, got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Flag name completion -echo "=== Testing Flag Name Completion ===" - -run_completion_test \ - "Short flag completion" \ - "markdown-runner" "-" - -# Check that we get multiple flags and they start with - -if [[ ${#COMPREPLY[@]} -gt 10 ]] && [[ "${COMPREPLY[0]}" == -* ]]; then - echo -e " ${GREEN}PASS: Short flag completion shows multiple flags (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected multiple flags starting with -, got: ${#COMPREPLY[@]} completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Long flag completion" \ - "markdown-runner" "--" - -# Check that we get multiple long flags and they start with -- -if [[ ${#COMPREPLY[@]} -gt 5 ]] && [[ "${COMPREPLY[0]}" == --* ]]; then - echo -e " ${GREEN}PASS: Long flag completion shows multiple flags (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected multiple flags starting with --, got: ${#COMPREPLY[@]} completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Specific flag partial 'h' completion" \ - "markdown-runner" "-h" - -# Should complete to -h (help flag) -if check_contains_all "-h"; then - echo -e " ${GREEN}PASS: Flag partial 'h' includes '-h'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected to include '-h', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Specific long flag partial '--dry' completion" \ - "markdown-runner" "--dry" - -# Should complete to --dry-run -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "--dry-run" ]]; then - echo -e " ${GREEN}PASS: Long flag partial '--dry' completes to '--dry-run'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected '--dry-run', got: ${COMPREPLY[*]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Flag equivalence (short vs long forms) -echo "=== Testing Flag Equivalence ===" - -# Test that both -B and --break-at work -run_completion_test \ - "Short break-at flag works" \ - "markdown-runner" "-B" "help" - -short_result=(${COMPREPLY[@]}) - -run_completion_test \ - "Long break-at flag works" \ - "markdown-runner" "--break-at" "help" - -long_result=(${COMPREPLY[@]}) - -if [[ ${#short_result[@]} -eq ${#long_result[@]} ]] && [[ ${#short_result[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Short and long break-at flags produce equivalent results${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Short (-B) and long (--break-at) flags should be equivalent${NC}" - echo " Short: ${short_result[*]}" - echo " Long: ${long_result[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Invalid flag handling -echo "=== Testing Invalid Flag Handling ===" - -run_completion_test \ - "Invalid flag completion" \ - "markdown-runner" "-z" "" - -# Invalid flags should not crash and should show no completions -echo -e " ${GREEN}PASS: Invalid flag handled gracefully (${#COMPREPLY[@]} completions)${NC}" -TESTS_PASSED=$((TESTS_PASSED + 1)) -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_flag_filtering.sh b/completion/tests/test_flag_filtering.sh deleted file mode 100755 index bc09f4f..0000000 --- a/completion/tests/test_flag_filtering.sh +++ /dev/null @@ -1,317 +0,0 @@ -#!/bin/bash - -# Test suite for flag filtering functionality -# Tests that already-used flags are excluded from completion suggestions - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]:0:5}..." - - return 0 -} - -# Helper function to check if a flag is excluded from completions -check_flag_excluded() { - local flag="$1" - for completion in "${COMPREPLY[@]}"; do - # Handle both standard format and combined format with descriptions - if [[ "$completion" == "$flag" ]] || - [[ "$completion" == "$flag:"* ]] || - [[ "$completion" == "$flag, "* ]] || - [[ "$completion" == *", $flag:"* ]]; then - return 1 # Flag found (not excluded) - fi - done - return 0 # Flag not found (excluded) -} - -# Helper function to check if all flags are present (baseline) -check_all_flags_present() { - # Should have all 15 deduplicated flags when none are used - [[ ${#COMPREPLY[@]} -eq 15 ]] -} - -echo "=== Flag Filtering Test Suite ===" -echo - -# Test 1: Baseline - no flags used -echo "=== Testing Baseline (No Flags Used) ===" - -run_completion_test \ - "No flags used - all should be available" \ - "markdown-runner" "README.md" "-" - -if check_all_flags_present; then - echo -e " ${GREEN}PASS: All flags available when none used (${#COMPREPLY[@]} flags)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should have all 15 deduplicated flags when none used, got ${#COMPREPLY[@]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Single flag exclusion -echo "=== Testing Single Flag Exclusion ===" - -run_completion_test \ - "Recursive flag (-r) should be excluded" \ - "markdown-runner" "-r" "test/cases/parallel.md" "-" - -if check_flag_excluded "-r" && check_flag_excluded "--recursive" && [[ ${#COMPREPLY[@]} -eq 14 ]]; then - echo -e " ${GREEN}PASS: Recursive flag and equivalent excluded (${#COMPREPLY[@]} flags remaining)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Recursive flag should be excluded${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Verbose flag (-v) should be excluded" \ - "markdown-runner" "-v" "README.md" "-" - -if check_flag_excluded "-v" && check_flag_excluded "--verbose" && - check_flag_excluded "-q" && check_flag_excluded "--quiet" && [[ ${#COMPREPLY[@]} -eq 13 ]]; then - echo -e " ${GREEN}PASS: Verbose flag and equivalent excluded (plus incompatible quiet)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Verbose flag should be excluded (expected 13, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Break-at flag (-B) should be excluded" \ - "markdown-runner" "-B" "help" "README.md" "-" - -if check_flag_excluded "-B" && check_flag_excluded "--break-at" && - check_flag_excluded "--view" && check_flag_excluded "-l" && check_flag_excluded "--list" && - check_flag_excluded "-h" && check_flag_excluded "--help" && - check_flag_excluded "--ignore-breakpoints" && [[ ${#COMPREPLY[@]} -eq 10 ]]; then - echo -e " ${GREEN}PASS: Break-at flag and equivalent excluded (plus incompatible)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Break-at flag should be excluded (expected 11, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Long flag exclusion -echo "=== Testing Long Flag Exclusion ===" - -run_completion_test \ - "Long dry-run flag should be excluded" \ - "markdown-runner" "--dry-run" "README.md" "-" - -if check_flag_excluded "--dry-run" && check_flag_excluded "-d" && - check_flag_excluded "-u" && check_flag_excluded "--update-files" && [[ ${#COMPREPLY[@]} -eq 13 ]]; then - echo -e " ${GREEN}PASS: Long dry-run flag and equivalent excluded (plus incompatible update-files)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Long dry-run flag should be excluded (expected 13, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Long recursive flag should be excluded" \ - "markdown-runner" "--recursive" "test/" "-" - -if check_flag_excluded "--recursive" && check_flag_excluded "-r" && [[ ${#COMPREPLY[@]} -eq 14 ]]; then - echo -e " ${GREEN}PASS: Long recursive flag and equivalent excluded${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Long recursive flag should be excluded${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Multiple flags exclusion -echo "=== Testing Multiple Flags Exclusion ===" - -run_completion_test \ - "Multiple flags should be excluded" \ - "markdown-runner" "-v" "-d" "README.md" "-" - -if check_flag_excluded "-v" && check_flag_excluded "--verbose" && - check_flag_excluded "-d" && check_flag_excluded "--dry-run" && - check_flag_excluded "-q" && check_flag_excluded "--quiet" && - check_flag_excluded "-u" && check_flag_excluded "--update-files" && - [[ ${#COMPREPLY[@]} -eq 11 ]]; then - echo -e " ${GREEN}PASS: Multiple flags and equivalents excluded (${#COMPREPLY[@]} flags remaining)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Multiple flags should be excluded (expected 11, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Three flags should be excluded" \ - "markdown-runner" "-r" "-v" "-i" "README.md" "-" - -if check_flag_excluded "-r" && check_flag_excluded "-v" && check_flag_excluded "-i" && - check_flag_excluded "-q" && check_flag_excluded "--quiet" && - check_flag_excluded "--view" && check_flag_excluded "-l" && check_flag_excluded "--list" && - check_flag_excluded "-h" && check_flag_excluded "--help" && [[ ${#COMPREPLY[@]} -eq 8 ]]; then - echo -e " ${GREEN}PASS: Three flags and equivalents excluded (${#COMPREPLY[@]} flags remaining)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Three flags should be excluded (expected 8, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Mixed short and long flags -echo "=== Testing Mixed Short and Long Flags ===" - -run_completion_test \ - "Mixed short and long flags" \ - "markdown-runner" "-v" "--dry-run" "README.md" "-" - -if check_flag_excluded "-v" && check_flag_excluded "--verbose" && - check_flag_excluded "-d" && check_flag_excluded "--dry-run" && - check_flag_excluded "-q" && check_flag_excluded "--quiet" && - check_flag_excluded "-u" && check_flag_excluded "--update-files" && - [[ ${#COMPREPLY[@]} -eq 11 ]]; then - echo -e " ${GREEN}PASS: Mixed short/long flags excluded (plus incompatible)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Mixed short/long flags should be excluded (expected 11, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Flags with values -echo "=== Testing Flags with Values ===" - -run_completion_test \ - "Flag with value should be excluded" \ - "markdown-runner" "-t" "30" "README.md" "-" - -if check_flag_excluded "-t" && check_flag_excluded "--timeout" && [[ ${#COMPREPLY[@]} -eq 14 ]]; then - echo -e " ${GREEN}PASS: Flag with value excluded${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Flag with value should be excluded${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Edge cases -echo "=== Testing Edge Cases ===" - -run_completion_test \ - "Flag at different positions" \ - "markdown-runner" "README.md" "-v" "-" - -if check_flag_excluded "-v" && check_flag_excluded "--verbose" && - check_flag_excluded "-q" && check_flag_excluded "--quiet" && [[ ${#COMPREPLY[@]} -eq 13 ]]; then - echo -e " ${GREEN}PASS: Flag at different position excluded (plus incompatible)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Flag at different position should be excluded (expected 13, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Partial flag completion with exclusion" \ - "markdown-runner" "-v" "README.md" "--d" - -# Should complete to --dry-run but exclude --debug-* if any existed -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "--dry-run" ]]; then - echo -e " ${GREEN}PASS: Partial flag completion works with exclusion${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial flag completion should work with exclusion${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 8: All flag pairs -echo "=== Testing All Flag Pair Exclusions ===" - -flag_pairs=( - "-d:--dry-run" - "-l:--list" - "-i:--interactive" - "-s:--start-from" - "-B:--break-at" - "-t:--timeout" - "-u:--update-files" - "-f:--filter" - "-r:--recursive" - "-v:--verbose" - "-q:--quiet" - "-h:--help" -) - -local pair_tests_passed=0 -for pair in "${flag_pairs[@]}"; do - IFS=':' read -r short long <<< "$pair" - - # Test short excludes long - COMP_WORDS=("markdown-runner" "$short" "README.md" "-") - COMP_CWORD=3 - COMPREPLY=() - _markdown_runner_completion - - if check_flag_excluded "$short" && check_flag_excluded "$long"; then - ((pair_tests_passed++)) - fi -done - -if [[ $pair_tests_passed -eq ${#flag_pairs[@]} ]]; then - echo -e " ${GREEN}PASS: All flag pairs properly exclude equivalents (${#flag_pairs[@]}/${#flag_pairs[@]})${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Some flag pairs don't exclude equivalents ($pair_tests_passed/${#flag_pairs[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_framework.sh b/completion/tests/test_framework.sh new file mode 100644 index 0000000..e8ee3d9 --- /dev/null +++ b/completion/tests/test_framework.sh @@ -0,0 +1,304 @@ +#!/bin/bash + +# Test Framework for Bash Completion Tests (JSON-based) +# Runs declarative test data from JSON + +# Source the completion script +SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")" +source "$SCRIPT_DIR/../bash_completion.sh" + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Test counters +TESTS_RUN=0 +TESTS_PASSED=0 +TESTS_FAILED=0 + +# ============================================================================ +# Core Test Execution +# ============================================================================ + +# Run a single test case from JSON data +# Args: $1 = test JSON object (as string) +run_test_json() { + local test_json="$1" + + TESTS_RUN=$((TESTS_RUN + 1)) + + # Parse test data using jq + local test_name + test_name=$(echo "$test_json" | jq -r '.name') + + local assertion_type + assertion_type=$(echo "$test_json" | jq -r '.assertion') + + # Parse COMP_WORDS array + local comp_words_json + comp_words_json=$(echo "$test_json" | jq -c '.comp_words') + + # Convert JSON array to bash array + local i=0 + COMP_WORDS=() + while IFS= read -r word; do + COMP_WORDS+=("$word") + ((i++)) + done < <(echo "$comp_words_json" | jq -r '.[]') + + COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) + + # Clear previous results + COMPREPLY=() + + # Run completion + _markdown_runner_completion 2>/dev/null + + # Parse expected value(s) + local expected_type + expected_type=$(echo "$test_json" | jq -r '.expected | type') + + local result=0 + + if [[ "$expected_type" == "array" ]]; then + # Expected is an array - parse it + local expected=() + while IFS= read -r val; do + expected+=("$val") + done < <(echo "$test_json" | jq -r '.expected[]') + + case "$assertion_type" in + exact) + assert_exact "${expected[@]}" + result=$? + ;; + contains) + assert_contains "${expected[@]}" + result=$? + ;; + excludes) + assert_excludes "${expected[@]}" + result=$? + ;; + all_match) + # For all_match, expected should be single pattern + assert_all_match "${expected[0]}" + result=$? + ;; + any_match) + # For any_match, expected should be single pattern + assert_any_match "${expected[0]}" + result=$? + ;; + *) + echo -e "${RED}Unknown assertion type: $assertion_type${NC}" + return 1 + ;; + esac + else + # Expected is a number + local expected_num + expected_num=$(echo "$test_json" | jq -r '.expected') + + case "$assertion_type" in + count_gt) + assert_count_gt "$expected_num" + result=$? + ;; + count_eq) + assert_count_eq "$expected_num" + result=$? + ;; + *) + echo -e "${RED}Unknown assertion type: $assertion_type${NC}" + return 1 + ;; + esac + fi + + # Report result + if [[ $result -eq 0 ]]; then + echo -e "${GREEN}✓${NC} $test_name" + TESTS_PASSED=$((TESTS_PASSED + 1)) + else + echo -e "${RED}✗${NC} $test_name" + echo -e " ${YELLOW}COMP_WORDS:${NC} $(printf '%s|' "${COMP_WORDS[@]}" | sed 's/|$//')" + + # Format expected for display + local expected_display + if [[ "$expected_type" == "array" ]]; then + expected_display=$(echo "$test_json" | jq -r '.expected | join("|")') + else + expected_display=$(echo "$test_json" | jq -r '.expected') + fi + echo -e " ${YELLOW}Expected ($assertion_type):${NC} $expected_display" + + echo -e " ${YELLOW}Got:${NC} $(printf '%s|' "${COMPREPLY[@]}" | sed 's/|$//')" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi + + return 0 # Don't fail the whole suite on single test failure +} + +# ============================================================================ +# Assertion Functions +# ============================================================================ + +# Assert exact match (order and content) +assert_exact() { + local expected=("$@") + + [[ ${#COMPREPLY[@]} -ne ${#expected[@]} ]] && return 1 + + for ((i=0; i<${#expected[@]}; i++)); do + [[ "${COMPREPLY[i]}" != "${expected[i]}" ]] && return 1 + done + return 0 +} + +# Assert contains all specified values (order doesn't matter) +assert_contains() { + local expected=("$@") + + for exp in "${expected[@]}"; do + local found=false + for actual in "${COMPREPLY[@]}"; do + # Handle both plain flags and flags with descriptions (e.g., "-d" or "-d, --dry-run:(dry-run)") + if [[ "$actual" == "$exp" ]] || + [[ "$actual" == "$exp:"* ]] || + [[ "$actual" == "$exp, "* ]] || + [[ "$actual" == *", $exp:"* ]]; then + found=true + break + fi + done + [[ "$found" == false ]] && return 1 + done + return 0 +} + +# Assert excludes all specified values +assert_excludes() { + local excluded=("$@") + + for excl in "${excluded[@]}"; do + for actual in "${COMPREPLY[@]}"; do + # Handle both plain flags and flags with descriptions + if [[ "$actual" == "$excl" ]] || + [[ "$actual" == "$excl:"* ]] || + [[ "$actual" == "$excl, "* ]] || + [[ "$actual" == *", $excl:"* ]]; then + return 1 # Found excluded value + fi + done + done + return 0 +} + +# Assert count greater than +assert_count_gt() { + local threshold="$1" + [[ ${#COMPREPLY[@]} -gt $threshold ]] +} + +# Assert count equals +assert_count_eq() { + local expected="$1" + [[ ${#COMPREPLY[@]} -eq $expected ]] +} + +# Assert all completions match pattern +assert_all_match() { + local pattern="$1" + + [[ ${#COMPREPLY[@]} -eq 0 ]] && return 1 + + for completion in "${COMPREPLY[@]}"; do + case "$pattern" in + "*@*") + [[ "$completion" != *@* ]] && return 1 + ;; + "*/*") + [[ "$completion" != */* ]] && return 1 + ;; + "no_@_or_/") + [[ "$completion" == *@* ]] || [[ "$completion" == */* ]] && return 1 + ;; + *) + [[ ! "$completion" =~ $pattern ]] && return 1 + ;; + esac + done + return 0 +} + +# Assert any completion matches pattern +assert_any_match() { + local pattern="$1" + + for completion in "${COMPREPLY[@]}"; do + case "$pattern" in + "*@*") + [[ "$completion" == *@* ]] && return 0 + ;; + "*/*") + [[ "$completion" == */* ]] && return 0 + ;; + *) + [[ "$completion" =~ $pattern ]] && return 0 + ;; + esac + done + return 1 +} + +# ============================================================================ +# Test Suite Runner +# ============================================================================ + +# Run all tests from a test suite in JSON +# Args: $1 = suite name (e.g., "timeout_tests") +# $2 = path to JSON file +run_test_suite_json() { + local suite_name="$1" + local json_file="$2" + + # Check if jq is available + if ! command -v jq &> /dev/null; then + echo -e "${RED}Error: jq is required but not installed${NC}" + echo "Install with: sudo dnf install jq (or apt-get, brew, etc.)" + return 1 + fi + + # Read and parse test suite + local suite_tests + suite_tests=$(jq -c ".${suite_name}[]" "$json_file" 2>/dev/null) + + if [[ -z "$suite_tests" ]]; then + echo -e "${YELLOW}Warning: No tests found for suite '${suite_name}'${NC}" + return 0 + fi + + echo -e "\n${BLUE}=== $(echo "$suite_name" | sed 's/_/ /g' | sed 's/\b\(.\)/\u\1/g') ===${NC}" + + # Run each test + while IFS= read -r test_json; do + run_test_json "$test_json" + done <<< "$suite_tests" +} + +# Print final results +print_results() { + echo + echo "============================================" + echo "Test Results:" + echo " Total: $TESTS_RUN" + echo -e " ${GREEN}Passed: $TESTS_PASSED${NC}" + [[ $TESTS_FAILED -gt 0 ]] && echo -e " ${RED}Failed: $TESTS_FAILED${NC}" || echo " Failed: 0" + echo "============================================" + + [[ $TESTS_FAILED -eq 0 ]] +} diff --git a/completion/tests/test_incompatible_flags.sh b/completion/tests/test_incompatible_flags.sh deleted file mode 100755 index 9e7eb80..0000000 --- a/completion/tests/test_incompatible_flags.sh +++ /dev/null @@ -1,393 +0,0 @@ -#!/bin/bash - -# Test suite for incompatible flag filtering -# Tests that logically incompatible flags are excluded from completion suggestions - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]:0:5}..." - - return 0 -} - -# Helper function to check if a flag is excluded from completions -check_flag_excluded() { - local flag="$1" - for completion in "${COMPREPLY[@]}"; do - # Handle both standard format and enhanced format with descriptions - if [[ "$completion" == "$flag" ]] || [[ "$completion" == "$flag:"* ]]; then - return 1 # Flag found (not excluded) - fi - done - return 0 # Flag not found (excluded) -} - -# Helper function to check if a flag is included in completions -check_flag_included() { - local flag="$1" - for completion in "${COMPREPLY[@]}"; do - # Handle both standard format and combined format with descriptions - if [[ "$completion" == "$flag" ]] || - [[ "$completion" == "$flag:"* ]] || - [[ "$completion" == "$flag, "* ]] || - [[ "$completion" == *", $flag:"* ]]; then - return 0 # Flag found (included) - fi - done - return 1 # Flag not found (not included) -} - -echo "=== Incompatible Flags Test Suite ===" -echo - -# Test 1: CI Mode Incompatibilities -echo "=== Testing CI Mode Incompatibilities ===" - -run_completion_test \ - "CI mode excludes interactive flags" \ - "markdown-runner" "--view" "ci" "README.md" "-" - -if check_flag_excluded "-i" && check_flag_excluded "--interactive" && - check_flag_excluded "-B" && check_flag_excluded "--break-at" && - check_flag_excluded "-s" && check_flag_excluded "--start-from"; then - echo -e " ${GREEN}PASS: CI mode excludes interactive flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: CI mode should exclude interactive flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "CI mode allows non-interactive flags" \ - "markdown-runner" "--view" "ci" "README.md" "-" - -if check_flag_included "-d" && check_flag_included "--dry-run" && - check_flag_included "-l" && check_flag_included "--list"; then - echo -e " ${GREEN}PASS: CI mode allows non-interactive flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: CI mode should allow non-interactive flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Default View Mode (should not exclude interactive flags) -echo "=== Testing Default View Mode ===" - -run_completion_test \ - "Default view allows interactive flags" \ - "markdown-runner" "--view" "default" "README.md" "-" - -if check_flag_included "-i" && check_flag_included "--interactive" && - check_flag_included "-B" && check_flag_included "--break-at"; then - echo -e " ${GREEN}PASS: Default view allows interactive flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Default view should allow interactive flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: List Mode Incompatibilities -echo "=== Testing List Mode Incompatibilities ===" - -run_completion_test \ - "List mode excludes execution flags" \ - "markdown-runner" "-l" "README.md" "-" - -if check_flag_excluded "-i" && check_flag_excluded "--interactive" && - check_flag_excluded "-B" && check_flag_excluded "--break-at" && - check_flag_excluded "-s" && check_flag_excluded "--start-from" && - check_flag_excluded "-d" && check_flag_excluded "--dry-run" && - check_flag_excluded "-t" && check_flag_excluded "--timeout" && - check_flag_excluded "-u" && check_flag_excluded "--update-files"; then - echo -e " ${GREEN}PASS: List mode excludes execution flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: List mode should exclude execution flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "List mode allows compatible flags" \ - "markdown-runner" "-l" "README.md" "-" - -if check_flag_included "-r" && check_flag_included "--recursive" && - check_flag_included "-f" && check_flag_included "--filter" && - check_flag_included "-v" && check_flag_included "--verbose"; then - echo -e " ${GREEN}PASS: List mode allows compatible flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: List mode should allow compatible flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Help Mode Incompatibilities -echo "=== Testing Help Mode Incompatibilities ===" - -run_completion_test \ - "Help mode excludes all other flags" \ - "markdown-runner" "-h" "-" - -if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Help mode excludes all other flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Help mode should exclude all other flags, got ${#COMPREPLY[@]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Long help mode excludes all other flags" \ - "markdown-runner" "--help" "-" - -if [[ ${#COMPREPLY[@]} -eq 0 ]]; then - echo -e " ${GREEN}PASS: Long help mode excludes all other flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Long help mode should exclude all other flags, got ${#COMPREPLY[@]}${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Dry Run Incompatibilities -echo "=== Testing Dry Run Incompatibilities ===" - -run_completion_test \ - "Dry run excludes update-files" \ - "markdown-runner" "--dry-run" "README.md" "-" - -if check_flag_excluded "-u" && check_flag_excluded "--update-files"; then - echo -e " ${GREEN}PASS: Dry run excludes update-files${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Dry run should exclude update-files${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Dry run allows other flags" \ - "markdown-runner" "--dry-run" "README.md" "-" - -if check_flag_included "-i" && check_flag_included "--interactive" && - check_flag_included "-B" && check_flag_included "--break-at"; then - echo -e " ${GREEN}PASS: Dry run allows other compatible flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Dry run should allow other compatible flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Verbose vs Quiet Mutual Exclusion -echo "=== Testing Verbose vs Quiet Mutual Exclusion ===" - -run_completion_test \ - "Verbose excludes quiet" \ - "markdown-runner" "-v" "README.md" "-" - -if check_flag_excluded "-q" && check_flag_excluded "--quiet"; then - echo -e " ${GREEN}PASS: Verbose excludes quiet${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Verbose should exclude quiet${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Quiet excludes verbose" \ - "markdown-runner" "-q" "README.md" "-" - -if check_flag_excluded "-v" && check_flag_excluded "--verbose"; then - echo -e " ${GREEN}PASS: Quiet excludes verbose${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Quiet should exclude verbose${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Complex Combinations -echo "=== Testing Complex Combinations ===" - -run_completion_test \ - "Multiple incompatible contexts" \ - "markdown-runner" "-v" "--dry-run" "-l" "README.md" "-" - -# Should exclude: verbose+quiet, dry-run+update-files, list+execution flags -if check_flag_excluded "-q" && check_flag_excluded "--quiet" && - check_flag_excluded "-u" && check_flag_excluded "--update-files" && - check_flag_excluded "-i" && check_flag_excluded "-B" && check_flag_excluded "-s"; then - echo -e " ${GREEN}PASS: Multiple incompatible contexts handled correctly${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Multiple incompatible contexts should be handled${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 8: Bidirectional Incompatibilities -echo "=== Testing Bidirectional Incompatibilities ===" - -run_completion_test \ - "Interactive flag excludes CI and list modes" \ - "markdown-runner" "-i" "README.md" "-" - -if check_flag_excluded "--view" && check_flag_excluded "-l" && check_flag_excluded "--list" && - check_flag_excluded "-h" && check_flag_excluded "--help"; then - echo -e " ${GREEN}PASS: Interactive flag excludes CI and list modes${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Interactive flag should exclude CI and list modes${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Break-at flag excludes CI and list modes" \ - "markdown-runner" "-B" "help" "README.md" "-" - -if check_flag_excluded "--view" && check_flag_excluded "-l" && check_flag_excluded "--list" && - check_flag_excluded "-h" && check_flag_excluded "--help"; then - echo -e " ${GREEN}PASS: Break-at flag excludes CI and list modes${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Break-at flag should exclude CI and list modes${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from flag excludes CI and list modes" \ - "markdown-runner" "-s" "help" "README.md" "-" - -if check_flag_excluded "--view" && check_flag_excluded "-l" && check_flag_excluded "--list" && - check_flag_excluded "-h" && check_flag_excluded "--help"; then - echo -e " ${GREEN}PASS: Start-from flag excludes CI and list modes${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from flag should exclude CI and list modes${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Update-files flag excludes dry-run" \ - "markdown-runner" "-u" "README.md" "-" - -if check_flag_excluded "-d" && check_flag_excluded "--dry-run"; then - echo -e " ${GREEN}PASS: Update-files flag excludes dry-run${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Update-files flag should exclude dry-run${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Break-at flag excludes ignore-breakpoints" \ - "markdown-runner" "-B" "help" "README.md" "-" - -if check_flag_excluded "--ignore-breakpoints"; then - echo -e " ${GREEN}PASS: Break-at flag excludes ignore-breakpoints${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Break-at flag should exclude ignore-breakpoints${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Ignore-breakpoints flag excludes break-at" \ - "markdown-runner" "--ignore-breakpoints" "README.md" "-" - -if check_flag_excluded "-B" && check_flag_excluded "--break-at"; then - echo -e " ${GREEN}PASS: Ignore-breakpoints flag excludes break-at${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Ignore-breakpoints flag should exclude break-at${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 9: Edge Cases -echo "=== Testing Edge Cases ===" - -run_completion_test \ - "View flag without value (should not trigger CI exclusions)" \ - "markdown-runner" "--view" "README.md" "-" - -# Should not exclude interactive flags since no "ci" value specified -if check_flag_included "-i" && check_flag_included "-B"; then - echo -e " ${GREEN}PASS: View flag without CI value allows interactive flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: View flag without CI value should allow interactive flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "No incompatible flags used (baseline)" \ - "markdown-runner" "-r" "README.md" "-" - -# Should have normal exclusions (just -r and --recursive) -if [[ ${#COMPREPLY[@]} -eq 14 ]] && check_flag_excluded "-r" && check_flag_excluded "--recursive"; then - echo -e " ${GREEN}PASS: No incompatible flags shows normal exclusions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: No incompatible flags should show normal exclusions (expected 14, got ${#COMPREPLY[@]})${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_integration.sh b/completion/tests/test_integration.sh deleted file mode 100755 index 7572546..0000000 --- a/completion/tests/test_integration.sh +++ /dev/null @@ -1,74 +0,0 @@ -#!/bin/bash - -# Integration test for the specific issue that was reported -# Tests that "markdown-runner -B help" shows chunks, not just stage names - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -echo -e "${YELLOW}=== Integration Test: Stage Chunk Completion Fix ===${NC}" -echo - -echo "Issue: 'markdown-runner -B help' should list available chunks under help" -echo - -# Test the specific case mentioned in the issue -echo "Testing: markdown-runner -B help" -COMP_WORDS=("markdown-runner" "-B" "help") -COMP_CWORD=2 -COMP_LINE="markdown-runner -B help" -COMP_POINT=${#COMP_LINE} -COMPREPLY=() - -_markdown_runner_completion - -echo "Completions found: ${#COMPREPLY[@]}" -echo "Completions: ${COMPREPLY[@]}" - -# Verify the result -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "help/0" ]]; then - echo -e "${GREEN}✓ SUCCESS: help stage correctly shows chunk help/0${NC}" - echo - - # Test a few more cases to ensure robustness - echo "Additional verification tests:" - - # Test setup stage - echo "Testing: markdown-runner -B setup" - COMP_WORDS=("markdown-runner" "-B" "setup") - COMP_CWORD=2 - COMPREPLY=() - _markdown_runner_completion - - if [[ ${#COMPREPLY[@]} -eq 2 ]]; then - echo -e " ${GREEN}✓ setup stage shows 2 chunks: ${COMPREPLY[@]}${NC}" - else - echo -e " ${RED}✗ setup stage failed: ${COMPREPLY[@]}${NC}" - fi - - # Test partial stage name - echo "Testing: markdown-runner -B hel" - COMP_WORDS=("markdown-runner" "-B" "hel") - COMP_CWORD=2 - COMPREPLY=() - _markdown_runner_completion - - if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "help" ]]; then - echo -e " ${GREEN}✓ partial 'hel' correctly shows 'help' stage${NC}" - else - echo -e " ${RED}✗ partial completion failed: ${COMPREPLY[@]}${NC}" - fi - - echo - echo -e "${GREEN}🎉 Integration test PASSED! The autocompletion fix is working correctly.${NC}" - exit 0 -else - echo -e "${RED}✗ FAILED: Expected 'help/0', got: ${COMPREPLY[@]}${NC}" - echo -e "${RED}The autocompletion fix is not working correctly.${NC}" - exit 1 -fi diff --git a/completion/tests/test_main_file_completion.sh b/completion/tests/test_main_file_completion.sh deleted file mode 100755 index d725cae..0000000 --- a/completion/tests/test_main_file_completion.sh +++ /dev/null @@ -1,348 +0,0 @@ -#!/bin/bash - -# Test suite for main file completion -# Tests .md file completion, directory completion, and nospace behavior - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 -} - -# Helper function to check if all completions are .md files or directories -check_md_files_and_dirs() { - for completion in "${COMPREPLY[@]}"; do - # Should be either .md file or directory (ending with /) - if [[ "$completion" != *.md ]] && [[ "$completion" != */ ]]; then - return 1 - fi - done - return 0 -} - -# Helper function to check if any completion ends with / -check_has_directories() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == */ ]]; then - return 0 - fi - done - return 1 -} - -# Helper function to check if any completion is .md file -check_has_md_files() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == *.md ]]; then - return 0 - fi - done - return 1 -} - -echo "=== Main File Completion Test Suite ===" -echo - -# Test 1: Basic file completion (main argument) -echo "=== Testing Basic File Completion ===" - -run_completion_test \ - "Main file argument completion (empty)" \ - "markdown-runner" "" - -if check_md_files_and_dirs && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Main completion shows .md files and directories${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Main completion should show .md files and directories only${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Main file argument with README prefix" \ - "markdown-runner" "README" - -# Should complete to README.md -if [[ ${#COMPREPLY[@]} -ge 1 ]] && check_has_md_files; then - echo -e " ${GREEN}PASS: README prefix shows .md file completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: README prefix should show .md file completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Main file argument with .md extension" \ - "markdown-runner" "README.md" - -# Should complete to README.md exactly -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - local found_readme=false - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "README.md" ]]; then - found_readme=true - break - fi - done - if [[ "$found_readme" == true ]]; then - echo -e " ${GREEN}PASS: Complete .md filename found in completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - else - echo -e " ${RED}FAIL: Complete .md filename should be in completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - fi -else - echo -e " ${RED}FAIL: Complete .md filename should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Directory completion -echo "=== Testing Directory Completion ===" - -run_completion_test \ - "Directory prefix completion" \ - "markdown-runner" "test" - -if check_has_directories; then - echo -e " ${GREEN}PASS: Directory prefix shows directory completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory prefix should show directory completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Complete directory path" \ - "markdown-runner" "test/" - -# Should show contents of test/ directory -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Complete directory path shows contents (${#COMPREPLY[@]} items)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Complete directory path should show directory contents${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Nested directory completion" \ - "markdown-runner" "test/cases/" - -# Should show .md files in test/cases/ -if check_has_md_files && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Nested directory shows .md files (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Nested directory should show .md files${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: File filtering (only .md files, not other types) -echo "=== Testing File Filtering ===" - -run_completion_test \ - "File filtering (should exclude non-.md files)" \ - "markdown-runner" "go" - -# Should not show .go files, only directories or .md files -if check_md_files_and_dirs; then - echo -e " ${GREEN}PASS: File filtering excludes non-.md files${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should only show .md files and directories${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Mixed file and directory completion -echo "=== Testing Mixed File and Directory Completion ===" - -run_completion_test \ - "Mixed completion with common prefix" \ - "markdown-runner" "c" - -# Should show both completion/ directory and any .md files starting with 'c' -if [[ ${#COMPREPLY[@]} -gt 0 ]] && check_md_files_and_dirs; then - echo -e " ${GREEN}PASS: Mixed completion shows both files and directories${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Mixed completion should show files and directories${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Nospace behavior for directories -echo "=== Testing Nospace Behavior ===" - -# This test verifies that the completion function sets up nospace correctly -# We can't directly test compopt, but we can verify the logic - -run_completion_test \ - "Directory completion (should trigger nospace logic)" \ - "markdown-runner" "test" - -# Check if we have directories in the results (which should trigger nospace) -if check_has_directories; then - echo -e " ${GREEN}PASS: Directory completion includes directories (nospace should be triggered)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory completion should include directories${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Path traversal -echo "=== Testing Path Traversal ===" - -run_completion_test \ - "Deep path traversal" \ - "markdown-runner" "test/cases/recursive/" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Deep path traversal works (${#COMPREPLY[@]} items)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Deep path traversal should show contents${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Very deep path traversal" \ - "markdown-runner" "test/cases/recursive/nested/" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Very deep path traversal works (${#COMPREPLY[@]} items)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Very deep path traversal should show contents${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Non-existent paths -echo "=== Testing Non-existent Paths ===" - -run_completion_test \ - "Non-existent directory" \ - "markdown-runner" "nonexistent/" - -# Should show no completions or handle gracefully -echo -e " ${GREEN}PASS: Non-existent directory handled gracefully (${#COMPREPLY[@]} completions)${NC}" -TESTS_PASSED=$((TESTS_PASSED + 1)) -echo - -run_completion_test \ - "Non-existent file prefix" \ - "markdown-runner" "nonexistent" - -# Should show no completions or handle gracefully -echo -e " ${GREEN}PASS: Non-existent file prefix handled gracefully (${#COMPREPLY[@]} completions)${NC}" -TESTS_PASSED=$((TESTS_PASSED + 1)) -echo - -# Test 8: Edge cases -echo "=== Testing Edge Cases ===" - -run_completion_test \ - "Empty string completion" \ - "markdown-runner" "" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Empty string shows completions (${#COMPREPLY[@]} items)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Empty string should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Single character completion" \ - "markdown-runner" "R" - -if [[ ${#COMPREPLY[@]} -ge 0 ]]; then - echo -e " ${GREEN}PASS: Single character completion works (${#COMPREPLY[@]} items)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Single character completion should work${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 9: Case sensitivity -echo "=== Testing Case Sensitivity ===" - -run_completion_test \ - "Lowercase readme completion" \ - "markdown-runner" "readme" - -lowercase_count=${#COMPREPLY[@]} - -run_completion_test \ - "Uppercase README completion" \ - "markdown-runner" "README" - -uppercase_count=${#COMPREPLY[@]} - -# Both should work (bash completion is typically case-sensitive) -if [[ $uppercase_count -gt 0 ]]; then - echo -e " ${GREEN}PASS: Case sensitivity works as expected (lowercase: $lowercase_count, uppercase: $uppercase_count)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Case sensitivity should work${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_positional_arguments.sh b/completion/tests/test_positional_arguments.sh deleted file mode 100755 index bf01955..0000000 --- a/completion/tests/test_positional_arguments.sh +++ /dev/null @@ -1,334 +0,0 @@ -#!/bin/bash - -# Test suite for positional argument completion behavior -# Tests that after providing a markdown file, completion switches to flags - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]:0:5}..." - - return 0 -} - -# Helper function to check if completions are flags -check_all_flags() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != -* ]]; then - return 1 # Found a non-flag completion - fi - done - return 0 # All completions are flags -} - -# Helper function to check if completions are files/directories -check_files_or_dirs() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == *.md ]] || [[ "$completion" == */ ]] || [[ -f "$completion" ]] || [[ -d "$completion" ]]; then - return 0 # Found a file or directory - fi - done - return 1 # No files or directories found -} - -echo "=== Positional Arguments Test Suite ===" -echo - -# Test 1: No markdown file provided (should show files) -echo "=== Testing No Markdown File Provided ===" - -run_completion_test \ - "Empty completion (should show files)" \ - "markdown-runner" "" - -if check_files_or_dirs && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: No file provided shows files/directories${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: No file provided should show files/directories${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Partial file completion" \ - "markdown-runner" "README" - -if check_files_or_dirs && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Partial file completion shows files${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Partial file completion should show files${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Markdown file provided (should show flags) -echo "=== Testing Markdown File Provided ===" - -run_completion_test \ - "README.md provided (should show flags)" \ - "markdown-runner" "README.md" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Markdown file provided shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Markdown file provided should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "File with path provided (should show flags)" \ - "markdown-runner" "test/cases/parallel.md" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: File with path shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File with path should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Different markdown extension (.MD)" \ - "markdown-runner" "file.MD" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: .MD extension shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: .MD extension should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Different markdown extension (.Markdown)" \ - "markdown-runner" "file.Markdown" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: .Markdown extension shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: .Markdown extension should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Directory provided (should show flags, not more files) -echo "=== Testing Directory Provided ===" - -run_completion_test \ - "Directory provided (should show flags)" \ - "markdown-runner" "test/" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Directory provided shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory provided should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Specific directory (test/cases/) should show flags" \ - "markdown-runner" "test/cases/" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Specific directory shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Specific directory should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Nested directory should show flags" \ - "markdown-runner" "test/cases/recursive/" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Nested directory shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Nested directory should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: File + flags combination -echo "=== Testing File + Flags Combination ===" - -run_completion_test \ - "File + flag completion" \ - "markdown-runner" "README.md" "-v" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: File + flag shows more flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File + flag should show more flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Multiple flags + file" \ - "markdown-runner" "-v" "-d" "README.md" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Multiple flags + file shows flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Multiple flags + file should show flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Flag value completion (should not be affected) -echo "=== Testing Flag Value Completion ===" - -run_completion_test \ - "File + break-at flag value" \ - "markdown-runner" "README.md" "-B" "" - -# Should show stage names, not flags -if [[ ${#COMPREPLY[@]} -gt 0 ]] && ! check_all_flags; then - echo -e " ${GREEN}PASS: File + flag value shows flag-specific completion${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File + flag value should show flag-specific completion${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "File + timeout flag value" \ - "markdown-runner" "README.md" "-t" "" - -# Should show timeout values -expected_timeouts=("1" "5" "10" "30" "60") -has_timeout_values=false -for timeout in "${expected_timeouts[@]}"; do - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "$timeout" ]]; then - has_timeout_values=true - break 2 - fi - done -done - -if [[ "$has_timeout_values" == true ]]; then - echo -e " ${GREEN}PASS: File + timeout flag shows timeout values${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File + timeout flag should show timeout values${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Partial flag completion -echo "=== Testing Partial Flag Completion ===" - -run_completion_test \ - "File + partial flag" \ - "markdown-runner" "README.md" "-" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: File + partial flag shows all flags${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File + partial flag should show all flags${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "File + specific flag prefix" \ - "markdown-runner" "README.md" "--dry" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "--dry-run" ]]; then - echo -e " ${GREEN}PASS: File + specific flag prefix completes correctly${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File + specific flag prefix should complete to --dry-run${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Edge cases -echo "=== Testing Edge Cases ===" - -run_completion_test \ - "Non-existent markdown file" \ - "markdown-runner" "nonexistent.md" "" - -if check_all_flags && [[ ${#COMPREPLY[@]} -gt 10 ]]; then - echo -e " ${GREEN}PASS: Non-existent .md file still triggers flag mode${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Non-existent .md file should trigger flag mode${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "File with no extension" \ - "markdown-runner" "somefile" "" - -if check_files_or_dirs; then - echo -e " ${GREEN}PASS: File with no extension shows files (not flag mode)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: File with no extension should show files${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_recursive_behavior.sh b/completion/tests/test_recursive_behavior.sh deleted file mode 100755 index 2b5ee83..0000000 --- a/completion/tests/test_recursive_behavior.sh +++ /dev/null @@ -1,187 +0,0 @@ -#!/bin/bash - -# Test suite for recursive flag behavior in completion -# Tests the distinction between recursive and non-recursive completion modes - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - local expected_behavior="$2" - shift 2 - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - echo " Expected: $expected_behavior" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " First 10 completions: ${COMPREPLY[@]:0:10}" - - return 0 # We'll do manual verification for now -} - -# Helper function to check if all completions end with @ -check_all_file_at_completions() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != *@ ]]; then - return 1 # Found a completion that doesn't end with @ - fi - done - return 0 # All completions end with @ -} - -# Helper function to check if completions contain stage names (not ending with @) -check_contains_stage_names() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != *@ ]]; then - return 0 # Found a stage name - fi - done - return 1 # No stage names found -} - -echo "=== Recursive Behavior Test Suite ===" -echo - -# Test 1: Non-recursive should show stage names + file@ completions -run_completion_test \ - "Non-recursive -B should show stages + file@ completions" \ - "Mix of stage names and file@ completions" \ - "markdown-runner" "-B" "" - -if check_contains_stage_names && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Contains stage names (non-recursive behavior)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should contain stage names in non-recursive mode${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Recursive should show ONLY file@ completions -run_completion_test \ - "Recursive -B should show ONLY file@ completions" \ - "Only file@ completions, no stage names" \ - "markdown-runner" "-r" "-B" "" - -if check_all_file_at_completions && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: All completions are file@ format (recursive behavior)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should only show file@ completions in recursive mode${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Non-recursive with partial stage name should work -run_completion_test \ - "Non-recursive partial stage name" \ - "Matching stage names" \ - "markdown-runner" "-B" "hel" - -if [[ ${#COMPREPLY[@]} -gt 0 ]] && ! check_all_file_at_completions; then - echo -e " ${GREEN}PASS: Shows matching stage names for partial input${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should show matching stage names for partial input${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Recursive with partial should show only matching file@ completions -run_completion_test \ - "Recursive partial should filter file@ completions" \ - "Only file@ completions matching the partial input" \ - "markdown-runner" "-r" "-B" "README" - -# For this test, we expect either 0 completions (no files match) or file@ completions -if [[ ${#COMPREPLY[@]} -eq 0 ]] || check_all_file_at_completions; then - echo -e " ${GREEN}PASS: Recursive partial shows only file@ completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Recursive partial should only show file@ completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: --recursive long form should work the same as -r -run_completion_test \ - "Long form --recursive should work like -r" \ - "Only file@ completions, no stage names" \ - "markdown-runner" "--recursive" "-B" "" - -if check_all_file_at_completions && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: --recursive works the same as -r${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: --recursive should work the same as -r${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Recursive with -s flag should also work -run_completion_test \ - "Recursive -s should show ONLY file@ completions" \ - "Only file@ completions, no stage names" \ - "markdown-runner" "-r" "-s" "" - -if check_all_file_at_completions && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Recursive -s shows only file@ completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Recursive -s should only show file@ completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Directory + recursive should show only file@ completions from that directory -run_completion_test \ - "Directory + recursive should show ONLY file@ from directory" \ - "Only file@ completions from specified directory" \ - "markdown-runner" "test/cases/" "-r" "-B" "" - -if check_all_file_at_completions && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Directory + recursive shows only file@ completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Directory + recursive should only show file@ completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_smart_nospace.sh b/completion/tests/test_smart_nospace.sh deleted file mode 100755 index 801b2b7..0000000 --- a/completion/tests/test_smart_nospace.sh +++ /dev/null @@ -1,178 +0,0 @@ -#!/bin/bash - -# Test suite for smart nospace behavior -# Tests that single-chunk stages allow trailing spaces while multi-chunk stages use nospace - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 -} - -# Helper function to count chunks for a stage -count_chunks_for_stage() { - local stage="$1" - local md_files - md_files=$(_get_executable_files | tr '\n' ' ') - - if [[ -n "$md_files" ]]; then - # Use the same regex pattern as the actual completion code - echo "$md_files" | xargs grep -h '```[a-zA-Z0-9_. -]*{.*"stage"[[:space:]]*:[[:space:]]*"'"$stage"'"' 2>/dev/null | wc -l - else - echo "0" - fi -} - -echo "=== Smart Nospace Behavior Test Suite ===" -echo - -# Test 1: Single chunk stage completion (should allow trailing space) -echo "=== Testing Single Chunk Stages (should allow trailing space) ===" - -run_completion_test \ - "help stage (1 chunk) - partial completion" \ - "markdown-runner" "-B" "hel" - -help_chunks=$(count_chunks_for_stage "help") -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "help" ]] && [[ $help_chunks -eq 1 ]]; then - echo -e " ${GREEN}PASS: Single chunk stage should allow trailing space${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected single completion 'help' for single chunk stage (chunks: $help_chunks)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "test stage (1 chunk) - partial completion" \ - "markdown-runner" "-B" "tes" - -# Check if test is in the results and has 1 chunk -test_chunks=$(count_chunks_for_stage "test") -has_test_stage=false -for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "test" ]]; then - has_test_stage=true - break - fi -done - -if [[ "$has_test_stage" == true ]] && [[ $test_chunks -eq 1 ]]; then - echo -e " ${GREEN}PASS: test stage (1 chunk) present in multi-completion${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: test stage should be present with 1 chunk (chunks: $test_chunks)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Multi chunk stage completion (should use nospace) -echo "=== Testing Multi Chunk Stages (should use nospace) ===" - -run_completion_test \ - "setup stage (2 chunks) - partial completion" \ - "markdown-runner" "-B" "setu" - -setup_chunks=$(count_chunks_for_stage "setup") -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "setup" ]] && [[ $setup_chunks -gt 1 ]]; then - echo -e " ${GREEN}PASS: Multi chunk stage should use nospace (chunks: $setup_chunks)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Expected single completion 'setup' for multi chunk stage (chunks: $setup_chunks)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Mixed completion (should use nospace if ANY stage has multiple chunks) -echo "=== Testing Mixed Completions (should use nospace if any multi-chunk) ===" - -run_completion_test \ - "Mixed stages with different chunk counts" \ - "markdown-runner" "-B" "te" - -# Check chunk counts for stages starting with "te" -has_multi_chunk=false -for completion in "${COMPREPLY[@]}"; do - chunk_count=$(count_chunks_for_stage "$completion") - if [[ $chunk_count -gt 1 ]]; then - has_multi_chunk=true - break - fi -done - -if [[ "$has_multi_chunk" == true ]]; then - echo -e " ${GREEN}PASS: Mixed completion should use nospace (has multi-chunk stages)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Mixed completion should use nospace when any stage has multiple chunks${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Verify chunk counting logic -echo "=== Testing Chunk Counting Logic ===" - -echo "Chunk counts for key stages:" -for stage in "help" "setup" "test" "main"; do - chunk_count=$(count_chunks_for_stage "$stage") - echo " $stage: $chunk_count chunks" -done - -# Verify our assumptions about chunk counts -help_chunks=$(count_chunks_for_stage "help") -setup_chunks=$(count_chunks_for_stage "setup") - -if [[ $help_chunks -eq 1 ]] && [[ $setup_chunks -gt 1 ]]; then - echo -e " ${GREEN}PASS: Chunk counting logic works correctly${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Chunk counting logic incorrect (help: $help_chunks, setup: $setup_chunks)${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_specific_file_completion.sh b/completion/tests/test_specific_file_completion.sh deleted file mode 100755 index 9ae70d5..0000000 --- a/completion/tests/test_specific_file_completion.sh +++ /dev/null @@ -1,141 +0,0 @@ -#!/bin/bash - -# Test suite for specific file completion functionality -# Tests that when a specific markdown file is provided, completion only shows stages from that file - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - COMP_LINE="${COMP_WORDS[*]}" - COMP_POINT=${#COMP_LINE} - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 # We'll do manual verification for now -} - -# Helper function to check if completions contain a specific item -check_completion_contains() { - local item="$1" - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "$item" ]]; then - return 0 - fi - done - return 1 -} - -# Helper function to check if completions do NOT contain a specific item -check_completion_not_contains() { - local item="$1" - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "$item" ]]; then - return 1 - fi - done - return 0 -} - -echo "=== Specific File Completion Test Suite ===" -echo - -# Test 1: Specific file should only show stages from that file (NOT README.md@) -run_completion_test \ - "Specific file should NOT include README.md@" \ - "markdown-runner" "test/cases/parallel.md" "-B" "" - -if check_completion_not_contains "README.md@" && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Does not include README.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should not include README.md@${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: No specific file should NOT include file@ completions (only stage names) -run_completion_test \ - "No specific file should NOT include README.md@" \ - "markdown-runner" "-B" "" - -if check_completion_not_contains "README.md@" && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Does not include README.md@ when no specific file${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should not include README.md@ when no specific file${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: Directory context should show file@ completions -run_completion_test \ - "Directory context should show file@ completions" \ - "markdown-runner" "test/cases/" "-B" "" - -if check_completion_contains "parallel.md@" && check_completion_contains "happy.md@"; then - echo -e " ${GREEN}PASS: Shows file@ completions for directory${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Should show file@ completions for directory${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Specific file with different extensions -if [[ -f "test/cases/happy.md" ]]; then - run_completion_test \ - "Different specific file should not include README.md@" \ - "markdown-runner" "test/cases/happy.md" "-B" "" - - if check_completion_not_contains "README.md@" && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Different file does not include README.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - else - echo -e " ${RED}FAIL: Different file should not include README.md@${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - fi - echo -fi - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_stage_chunk_completion.sh b/completion/tests/test_stage_chunk_completion.sh deleted file mode 100755 index 439f53a..0000000 --- a/completion/tests/test_stage_chunk_completion.sh +++ /dev/null @@ -1,163 +0,0 @@ -#!/bin/bash - -# Test suite for stage chunk completion functionality -# Tests the ability to complete stage names to show chunks within those stages - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - local comp_line="$2" - local expected_count="$3" - local expected_completions="$4" # Space-separated list of expected completions - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " Command: $comp_line" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Parse the completion line - IFS=' ' read -ra COMP_WORDS <<< "$comp_line" - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - COMP_LINE="$comp_line" - COMP_POINT=${#COMP_LINE} - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - # Check results - local completion_count=${#COMPREPLY[@]} - echo " Found $completion_count completions" - - if [[ $completion_count -ne $expected_count ]]; then - echo -e " ${RED}FAIL: Expected $expected_count completions, got $completion_count${NC}" - echo " Completions found: ${COMPREPLY[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - - # Check if expected completions are present - if [[ -n "$expected_completions" ]]; then - IFS=' ' read -ra expected_array <<< "$expected_completions" - for expected in "${expected_array[@]}"; do - local found=false - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == "$expected" ]]; then - found=true - break - fi - done - - if [[ "$found" == false ]]; then - echo -e " ${RED}FAIL: Expected completion '$expected' not found${NC}" - echo " Completions found: ${COMPREPLY[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - return 1 - fi - done - fi - - echo " Completions: ${COMPREPLY[*]}" - echo -e " ${GREEN}PASS${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - return 0 -} - -echo "=== Stage Chunk Completion Test Suite ===" -echo - -# Test 1: Exact stage names should show chunks -run_completion_test \ - "help stage shows chunks" \ - "markdown-runner -B help" \ - 1 \ - "help/0" - -run_completion_test \ - "setup stage shows chunks" \ - "markdown-runner -B setup" \ - 2 \ - "setup/init setup/1" - -run_completion_test \ - "main stage shows chunks" \ - "markdown-runner -B main" \ - 1 \ - "main/process" - -run_completion_test \ - "test stage shows chunks" \ - "markdown-runner -B test" \ - 1 \ - "test/0" - -# Test 2: Partial stage names should show matching stage names -run_completion_test \ - "partial 'hel' shows help stage" \ - "markdown-runner -B hel" \ - 1 \ - "help" - -run_completion_test \ - "partial 'se' shows setup stage" \ - "markdown-runner -B se" \ - 1 \ - "setup" - -run_completion_test \ - "partial 'tes' shows test stages" \ - "markdown-runner -B tes" \ - 6 \ - "test test1 test2 test3 test4 test5" - -# Test 3: Existing functionality should still work -run_completion_test \ - "file@stage format shows chunks for complete stage" \ - "markdown-runner -B README.md@setup" \ - 2 \ - "README.md@setup/init README.md@setup/1" - -run_completion_test \ - "stage/chunk format still works" \ - "markdown-runner -B setup/" \ - 2 \ - "setup/init setup/1" - -run_completion_test \ - "file@stage/chunk format still works" \ - "markdown-runner -B README.md@setup/" \ - 2 \ - "README.md@setup/init README.md@setup/1" - -echo -# Note: Stage name completions use smart nospace behavior: -# - Single chunk stages: Allow trailing space (no need to chain) -# - Multiple chunk stages: Use nospace to enable chaining -echo "Note: Smart nospace - single chunk stages get trailing space, multi-chunk stages enable chaining" - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_stage_validation.sh b/completion/tests/test_stage_validation.sh deleted file mode 100755 index 1fb85f8..0000000 --- a/completion/tests/test_stage_validation.sh +++ /dev/null @@ -1,125 +0,0 @@ -#!/bin/bash - -# Test suite for stage validation functionality -# Tests the _is_valid_stage_name function - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to test stage validation -test_stage_validation() { - local test_name="$1" - local stage_name="$2" - local expected_result="$3" # "valid" or "invalid" - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " Stage name: '$stage_name'" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up minimal completion context - COMP_WORDS=("markdown-runner" "-B" "$stage_name") - COMP_CWORD=2 - - # Call the function - local result - if _is_valid_stage_name "$stage_name"; then - result="valid" - else - result="invalid" - fi - - echo " Result: $result" - echo " Expected: $expected_result" - - if [[ "$result" == "$expected_result" ]]; then - echo -e " ${GREEN}PASS${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) - else - echo -e " ${RED}FAIL${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) - fi - echo -} - -echo "=== Stage Validation Test Suite ===" -echo - -# Test 1: Valid stage names -test_stage_validation \ - "help is valid stage" \ - "help" \ - "valid" - -test_stage_validation \ - "setup is valid stage" \ - "setup" \ - "valid" - -test_stage_validation \ - "main is valid stage" \ - "main" \ - "valid" - -test_stage_validation \ - "test is valid stage" \ - "test" \ - "valid" - -test_stage_validation \ - "test1 is valid stage" \ - "test1" \ - "valid" - -test_stage_validation \ - "teardown is valid stage" \ - "teardown" \ - "valid" - -test_stage_validation \ - "integration-test is valid stage" \ - "integration-test" \ - "valid" - -# Test 2: Invalid stage names -test_stage_validation \ - "nonexistent is invalid stage" \ - "nonexistent" \ - "invalid" - -test_stage_validation \ - "partial hel is invalid stage" \ - "hel" \ - "invalid" - -test_stage_validation \ - "partial tes is invalid stage" \ - "tes" \ - "invalid" - -test_stage_validation \ - "empty string is invalid stage" \ - "" \ - "invalid" - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi diff --git a/completion/tests/test_start_from_completion.sh b/completion/tests/test_start_from_completion.sh deleted file mode 100755 index eb64ab5..0000000 --- a/completion/tests/test_start_from_completion.sh +++ /dev/null @@ -1,383 +0,0 @@ -#!/bin/bash - -# Test suite for -s/--start-from flag completion -# Tests that start-from behaves similarly to break-at but may have subtle differences - -source "$(dirname "$0")/../bash_completion.sh" - -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Test counters -TESTS_RUN=0 -TESTS_PASSED=0 -TESTS_FAILED=0 - -# Helper function to run a completion test -run_completion_test() { - local test_name="$1" - shift - local comp_words=("$@") - - echo -e "${YELLOW}Testing: $test_name${NC}" - echo " COMP_WORDS: ${comp_words[*]}" - - TESTS_RUN=$((TESTS_RUN + 1)) - - # Set up completion variables - COMP_WORDS=("${comp_words[@]}") - COMP_CWORD=$((${#COMP_WORDS[@]} - 1)) - - # Clear previous results - COMPREPLY=() - - # Run completion - _markdown_runner_completion - - echo " Completions found: ${#COMPREPLY[@]}" - echo " Completions: ${COMPREPLY[*]}" - - return 0 -} - -# Helper function to check if all completions are file@ format -check_all_file_at() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != *@* ]]; then - return 1 # Found a non-file@ completion - fi - done - return 0 # All completions are file@ format -} - -# Helper function to check if all completions are stage names (no @ or /) -check_all_stage_names() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" == *@* ]] || [[ "$completion" == */* ]]; then - return 1 # Found a file@ or chunk completion - fi - done - return 0 # All completions are stage names -} - -# Helper function to check if all completions are chunks (contain /) -check_all_chunks() { - for completion in "${COMPREPLY[@]}"; do - if [[ "$completion" != */* ]]; then - return 1 # Found a non-chunk completion - fi - done - return 0 # All completions are chunks -} - -echo "=== Start-From Flag Completion Test Suite ===" -echo - -# Test 1: Basic start-from completion (should be similar to break-at) -echo "=== Testing Basic Start-From Completion ===" - -run_completion_test \ - "Start-from empty completion" \ - "markdown-runner" "-s" "" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from empty completion shows results (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from empty completion should show results${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from long form empty completion" \ - "markdown-runner" "--start-from" "" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Long start-from empty completion shows results (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Long start-from empty completion should show results${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 2: Stage name completion -echo "=== Testing Stage Name Completion ===" - -run_completion_test \ - "Start-from stage completion 'help'" \ - "markdown-runner" "-s" "help" - -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Start-from stage 'help' shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from stage 'help' should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from partial stage 'hel'" \ - "markdown-runner" "-s" "hel" - -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Start-from partial 'hel' shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from partial 'hel' should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from stage completion 'setup'" \ - "markdown-runner" "-s" "setup" - -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Start-from stage 'setup' shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from stage 'setup' should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 3: File@stage completion -echo "=== Testing File@Stage Completion ===" - -run_completion_test \ - "Start-from file@stage empty" \ - "markdown-runner" "-s" "README.md@" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from README.md@ shows stages (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from README.md@ should show stages${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from file@stage partial" \ - "markdown-runner" "-s" "README.md@hel" - -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Start-from README.md@hel shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from README.md@hel should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from file@stage complete" \ - "markdown-runner" "-s" "README.md@help" - -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Start-from README.md@help shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from README.md@help should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 4: Directory context (recursive mode) -echo "=== Testing Directory Context ===" - -run_completion_test \ - "Start-from recursive mode" \ - "markdown-runner" "-r" "-s" "" - -if check_all_file_at && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from recursive shows file@ completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -elif [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from recursive shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from recursive should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from directory context" \ - "markdown-runner" "test/cases/" "-s" "" - -if check_all_file_at && [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from directory shows file@ completions${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -elif [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from directory shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from directory should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from directory partial" \ - "markdown-runner" "test/cases/" "-s" "p" - -if [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ "${COMPREPLY[0]}" == "parallel.md@" ]]; then - echo -e " ${GREEN}PASS: Start-from directory partial 'p' shows parallel.md@${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -elif [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from directory partial 'p' shows completions: ${COMPREPLY[*]}${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from directory partial 'p' should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 5: Comparison with break-at (equivalence test) -echo "=== Testing Start-From vs Break-At Equivalence ===" - -# Test same scenario with both flags -run_completion_test \ - "Break-at help completion" \ - "markdown-runner" "-B" "help" - -break_at_result=(${COMPREPLY[@]}) - -run_completion_test \ - "Start-from help completion" \ - "markdown-runner" "-s" "help" - -start_from_result=(${COMPREPLY[@]}) - -if [[ ${#break_at_result[@]} -eq ${#start_from_result[@]} ]] && [[ ${#break_at_result[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from and break-at produce similar results for 'help'${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from and break-at should produce similar results${NC}" - echo " Break-at: ${break_at_result[*]}" - echo " Start-from: ${start_from_result[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test file@stage equivalence -run_completion_test \ - "Break-at file@stage" \ - "markdown-runner" "-B" "README.md@help" - -break_at_file_result=(${COMPREPLY[@]}) - -run_completion_test \ - "Start-from file@stage" \ - "markdown-runner" "-s" "README.md@help" - -start_from_file_result=(${COMPREPLY[@]}) - -if [[ ${#break_at_file_result[@]} -eq ${#start_from_file_result[@]} ]] && [[ ${#break_at_file_result[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from and break-at produce similar results for file@stage${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from and break-at should produce similar results for file@stage${NC}" - echo " Break-at: ${break_at_file_result[*]}" - echo " Start-from: ${start_from_file_result[*]}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 6: Specific file context -echo "=== Testing Specific File Context ===" - -run_completion_test \ - "Start-from with specific file" \ - "markdown-runner" "README.md" "-s" "" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from with specific file shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from with specific file should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from with specific file and stage" \ - "markdown-runner" "README.md" "-s" "help" - -if [[ ${#COMPREPLY[@]} -ge 1 ]]; then - echo -e " ${GREEN}PASS: Start-from with specific file and stage shows completions (${#COMPREPLY[@]} found)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from with specific file and stage should show completions${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -# Test 7: Edge cases -echo "=== Testing Edge Cases ===" - -run_completion_test \ - "Start-from non-existent stage" \ - "markdown-runner" "-s" "nonexistent" - -# Should handle gracefully (may show no completions) -echo -e " ${GREEN}PASS: Start-from non-existent stage handled gracefully (${#COMPREPLY[@]} completions)${NC}" -TESTS_PASSED=$((TESTS_PASSED + 1)) -echo - -run_completion_test \ - "Start-from non-existent file@stage" \ - "markdown-runner" "-s" "nonexistent.md@stage" - -# Should handle gracefully -echo -e " ${GREEN}PASS: Start-from non-existent file@stage handled gracefully (${#COMPREPLY[@]} completions)${NC}" -TESTS_PASSED=$((TESTS_PASSED + 1)) -echo - -# Test 8: Complex scenarios -echo "=== Testing Complex Scenarios ===" - -run_completion_test \ - "Start-from with multiple flags" \ - "markdown-runner" "-r" "-v" "-s" "" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from with multiple flags works (${#COMPREPLY[@]} completions)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from with multiple flags should work${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -run_completion_test \ - "Start-from with directory and multiple flags" \ - "markdown-runner" "-r" "test/cases/" "-s" "p" - -if [[ ${#COMPREPLY[@]} -gt 0 ]]; then - echo -e " ${GREEN}PASS: Start-from complex scenario works (${#COMPREPLY[@]} completions)${NC}" - TESTS_PASSED=$((TESTS_PASSED + 1)) -else - echo -e " ${RED}FAIL: Start-from complex scenario should work${NC}" - TESTS_FAILED=$((TESTS_FAILED + 1)) -fi -echo - -echo "=== Test Results ===" -echo "Tests run: $TESTS_RUN" -echo -e "Tests passed: ${GREEN}$TESTS_PASSED${NC}" -if [[ $TESTS_FAILED -gt 0 ]]; then - echo -e "Tests failed: ${RED}$TESTS_FAILED${NC}" - exit 1 -else - echo -e "Tests failed: ${GREEN}0${NC}" - echo -e "${GREEN}All tests passed!${NC}" - exit 0 -fi