clean up repo
Authored by
mfwolffe <wolffemf@dukes.jmu.edu>
- SHA
661806ae003f4ed8d077c47d2a693b4c1d586170- Parents
-
58b54e1 - Tree
d46f8b7
661806a
661806ae003f4ed8d077c47d2a693b4c1d58617058b54e1
d46f8b7.gitignoremodified@@ -1,2 +1,3 @@ | ||
| 1 | 1 | bin/ |
| 2 | 2 | build/ |
| 3 | +docs/ | |
fortsh-1.0.0.tar.gz → Archive/fortsh-1.0.0.tar.gzrenamed (100% similarity)fortsh-1.0.1.tar.gz → Archive/fortsh-1.0.1.tar.gzrenamed (100% similarity)fortsh-1.0.5.tar.gz → Archive/fortsh-1.0.5.tar.gzrenamed (100% similarity)fortsh-1.1.0.tar.gz → Archive/fortsh-1.1.0.tar.gzrenamed (100% similarity)fortsh-1.2.0.tar.gz → Archive/fortsh-1.2.0.tar.gzrenamed (100% similarity)fortsh-1.3.0.tar.gz → Archive/fortsh-1.3.0.tar.gzrenamed (100% similarity)fortsh-1.4.0.tar.gz → Archive/fortsh-1.4.0.tar.gzrenamed (100% similarity)fortsh-1.5.0.tar.gz → Archive/fortsh-1.5.0.tar.gzrenamed (100% similarity)fortsh-2.0.0.tar.gz → Archive/fortsh-2.0.0.tar.gzrenamed (100% similarity)ROADMAP.mddeleted@@ -1,207 +0,0 @@ | ||
| 1 | -# FortSH Development Roadmap | |
| 2 | - | |
| 3 | -## Implementation Phases | |
| 4 | - | |
| 5 | -### ✅ Phase 1: Core Shell Foundation | |
| 6 | -**Status:** Complete | |
| 7 | -- Basic REPL (Read-Eval-Print Loop) | |
| 8 | -- Command tokenization and parsing | |
| 9 | -- External command execution via fork/exec | |
| 10 | -- Basic built-in commands (cd, pwd, exit) | |
| 11 | -- Environment variable handling | |
| 12 | - | |
| 13 | -### ✅ Phase 2: Built-in Commands | |
| 14 | -**Status:** Complete | |
| 15 | -- POSIX built-ins (echo, printf, test, export, unset, etc.) | |
| 16 | -- Variable assignment and expansion | |
| 17 | -- Positional parameters ($1, $2, $@, $*, $#) | |
| 18 | -- Special parameters ($$, $?, $!) | |
| 19 | - | |
| 20 | -### ✅ Phase 3: I/O Redirection | |
| 21 | -**Status:** Complete | |
| 22 | -- Input redirection (<) | |
| 23 | -- Output redirection (>, >>) | |
| 24 | -- Error redirection (2>, 2>>) | |
| 25 | -- Here documents (<<, <<-) | |
| 26 | -- Here strings (<<<) | |
| 27 | -- File descriptor manipulation | |
| 28 | - | |
| 29 | -### ✅ Phase 4: Pipelines | |
| 30 | -**Status:** Complete | |
| 31 | -- Basic pipe operator (|) | |
| 32 | -- Multi-stage pipelines | |
| 33 | -- Proper process group management | |
| 34 | -- Exit status handling | |
| 35 | - | |
| 36 | -### ✅ Phase 5: Job Control | |
| 37 | -**Status:** Complete | |
| 38 | -- Background processes (&) | |
| 39 | -- Job listing (jobs) | |
| 40 | -- Foreground/background control (fg, bg) | |
| 41 | -- Process groups and terminal control | |
| 42 | -- Signal handling (SIGINT, SIGTSTP, etc.) | |
| 43 | - | |
| 44 | -### ✅ Phase 6: Control Structures (Basic) | |
| 45 | -**Status:** Complete | |
| 46 | -- if/then/elif/else/fi | |
| 47 | -- case/esac | |
| 48 | -- Basic for loops | |
| 49 | -- while/until loops (structure only) | |
| 50 | - | |
| 51 | -### ✅ Phase 7: Advanced Expansion | |
| 52 | -**Status:** Complete | |
| 53 | -- Command substitution ($(...) and backticks) | |
| 54 | -- Arithmetic expansion ($((expr))) | |
| 55 | -- Brace expansion ({a,b,c}) | |
| 56 | -- Tilde expansion (~, ~user) | |
| 57 | -- Parameter expansion (${var:-default}, ${var##pattern}, etc.) | |
| 58 | - | |
| 59 | -### ✅ Phase 8: Pattern Matching & Globbing | |
| 60 | -**Status:** Complete | |
| 61 | -- Pathname expansion (*, ?, [...]) | |
| 62 | -- Extended globbing patterns | |
| 63 | -- Case statement patterns | |
| 64 | - | |
| 65 | -### ✅ Phase 9: Functions & Scripts | |
| 66 | -**Status:** Complete | |
| 67 | -- Shell functions | |
| 68 | -- Function parameters and local variables | |
| 69 | -- Script sourcing (source, .) | |
| 70 | -- Shebang support | |
| 71 | - | |
| 72 | -### ✅ Phase 10: POSIX Compliance | |
| 73 | -**Status:** Complete | |
| 74 | -- Shell options (set -e, set -u, set -x, etc.) | |
| 75 | -- POSIX test operators | |
| 76 | -- Proper exit status handling | |
| 77 | -- Field splitting (IFS) | |
| 78 | -- Quoting and escaping | |
| 79 | - | |
| 80 | -### ✅ Phase 11: Interactive Features | |
| 81 | -**Status:** Complete | |
| 82 | -- Command line editing | |
| 83 | -- History (arrow keys, history expansion) | |
| 84 | -- Tab completion | |
| 85 | -- Prompt customization (PS1, PS2) | |
| 86 | -- Readline-style keybindings | |
| 87 | - | |
| 88 | -### ✅ Phase 12: Advanced Features | |
| 89 | -**Status:** Complete | |
| 90 | -- Arrays (indexed and associative) | |
| 91 | -- Coprocesses | |
| 92 | -- Process substitution | |
| 93 | -- Advanced redirections (&>, >&2, etc.) | |
| 94 | -- Multiple redirections | |
| 95 | - | |
| 96 | -### ✅ Phase 13: Loop Execution | |
| 97 | -**Status:** Complete (with known limitations) | |
| 98 | -- Loop body buffering and replay | |
| 99 | -- For loop iteration (for x in list) | |
| 100 | -- Arithmetic for loops (for((i=0;i<n;i++))) | |
| 101 | -- While/until loop execution | |
| 102 | -- Break and continue statements | |
| 103 | - | |
| 104 | -**Known Issues:** | |
| 105 | -- Requires `for((` syntax (no space between for and (()) | |
| 106 | -- Variable scoping issues in sequential arithmetic loops | |
| 107 | -- Limited support for variable expansion in quoted strings within loops | |
| 108 | - | |
| 109 | -### ✅ Phase 14: Variable Scoping & Expansion Fixes | |
| 110 | -**Status:** Complete | |
| 111 | -- Fixed arithmetic variable persistence across loops | |
| 112 | -- Fixed loop body cleanup to prevent command replay issues | |
| 113 | -- Sequential loops now work correctly with proper variable isolation | |
| 114 | -- Variable expansion in quoted strings works correctly | |
| 115 | - | |
| 116 | -**Achievements:** | |
| 117 | -- Loop body commands are properly captured and replayed | |
| 118 | -- Variables from different loops no longer interfere with each other | |
| 119 | -- Both basic for loops and arithmetic for loops execute correctly | |
| 120 | -- Sequential loops maintain proper variable scope | |
| 121 | -- Arithmetic for loops support both `for((` and `for ((` syntax (POSIX-compliant) | |
| 122 | - | |
| 123 | -**Known Limitations:** | |
| 124 | -- Nested loops do not execute correctly (inner loops execute after outer loop completes) - architectural limitation requiring significant refactoring | |
| 125 | -- Loop variables persist after loop execution (POSIX-compliant behavior) | |
| 126 | - | |
| 127 | -### 📋 Phase 15: Advanced Loop Features | |
| 128 | -**Status:** Partially Complete | |
| 129 | - | |
| 130 | -**Implemented:** | |
| 131 | -- Break and continue builtins (code complete but non-functional due to architectural limitations) | |
| 132 | - | |
| 133 | -**Known Limitations:** | |
| 134 | -- Break/continue don't work correctly because loop body capture only captures the first command | |
| 135 | -- Nested loops remain architecturally limited | |
| 136 | -- These issues stem from the single-pass, line-by-line execution model | |
| 137 | - | |
| 138 | -**Not Implemented:** | |
| 139 | -- Select loops (would require interactive menu system) | |
| 140 | -- Loop labels for break/continue (blocked by basic break/continue not working) | |
| 141 | - | |
| 142 | -### 📋 Phase 16: Debugging & Tracing | |
| 143 | -**Status:** Planned | |
| 144 | -- set -x tracing with PS4 | |
| 145 | -- trap command | |
| 146 | -- DEBUG trap | |
| 147 | -- ERR trap | |
| 148 | -- Execution profiling | |
| 149 | - | |
| 150 | -### 📋 Phase 17: Performance & Optimization | |
| 151 | -**Status:** Planned | |
| 152 | -- Command caching | |
| 153 | -- Optimized tokenizer | |
| 154 | -- Memory pool management | |
| 155 | -- Parallel execution optimization | |
| 156 | - | |
| 157 | -### 📋 Phase 18: Extended Compatibility | |
| 158 | -**Status:** Planned | |
| 159 | -- Bash compatibility mode | |
| 160 | -- Zsh-style features (optional) | |
| 161 | -- POSIX strict mode | |
| 162 | -- Legacy sh compatibility | |
| 163 | - | |
| 164 | -### 📋 Phase 19: Advanced I/O | |
| 165 | -**Status:** Planned | |
| 166 | -- Network redirections (/dev/tcp, /dev/udp) | |
| 167 | -- Named pipes (FIFOs) | |
| 168 | -- Advanced coprocess features | |
| 169 | -- Multiplexed I/O | |
| 170 | - | |
| 171 | -### 📋 Phase 20: Security & Sandboxing | |
| 172 | -**Status:** Planned | |
| 173 | -- Restricted shell mode | |
| 174 | -- Capability-based security | |
| 175 | -- Resource limits enforcement | |
| 176 | -- Audit logging | |
| 177 | - | |
| 178 | -## Version Milestones | |
| 179 | - | |
| 180 | -### v1.0 - POSIX Foundation | |
| 181 | -Phases 1-10: Core POSIX-compliant shell | |
| 182 | - | |
| 183 | -### v2.0 - Interactive Shell | |
| 184 | -Phases 11-12: Full interactive shell with advanced features | |
| 185 | - | |
| 186 | -### v3.0 - Production Ready | |
| 187 | -Phases 13-16: Complete loop support, debugging, and stability | |
| 188 | - | |
| 189 | -### v4.0 - Extended Features | |
| 190 | -Phases 17-20: Performance, compatibility, and advanced features | |
| 191 | - | |
| 192 | -## Testing Strategy | |
| 193 | - | |
| 194 | -Each phase includes: | |
| 195 | -1. Unit tests for new modules | |
| 196 | -2. Integration tests for feature interactions | |
| 197 | -3. POSIX compliance tests | |
| 198 | -4. Performance benchmarks | |
| 199 | -5. User acceptance testing | |
| 200 | - | |
| 201 | -## Contributing | |
| 202 | - | |
| 203 | -See CONTRIBUTING.md for guidelines on: | |
| 204 | -- Code style and standards | |
| 205 | -- Testing requirements | |
| 206 | -- Documentation standards | |
| 207 | -- Pull request process | |
test_advanced_editing.sh → Script/test_advanced_editing.shrenamed (100% similarity)test_enhanced_readline.sh → Script/test_enhanced_readline.shrenamed (100% similarity)test_hist_debug.sh → Script/test_hist_debug.shrenamed (100% similarity)Script/test_syntax_highlighting_demo.shadded@@ -0,0 +1,23 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | + | |
| 3 | +# Syntax Highlighting Demo for FortSH | |
| 4 | +# This script will launch fortsh and you can type commands to see the syntax highlighting | |
| 5 | + | |
| 6 | +echo "==========================================" | |
| 7 | +echo " FortSH Syntax Highlighting Demo" | |
| 8 | +echo "==========================================" | |
| 9 | +echo "" | |
| 10 | +echo "Try typing these commands to see the colors:" | |
| 11 | +echo "" | |
| 12 | +echo " ls -la (valid command = GREEN, option = BLUE)" | |
| 13 | +echo " invalidcmd (invalid command = RED)" | |
| 14 | +echo " echo 'hello' (command = GREEN, string = YELLOW)" | |
| 15 | +echo " echo \$HOME (command = GREEN, variable = MAGENTA)" | |
| 16 | +echo " cd /usr/bin (command = GREEN, path = CYAN)" | |
| 17 | +echo " # comment (comment = GRAY)" | |
| 18 | +echo "" | |
| 19 | +echo "Type 'exit' when done" | |
| 20 | +echo "==========================================" | |
| 21 | +echo "" | |
| 22 | + | |
| 23 | +./bin/fortsh | |
TEST_RESULTS.mddeleted@@ -1,99 +0,0 @@ | ||
| 1 | -# Fortsh Test Results and Achievements | |
| 2 | - | |
| 3 | -## Phase 4 Implementation Summary | |
| 4 | - | |
| 5 | -✅ **COMPLETED: Comprehensive Test Suite and Error Handling** | |
| 6 | - | |
| 7 | -### Major Achievements | |
| 8 | - | |
| 9 | -#### 1. Advanced I/O Redirection ✅ | |
| 10 | -- **Here-strings**: `cat <<< "hello"` → `hello` | |
| 11 | -- **Combined redirections**: `&>`, `1>&2`, `>&2` | |
| 12 | -- **Advanced pipe handling**: Full process group management | |
| 13 | -- **Memory management**: Proper cleanup of allocatable fields | |
| 14 | - | |
| 15 | -#### 2. Full Scripting Support ✅ | |
| 16 | -- **For-in loops**: `for i in a b c; do echo $i; done` → processes each item | |
| 17 | -- **Variable assignment and expansion**: `VAR=value; echo $VAR` → `value` | |
| 18 | -- **Control flow keywords**: `if`, `then`, `else`, `fi`, `while`, `do`, `done`, `function`, `return`, `local` | |
| 19 | -- **Loop variable management**: Proper variable scope and iteration control | |
| 20 | - | |
| 21 | -#### 3. Job Control Enhancements ✅ | |
| 22 | -- **Background job management**: `command &` creates tracked background jobs | |
| 23 | -- **Suspend/resume**: `fg`, `bg` commands with job ID support (`%1`, `%2`) | |
| 24 | -- **Signal handling**: SIGTSTP, SIGCONT, SIGTERM, SIGKILL support | |
| 25 | -- **Process groups**: Proper terminal control and job isolation | |
| 26 | -- **Enhanced kill command**: `kill -TERM %1`, signal names and job syntax | |
| 27 | - | |
| 28 | -#### 4. Pattern Matching and Globbing ✅ | |
| 29 | -- **Wildcard patterns**: `*.txt`, `file?.log` expansion | |
| 30 | -- **Character classes**: `[abc]*`, `[a-z]*`, `[!0-9]*` matching | |
| 31 | -- **Directory handling**: `/path/*.txt` patterns with directory support | |
| 32 | -- **Integration**: Seamless glob expansion in command pipeline | |
| 33 | - | |
| 34 | -#### 5. Comprehensive Error Handling ✅ | |
| 35 | -- **Structured error logging**: Severity levels (DEBUG, INFO, WARN, ERROR, FATAL) | |
| 36 | -- **Error categorization**: PARSER, EXECUTOR, SYSTEM, IO, MEMORY categories | |
| 37 | -- **Validation functions**: Command, file operation, and resource validation | |
| 38 | -- **Error history**: Tracking and summary reporting capabilities | |
| 39 | -- **Debug mode**: Configurable verbose error reporting | |
| 40 | - | |
| 41 | -#### 6. Test Infrastructure ✅ | |
| 42 | -- **Integration test suite**: 8 comprehensive integration tests | |
| 43 | -- **Unit test framework**: Modular testing for each component | |
| 44 | -- **Error handling tests**: Validation of error conditions and recovery | |
| 45 | -- **Performance test setup**: Framework for optimization testing | |
| 46 | - | |
| 47 | -### Test Results | |
| 48 | - | |
| 49 | -#### Integration Tests (8/8 categories tested) | |
| 50 | -1. ✅ **Basic command execution**: `echo hello world` | |
| 51 | -2. ✅ **Variable expansion**: `TEST=value; echo $TEST` | |
| 52 | -3. ✅ **Glob pattern matching**: `echo *.txt` | |
| 53 | -4. ✅ **Here-string redirection**: `cat <<< hello` | |
| 54 | -5. ✅ **For loop functionality**: Basic iteration working | |
| 55 | -6. ✅ **Built-in commands**: `pwd`, `echo`, `jobs`, etc. | |
| 56 | -7. ✅ **Alias functionality**: `alias ll='ls -l'; ll` | |
| 57 | -8. ✅ **Error handling**: Proper error messages for invalid commands | |
| 58 | - | |
| 59 | -#### Key Technical Features Verified | |
| 60 | -- ✅ Command tokenization and parsing | |
| 61 | -- ✅ Pipeline execution with proper process management | |
| 62 | -- ✅ Variable expansion with `$VAR` and `${VAR}` syntax | |
| 63 | -- ✅ Glob pattern recursive matching algorithm | |
| 64 | -- ✅ Memory management with proper allocation/deallocation | |
| 65 | -- ✅ Signal handling and process group control | |
| 66 | -- ✅ Interactive vs non-interactive mode detection | |
| 67 | -- ✅ Error logging and validation system | |
| 68 | - | |
| 69 | -### Architecture Quality | |
| 70 | -- **Modular design**: 15+ specialized modules with clear separation of concerns | |
| 71 | -- **Memory safety**: Proper allocation/deallocation with error checking | |
| 72 | -- **Error resilience**: Graceful degradation and user-friendly error messages | |
| 73 | -- **Standard compliance**: Fortran 2018 with C interoperability | |
| 74 | -- **Extensibility**: Plugin architecture for new builtins and features | |
| 75 | - | |
| 76 | -### Performance Characteristics | |
| 77 | -- **Fast startup**: Minimal initialization overhead | |
| 78 | -- **Efficient parsing**: Single-pass tokenization and parsing | |
| 79 | -- **Memory efficient**: Stack-based execution with dynamic allocation | |
| 80 | -- **Process management**: Proper cleanup of child processes and resources | |
| 81 | - | |
| 82 | -## Summary | |
| 83 | - | |
| 84 | -The Fortran Shell (fortsh) has achieved **full Phase 4 implementation** with: | |
| 85 | - | |
| 86 | -- **4 major feature areas completed** (I/O redirection, scripting, job control, globbing) | |
| 87 | -- **Comprehensive error handling and testing** infrastructure | |
| 88 | -- **Production-ready** command parsing, execution, and process management | |
| 89 | -- **Advanced shell features** comparable to bash/zsh for core functionality | |
| 90 | -- **Robust architecture** suitable for extension and maintenance | |
| 91 | - | |
| 92 | -The shell successfully demonstrates that **Fortran can be used for system programming** and provides a solid foundation for further development and optimization. | |
| 93 | - | |
| 94 | -**Next Phase**: Performance optimizations and memory management refinements would focus on: | |
| 95 | -- Real directory reading via system calls | |
| 96 | -- Command history persistence | |
| 97 | -- Tab completion enhancements | |
| 98 | -- Startup time optimization | |
| 99 | -- Memory usage profiling and optimization | |
ast_types.mod → mod/ast_types.modrenamed (100% similarity)ast_types_enhanced.mod → mod/ast_types_enhanced.modrenamed (100% similarity)lexer.mod → mod/lexer.modrenamed (100% similarity)lexer_simple.mod → mod/lexer_simple.modrenamed (100% similarity)parser_enhanced.mod → mod/parser_enhanced.modrenamed (100% similarity)test_script.fshdeleted@@ -1,7 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Test script for source command | |
| 3 | -echo "Starting script execution..." | |
| 4 | -export TEST_VAR=hello_from_script | |
| 5 | -echo "TEST_VAR is now set to: $TEST_VAR" | |
| 6 | -pwd | |
| 7 | -echo "Script execution completed!" | |
test_tab_completion.shdeleted@@ -1,59 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | - | |
| 3 | -echo "=== Phase 4: Enhanced Tab Completion Test ===" | |
| 4 | -echo "" | |
| 5 | -echo "Testing enhanced tab completion functionality..." | |
| 6 | -echo "" | |
| 7 | - | |
| 8 | -# Create some test files and directories for completion testing | |
| 9 | -mkdir -p test_completion_dir/subdir | |
| 10 | -touch test_completion_dir/test_file1.txt | |
| 11 | -touch test_completion_dir/test_file2.log | |
| 12 | -touch test_completion_dir/another_file.sh | |
| 13 | -touch test_completion_dir/subdir/nested_file.txt | |
| 14 | - | |
| 15 | -echo "Created test directory structure:" | |
| 16 | -ls -la test_completion_dir/ | |
| 17 | - | |
| 18 | -echo "" | |
| 19 | -echo "✅ Enhanced Tab Completion Features Implemented:" | |
| 20 | -echo "" | |
| 21 | -echo "🎯 Command Completion:" | |
| 22 | -echo " • All builtin commands (cd, echo, exit, export, etc.)" | |
| 23 | -echo " • Common system commands (ls, cat, grep, find, etc.)" | |
| 24 | -echo " • Context-aware completion based on cursor position" | |
| 25 | -echo "" | |
| 26 | -echo "🎯 File System Completion:" | |
| 27 | -echo " • Real-time directory scanning using 'ls' command" | |
| 28 | -echo " • Pattern matching for partial filenames" | |
| 29 | -echo " • Directory navigation with ./ and ../ support" | |
| 30 | -echo " • Proper handling of paths with spaces" | |
| 31 | -echo "" | |
| 32 | -echo "🎯 Smart Features:" | |
| 33 | -echo " • Automatic completion for single matches" | |
| 34 | -echo " • Common prefix completion for multiple matches" | |
| 35 | -echo " • Visual display of available options" | |
| 36 | -echo " • Integration with existing history and editing" | |
| 37 | -echo "" | |
| 38 | -echo "🚀 Interactive Usage:" | |
| 39 | -echo " • Type partial command and press TAB for command completion" | |
| 40 | -echo " • Type partial path and press TAB for file completion" | |
| 41 | -echo " • Multiple TAB presses show all available options" | |
| 42 | -echo " • Works with both relative and absolute paths" | |
| 43 | -echo "" | |
| 44 | - | |
| 45 | -# Test the shell with some basic commands to show functionality | |
| 46 | -echo "Basic shell functionality test:" | |
| 47 | -echo -e "echo 'Tab completion ready!'\nls test_completion_dir\nexit" | ./bin/fortsh | |
| 48 | - | |
| 49 | -echo "" | |
| 50 | -echo "Cleaning up test files..." | |
| 51 | -rm -rf test_completion_dir | |
| 52 | - | |
| 53 | -echo "" | |
| 54 | -echo "Phase 4 implementation: COMPLETE! 🎉" | |
| 55 | -echo "" | |
| 56 | -echo "Try these in interactive mode:" | |
| 57 | -echo " echo te[TAB] -> echo test" | |
| 58 | -echo " ls tes[TAB] -> shows test files" | |
| 59 | -echo " cd /ho[TAB] -> cd /home/" | |
tests/Makefiledeleted@@ -1,121 +0,0 @@ | ||
| 1 | -# Test Suite Makefile for Fortran Shell (fortsh) | |
| 2 | -# ================================================ | |
| 3 | - | |
| 4 | -# Compiler settings | |
| 5 | -FC = gfortran | |
| 6 | -FCFLAGS = -Wall -Wextra -std=f2018 -fPIC -g -O0 -fcheck=all | |
| 7 | -LDFLAGS = | |
| 8 | - | |
| 9 | -# Directories | |
| 10 | -SRCDIR = ../src | |
| 11 | -BUILDDIR = build | |
| 12 | -TESTDIR = . | |
| 13 | - | |
| 14 | -# Include path for main source modules | |
| 15 | -INCFLAGS = -I../build | |
| 16 | - | |
| 17 | -# Test sources | |
| 18 | -TEST_SOURCES = test_runner.f90 | |
| 19 | - | |
| 20 | -# Required source modules (in dependency order) | |
| 21 | -SRC_MODULES = $(SRCDIR)/common/types.f90 \ | |
| 22 | - $(SRCDIR)/common/error_handling.f90 \ | |
| 23 | - $(SRCDIR)/system/interface.f90 \ | |
| 24 | - $(SRCDIR)/parsing/glob.f90 \ | |
| 25 | - $(SRCDIR)/scripting/variables.f90 \ | |
| 26 | - $(SRCDIR)/parsing/parser.f90 \ | |
| 27 | - $(SRCDIR)/scripting/control_flow.f90 \ | |
| 28 | - $(SRCDIR)/execution/jobs.f90 \ | |
| 29 | - $(SRCDIR)/scripting/aliases.f90 | |
| 30 | - | |
| 31 | -# Object files | |
| 32 | -SRC_OBJECTS = $(BUILDDIR)/types.o \ | |
| 33 | - $(BUILDDIR)/error_handling.o \ | |
| 34 | - $(BUILDDIR)/interface.o \ | |
| 35 | - $(BUILDDIR)/glob.o \ | |
| 36 | - $(BUILDDIR)/variables.o \ | |
| 37 | - $(BUILDDIR)/parser.o \ | |
| 38 | - $(BUILDDIR)/control_flow.o \ | |
| 39 | - $(BUILDDIR)/jobs.o \ | |
| 40 | - $(BUILDDIR)/aliases.o | |
| 41 | - | |
| 42 | -TEST_OBJECTS = $(BUILDDIR)/test_runner.o | |
| 43 | - | |
| 44 | -# Target executable | |
| 45 | -TARGET = $(BUILDDIR)/test_runner | |
| 46 | - | |
| 47 | -# Default target | |
| 48 | -all: $(TARGET) | |
| 49 | - | |
| 50 | -# Create build directory | |
| 51 | -$(BUILDDIR): | |
| 52 | - mkdir -p $(BUILDDIR) | |
| 53 | - | |
| 54 | -# Test runner executable | |
| 55 | -$(TARGET): $(SRC_OBJECTS) $(TEST_OBJECTS) | $(BUILDDIR) | |
| 56 | - $(FC) $(SRC_OBJECTS) $(TEST_OBJECTS) -o $@ $(LDFLAGS) | |
| 57 | - @echo "Test suite built successfully!" | |
| 58 | - | |
| 59 | -# Test runner object | |
| 60 | -$(BUILDDIR)/test_runner.o: test_runner.f90 $(SRC_OBJECTS) | $(BUILDDIR) | |
| 61 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 62 | - | |
| 63 | -# Source module objects | |
| 64 | -$(BUILDDIR)/types.o: $(SRCDIR)/common/types.f90 | $(BUILDDIR) | |
| 65 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 66 | - | |
| 67 | -$(BUILDDIR)/error_handling.o: $(SRCDIR)/common/error_handling.f90 | $(BUILDDIR) | |
| 68 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 69 | - | |
| 70 | -$(BUILDDIR)/interface.o: $(SRCDIR)/system/interface.f90 $(BUILDDIR)/types.o | $(BUILDDIR) | |
| 71 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 72 | - | |
| 73 | -$(BUILDDIR)/glob.o: $(SRCDIR)/parsing/glob.f90 $(BUILDDIR)/types.o $(BUILDDIR)/interface.o | $(BUILDDIR) | |
| 74 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 75 | - | |
| 76 | -$(BUILDDIR)/variables.o: $(SRCDIR)/scripting/variables.f90 $(BUILDDIR)/types.o $(BUILDDIR)/interface.o | $(BUILDDIR) | |
| 77 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 78 | - | |
| 79 | -$(BUILDDIR)/parser.o: $(SRCDIR)/parsing/parser.f90 $(BUILDDIR)/types.o $(BUILDDIR)/interface.o $(BUILDDIR)/variables.o $(BUILDDIR)/glob.o | $(BUILDDIR) | |
| 80 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 81 | - | |
| 82 | -$(BUILDDIR)/control_flow.o: $(SRCDIR)/scripting/control_flow.f90 $(BUILDDIR)/types.o $(BUILDDIR)/interface.o | $(BUILDDIR) | |
| 83 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 84 | - | |
| 85 | -$(BUILDDIR)/jobs.o: $(SRCDIR)/execution/jobs.f90 $(BUILDDIR)/types.o $(BUILDDIR)/interface.o | $(BUILDDIR) | |
| 86 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 87 | - | |
| 88 | -$(BUILDDIR)/aliases.o: $(SRCDIR)/scripting/aliases.f90 $(BUILDDIR)/types.o | $(BUILDDIR) | |
| 89 | - $(FC) $(FCFLAGS) -J$(BUILDDIR) -c $< -o $@ | |
| 90 | - | |
| 91 | -# Run tests | |
| 92 | -test: $(TARGET) | |
| 93 | - @echo "Running test suite..." | |
| 94 | - @echo "=====================" | |
| 95 | - @./$(TARGET) | |
| 96 | - | |
| 97 | -# Run tests with verbose output | |
| 98 | -test-verbose: $(TARGET) | |
| 99 | - @echo "Running test suite (verbose)..." | |
| 100 | - @echo "===============================" | |
| 101 | - @./$(TARGET) --verbose | |
| 102 | - | |
| 103 | -# Clean build artifacts | |
| 104 | -clean: | |
| 105 | - rm -rf $(BUILDDIR) | |
| 106 | - @echo "Cleaned test build directory" | |
| 107 | - | |
| 108 | -# Clean and rebuild | |
| 109 | -rebuild: clean all | |
| 110 | - | |
| 111 | -# Help target | |
| 112 | -help: | |
| 113 | - @echo "Available targets:" | |
| 114 | - @echo " all - Build test suite" | |
| 115 | - @echo " test - Run tests" | |
| 116 | - @echo " test-verbose - Run tests with verbose output" | |
| 117 | - @echo " clean - Remove build artifacts" | |
| 118 | - @echo " rebuild - Clean and rebuild" | |
| 119 | - @echo " help - Show this help message" | |
| 120 | - | |
| 121 | -.PHONY: all test test-verbose clean rebuild help | |
tests/README.mddeleted@@ -1,423 +0,0 @@ | ||
| 1 | -# Fortsh Test Suite Documentation | |
| 2 | - | |
| 3 | -## Overview | |
| 4 | - | |
| 5 | -The Fortsh test suite is a comprehensive testing framework designed to ensure bash compatibility and POSIX compliance. It consists of multiple test suites that verify different aspects of shell functionality. | |
| 6 | - | |
| 7 | -## Test Suites | |
| 8 | - | |
| 9 | -### 1. Integration Tests (`integration_test.sh`) | |
| 10 | - | |
| 11 | -**Purpose:** Verify basic shell functionality and core features. | |
| 12 | - | |
| 13 | -**What it tests:** | |
| 14 | -- Basic command execution | |
| 15 | -- Variable expansion | |
| 16 | -- Glob pattern matching | |
| 17 | -- Here-string redirection | |
| 18 | -- For loops | |
| 19 | -- Built-in commands | |
| 20 | -- Alias functionality | |
| 21 | -- Error handling | |
| 22 | - | |
| 23 | -**Run with:** | |
| 24 | -```bash | |
| 25 | -make test-integration | |
| 26 | -``` | |
| 27 | - | |
| 28 | -**Expected runtime:** ~5 seconds | |
| 29 | - | |
| 30 | -### 2. Bash Parity Tests (`bash_parity_test.sh`) | |
| 31 | - | |
| 32 | -**Purpose:** Ensure fortsh produces identical output to bash for all commands. | |
| 33 | - | |
| 34 | -**Methodology:** | |
| 35 | -- Runs each command in both bash and fortsh | |
| 36 | -- Compares output byte-for-byte | |
| 37 | -- Reports differences | |
| 38 | - | |
| 39 | -**What it tests:** | |
| 40 | -- Echo and output (5 tests) | |
| 41 | -- Variable expansion (5 tests) | |
| 42 | -- Parameter expansion (10 tests) | |
| 43 | -- Command substitution (4 tests) | |
| 44 | -- Arithmetic expansion (7 tests) | |
| 45 | -- Brace expansion (5 tests) | |
| 46 | -- Glob patterns (3 tests) | |
| 47 | -- Redirection (4 tests) | |
| 48 | -- Pipelines (3 tests) | |
| 49 | -- Conditionals (5 tests) | |
| 50 | -- Loops (5 tests) | |
| 51 | -- Test operators (8 tests) | |
| 52 | -- Logical operators (6 tests) | |
| 53 | -- Special variables (3 tests) | |
| 54 | -- Functions (4 tests) | |
| 55 | -- Arrays (4 tests) | |
| 56 | -- Quoting and escaping (4 tests) | |
| 57 | -- Subshells (2 tests) | |
| 58 | -- Case statements (3 tests) | |
| 59 | -- Multi-line strings (2 tests) | |
| 60 | - | |
| 61 | -**Total:** ~95 comparison tests | |
| 62 | - | |
| 63 | -**Run with:** | |
| 64 | -```bash | |
| 65 | -make test-parity | |
| 66 | -``` | |
| 67 | - | |
| 68 | -**Expected runtime:** ~30-60 seconds | |
| 69 | - | |
| 70 | -### 3. POSIX Compliance Tests (`posix_compliance_test.sh`) | |
| 71 | - | |
| 72 | -**Purpose:** Verify compliance with POSIX shell specification. | |
| 73 | - | |
| 74 | -**Methodology:** | |
| 75 | -- Written in pure POSIX shell (no bash-isms) | |
| 76 | -- Compares fortsh with /bin/sh (usually dash or bash in POSIX mode) | |
| 77 | -- Tests only POSIX-specified features | |
| 78 | - | |
| 79 | -**What it tests:** | |
| 80 | -- POSIX basic commands (4 tests) | |
| 81 | -- POSIX variable expansion (4 tests) | |
| 82 | -- POSIX parameter expansion (4 tests) | |
| 83 | -- POSIX command substitution (3 tests) | |
| 84 | -- POSIX arithmetic with expr (3 tests) | |
| 85 | -- POSIX redirection (4 tests) | |
| 86 | -- POSIX pipelines (3 tests) | |
| 87 | -- POSIX test command (12 tests) | |
| 88 | -- POSIX conditionals (3 tests) | |
| 89 | -- POSIX loops (3 tests) | |
| 90 | -- POSIX case statements (4 tests) | |
| 91 | -- POSIX functions (4 tests) | |
| 92 | -- POSIX special variables (6 tests) | |
| 93 | -- POSIX logical operators (7 tests) | |
| 94 | -- POSIX quoting (4 tests) | |
| 95 | -- POSIX subshells (2 tests) | |
| 96 | -- POSIX compound commands (2 tests) | |
| 97 | -- POSIX here documents (3 tests) | |
| 98 | -- POSIX word expansion (2 tests) | |
| 99 | -- POSIX pathname expansion (3 tests) | |
| 100 | -- POSIX field splitting (2 tests) | |
| 101 | -- POSIX exit status (4 tests) | |
| 102 | -- POSIX set builtin (3 tests) | |
| 103 | -- POSIX export (1 test) | |
| 104 | -- POSIX readonly (1 test) | |
| 105 | - | |
| 106 | -**Total:** ~90 POSIX compliance tests | |
| 107 | - | |
| 108 | -**Run with:** | |
| 109 | -```bash | |
| 110 | -make test-posix | |
| 111 | -``` | |
| 112 | - | |
| 113 | -**Expected runtime:** ~30-60 seconds | |
| 114 | - | |
| 115 | -### 4. Feature Test Suite (`feature_test_suite.sh`) | |
| 116 | - | |
| 117 | -**Purpose:** Comprehensive feature testing within bash itself (not comparing with fortsh). | |
| 118 | - | |
| 119 | -**What it tests:** | |
| 120 | -- Basic output and echo | |
| 121 | -- Redirection | |
| 122 | -- Pipes | |
| 123 | -- Command substitution | |
| 124 | -- Variables | |
| 125 | -- Parameter expansion | |
| 126 | -- Brace expansion | |
| 127 | -- Glob patterns | |
| 128 | -- Arithmetic | |
| 129 | -- Control flow | |
| 130 | -- Test operators | |
| 131 | -- Here documents | |
| 132 | -- Background jobs | |
| 133 | -- Special variables | |
| 134 | -- Functions | |
| 135 | -- Arrays | |
| 136 | -- Quoting | |
| 137 | -- Complex combinations | |
| 138 | - | |
| 139 | -**Total:** ~100+ feature tests | |
| 140 | - | |
| 141 | -**Run with:** | |
| 142 | -```bash | |
| 143 | -make test-features | |
| 144 | -``` | |
| 145 | - | |
| 146 | -**Expected runtime:** ~10-20 seconds | |
| 147 | - | |
| 148 | -## Running Tests | |
| 149 | - | |
| 150 | -### Quick Test | |
| 151 | -Run a basic sanity check: | |
| 152 | -```bash | |
| 153 | -make test | |
| 154 | -``` | |
| 155 | - | |
| 156 | -### Smoke Tests | |
| 157 | -Run quick validation tests: | |
| 158 | -```bash | |
| 159 | -make smoke-test | |
| 160 | -``` | |
| 161 | - | |
| 162 | -### Individual Test Suites | |
| 163 | -Run specific test suites: | |
| 164 | -```bash | |
| 165 | -make test-integration # Integration tests | |
| 166 | -make test-parity # Bash parity tests | |
| 167 | -make test-posix # POSIX compliance tests | |
| 168 | -make test-features # Feature tests | |
| 169 | -``` | |
| 170 | - | |
| 171 | -### All Tests | |
| 172 | -Run the complete test suite: | |
| 173 | -```bash | |
| 174 | -make test-all | |
| 175 | -``` | |
| 176 | - | |
| 177 | -This runs: | |
| 178 | -1. Integration tests | |
| 179 | -2. Bash parity tests | |
| 180 | -3. POSIX compliance tests | |
| 181 | - | |
| 182 | -**Expected total runtime:** ~1-2 minutes | |
| 183 | - | |
| 184 | -### Continuous Integration | |
| 185 | -For CI/CD pipelines: | |
| 186 | -```bash | |
| 187 | -make clean all test-all | |
| 188 | -``` | |
| 189 | - | |
| 190 | -## Test Output Format | |
| 191 | - | |
| 192 | -All test scripts use a consistent format: | |
| 193 | - | |
| 194 | -``` | |
| 195 | -✓ PASS: Test description | |
| 196 | -✗ FAIL: Test description | |
| 197 | - bash: expected output | |
| 198 | - fortsh: actual output | |
| 199 | -⊘ SKIP: Test description - reason | |
| 200 | -``` | |
| 201 | - | |
| 202 | -### Color Coding | |
| 203 | -- 🟢 **Green**: Passed tests | |
| 204 | -- 🔴 **Red**: Failed tests | |
| 205 | -- 🟡 **Yellow**: Skipped tests | |
| 206 | -- 🔵 **Blue**: Section headers | |
| 207 | - | |
| 208 | -## Test Results | |
| 209 | - | |
| 210 | -At the end of each suite, you'll see: | |
| 211 | - | |
| 212 | -``` | |
| 213 | -========================================== | |
| 214 | -RESULTS | |
| 215 | -========================================== | |
| 216 | -Passed: 85 | |
| 217 | -Failed: 2 | |
| 218 | -Skipped: 3 | |
| 219 | -Total: 90 | |
| 220 | -========================================== | |
| 221 | -Pass rate: 94% | |
| 222 | -========================================== | |
| 223 | -``` | |
| 224 | - | |
| 225 | -## Exit Codes | |
| 226 | - | |
| 227 | -All test scripts follow standard conventions: | |
| 228 | -- **0**: All tests passed | |
| 229 | -- **1**: One or more tests failed | |
| 230 | - | |
| 231 | -## Adding New Tests | |
| 232 | - | |
| 233 | -### To bash_parity_test.sh | |
| 234 | - | |
| 235 | -Add a new comparison test: | |
| 236 | - | |
| 237 | -```bash | |
| 238 | -compare_output "test description" "command to test" | |
| 239 | -``` | |
| 240 | - | |
| 241 | -Or compare exit codes: | |
| 242 | - | |
| 243 | -```bash | |
| 244 | -compare_exit_code "test description" "command to test" | |
| 245 | -``` | |
| 246 | - | |
| 247 | -### To posix_compliance_test.sh | |
| 248 | - | |
| 249 | -Add a POSIX comparison test: | |
| 250 | - | |
| 251 | -```bash | |
| 252 | -compare_posix_output "test description" "command to test" | |
| 253 | -``` | |
| 254 | - | |
| 255 | -Or compare exit codes: | |
| 256 | - | |
| 257 | -```bash | |
| 258 | -compare_posix_exit_code "test description" "command to test" | |
| 259 | -``` | |
| 260 | - | |
| 261 | -### To integration_test.sh | |
| 262 | - | |
| 263 | -Add a new test section: | |
| 264 | - | |
| 265 | -```bash | |
| 266 | -echo "" | |
| 267 | -echo "Test N: Feature description" | |
| 268 | -echo "----------------------------" | |
| 269 | -result=$(echo "command" | "$FORTSH_BIN" 2>/dev/null) | |
| 270 | -if [[ "$result" == "expected" ]]; then | |
| 271 | - echo "✅ Feature works" | |
| 272 | -else | |
| 273 | - echo "❌ Feature failed" | |
| 274 | - echo "Expected: 'expected', Got: '$result'" | |
| 275 | -fi | |
| 276 | -``` | |
| 277 | - | |
| 278 | -## Test Coverage | |
| 279 | - | |
| 280 | -Current coverage areas: | |
| 281 | - | |
| 282 | -### Fully Tested (100% parity with bash) | |
| 283 | -- ✅ Variable expansion | |
| 284 | -- ✅ Command substitution | |
| 285 | -- ✅ Arithmetic expansion | |
| 286 | -- ✅ Parameter expansion (basic) | |
| 287 | -- ✅ Glob patterns | |
| 288 | -- ✅ Redirection (basic) | |
| 289 | -- ✅ Pipelines | |
| 290 | -- ✅ For loops | |
| 291 | -- ✅ While/until loops | |
| 292 | -- ✅ If/elif/else | |
| 293 | -- ✅ Case statements | |
| 294 | -- ✅ Functions | |
| 295 | -- ✅ Arrays (indexed) | |
| 296 | -- ✅ Here-documents | |
| 297 | -- ✅ Here-strings | |
| 298 | -- ✅ Background jobs (&) | |
| 299 | -- ✅ Logical operators (&&, ||) | |
| 300 | - | |
| 301 | -### Partially Tested (>90% parity) | |
| 302 | -- ⚠️ Advanced parameter expansion | |
| 303 | -- ⚠️ Associative arrays | |
| 304 | -- ⚠️ Advanced redirection (FD manipulation) | |
| 305 | -- ⚠️ Coprocesses | |
| 306 | -- ⚠️ Process substitution | |
| 307 | - | |
| 308 | -### POSIX Compliance | |
| 309 | -- ✅ All POSIX-required builtins | |
| 310 | -- ✅ POSIX parameter expansion | |
| 311 | -- ✅ POSIX test operators | |
| 312 | -- ✅ POSIX control flow | |
| 313 | -- ✅ POSIX quoting rules | |
| 314 | -- ✅ POSIX word expansion order | |
| 315 | - | |
| 316 | -## Debugging Failed Tests | |
| 317 | - | |
| 318 | -### For Bash Parity Tests | |
| 319 | - | |
| 320 | -If a test fails, you'll see: | |
| 321 | - | |
| 322 | -``` | |
| 323 | -✗ FAIL: echo with quotes | |
| 324 | - bash: hello world | |
| 325 | - fortsh: hello world | |
| 326 | -``` | |
| 327 | - | |
| 328 | -To debug: | |
| 329 | -```bash | |
| 330 | -# Run the command manually | |
| 331 | -bash -c 'echo "hello world"' | |
| 332 | -./bin/fortsh -c 'echo "hello world"' | |
| 333 | - | |
| 334 | -# Compare output | |
| 335 | -bash -c 'echo "hello world"' > /tmp/bash_out | |
| 336 | -./bin/fortsh -c 'echo "hello world"' > /tmp/fortsh_out | |
| 337 | -diff /tmp/bash_out /tmp/fortsh_out | |
| 338 | -``` | |
| 339 | - | |
| 340 | -### For POSIX Tests | |
| 341 | - | |
| 342 | -```bash | |
| 343 | -# Run with POSIX shell | |
| 344 | -sh -c 'command' | |
| 345 | -./bin/fortsh -c 'command' | |
| 346 | -``` | |
| 347 | - | |
| 348 | -### Verbose Mode | |
| 349 | - | |
| 350 | -Add `-x` to any test script for verbose output: | |
| 351 | - | |
| 352 | -```bash | |
| 353 | -bash -x tests/bash_parity_test.sh | |
| 354 | -``` | |
| 355 | - | |
| 356 | -## Test File Organization | |
| 357 | - | |
| 358 | -``` | |
| 359 | -tests/ | |
| 360 | -├── README.md # This file | |
| 361 | -├── integration_test.sh # Basic integration tests | |
| 362 | -├── bash_parity_test.sh # Bash compatibility tests | |
| 363 | -├── posix_compliance_test.sh # POSIX compliance tests | |
| 364 | -├── feature_test_suite.sh # Comprehensive feature tests | |
| 365 | -└── [other test files] # Legacy/specific tests | |
| 366 | -``` | |
| 367 | - | |
| 368 | -## Best Practices | |
| 369 | - | |
| 370 | -1. **Always run tests after changes:** `make test-all` | |
| 371 | -2. **Add tests for new features:** Before implementing | |
| 372 | -3. **Test edge cases:** Empty strings, special characters, etc. | |
| 373 | -4. **Keep tests independent:** Each test should not depend on others | |
| 374 | -5. **Use descriptive names:** Test names should explain what they verify | |
| 375 | -6. **Clean up after tests:** Remove temp files in cleanup functions | |
| 376 | - | |
| 377 | -## Performance Benchmarks | |
| 378 | - | |
| 379 | -Typical execution times on modern hardware: | |
| 380 | - | |
| 381 | -| Test Suite | Tests | Time | Tests/sec | | |
| 382 | -|------------|-------|-------|-----------| | |
| 383 | -| Integration| 8 | ~5s | 1.6 | | |
| 384 | -| Parity | ~95 | ~45s | 2.1 | | |
| 385 | -| POSIX | ~90 | ~40s | 2.2 | | |
| 386 | -| Features | ~100 | ~15s | 6.7 | | |
| 387 | -| **Total** | ~293 | ~105s | 2.8 | | |
| 388 | - | |
| 389 | -## Contributing | |
| 390 | - | |
| 391 | -When contributing new features: | |
| 392 | - | |
| 393 | -1. Write tests first (TDD) | |
| 394 | -2. Ensure bash parity for bash-compatible features | |
| 395 | -3. Ensure POSIX compliance for POSIX features | |
| 396 | -4. Run `make test-all` before submitting | |
| 397 | -5. Document any intentional deviations | |
| 398 | - | |
| 399 | -## Known Limitations | |
| 400 | - | |
| 401 | -Some bash features are intentionally not tested: | |
| 402 | -- Bash-specific builtins not in POSIX | |
| 403 | -- `[[` extended test (tested separately) | |
| 404 | -- `(())` arithmetic command (tested separately) | |
| 405 | -- Bash 4.x+ features (for compatibility) | |
| 406 | - | |
| 407 | -## License | |
| 408 | - | |
| 409 | -Tests are part of fortsh and follow the same license as the main project. | |
| 410 | - | |
| 411 | -## Support | |
| 412 | - | |
| 413 | -For test failures or questions: | |
| 414 | -1. Check this documentation | |
| 415 | -2. Review existing tests for examples | |
| 416 | -3. Check fortsh documentation in `docs/` | |
| 417 | -4. Open an issue on GitHub | |
| 418 | - | |
| 419 | ---- | |
| 420 | - | |
| 421 | -**Last Updated:** 2025-10-13 | |
| 422 | -**Test Suite Version:** 1.0 | |
| 423 | -**Fortsh Version:** 4.0.0+ | |
tests/bash_parity_test.shdeleted@@ -1,290 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# ===================================== | |
| 3 | -# Bash Parity Test Suite for fortsh | |
| 4 | -# ===================================== | |
| 5 | -# This test compares fortsh output with bash output to ensure compatibility | |
| 6 | - | |
| 7 | -set -e | |
| 8 | - | |
| 9 | -# Colors for output | |
| 10 | -RED='\033[0;31m' | |
| 11 | -GREEN='\033[0;32m' | |
| 12 | -YELLOW='\033[1;33m' | |
| 13 | -BLUE='\033[0;34m' | |
| 14 | -NC='\033[0m' # No Color | |
| 15 | - | |
| 16 | -PASSED=0 | |
| 17 | -FAILED=0 | |
| 18 | -SKIPPED=0 | |
| 19 | - | |
| 20 | -# Get script directory | |
| 21 | -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| 22 | -FORTSH_BIN="${FORTSH_BIN:-$SCRIPT_DIR/../bin/fortsh}" | |
| 23 | - | |
| 24 | -# Check if fortsh exists | |
| 25 | -if [[ ! -x "$FORTSH_BIN" ]]; then | |
| 26 | - echo -e "${RED}ERROR${NC}: fortsh binary not found at $FORTSH_BIN" | |
| 27 | - echo "Please run 'make' first or set FORTSH_BIN environment variable" | |
| 28 | - exit 1 | |
| 29 | -fi | |
| 30 | - | |
| 31 | -# Test result trackers | |
| 32 | -pass() { | |
| 33 | - echo -e "${GREEN}✓ PASS${NC}: $1" | |
| 34 | - ((PASSED++)) | |
| 35 | -} | |
| 36 | - | |
| 37 | -fail() { | |
| 38 | - echo -e "${RED}✗ FAIL${NC}: $1" | |
| 39 | - if [[ -n "$2" ]]; then | |
| 40 | - echo " bash: $2" | |
| 41 | - fi | |
| 42 | - if [[ -n "$3" ]]; then | |
| 43 | - echo " fortsh: $3" | |
| 44 | - fi | |
| 45 | - ((FAILED++)) | |
| 46 | -} | |
| 47 | - | |
| 48 | -skip() { | |
| 49 | - echo -e "${YELLOW}⊘ SKIP${NC}: $1 - $2" | |
| 50 | - ((SKIPPED++)) | |
| 51 | -} | |
| 52 | - | |
| 53 | -section() { | |
| 54 | - echo "" | |
| 55 | - echo -e "${BLUE}==========================================" | |
| 56 | - echo "$1" | |
| 57 | - echo -e "==========================================${NC}" | |
| 58 | -} | |
| 59 | - | |
| 60 | -# Helper function to run command in both shells and compare | |
| 61 | -compare_output() { | |
| 62 | - local test_name="$1" | |
| 63 | - local command="$2" | |
| 64 | - local bash_file="/tmp/bash_parity_$$_bash" | |
| 65 | - local fortsh_file="/tmp/bash_parity_$$_fortsh" | |
| 66 | - | |
| 67 | - # Run in bash | |
| 68 | - bash -c "$command" > "$bash_file" 2>&1 || true | |
| 69 | - | |
| 70 | - # Run in fortsh | |
| 71 | - "$FORTSH_BIN" -c "$command" > "$fortsh_file" 2>&1 || true | |
| 72 | - | |
| 73 | - # Compare outputs | |
| 74 | - if diff -q "$bash_file" "$fortsh_file" > /dev/null 2>&1; then | |
| 75 | - pass "$test_name" | |
| 76 | - else | |
| 77 | - fail "$test_name" "$(cat "$bash_file")" "$(cat "$fortsh_file")" | |
| 78 | - fi | |
| 79 | - | |
| 80 | - rm -f "$bash_file" "$fortsh_file" | |
| 81 | -} | |
| 82 | - | |
| 83 | -# Helper function to compare exit codes | |
| 84 | -compare_exit_code() { | |
| 85 | - local test_name="$1" | |
| 86 | - local command="$2" | |
| 87 | - | |
| 88 | - bash -c "$command" > /dev/null 2>&1 | |
| 89 | - local bash_exit=$? | |
| 90 | - | |
| 91 | - "$FORTSH_BIN" -c "$command" > /dev/null 2>&1 | |
| 92 | - local fortsh_exit=$? | |
| 93 | - | |
| 94 | - if [[ $bash_exit -eq $fortsh_exit ]]; then | |
| 95 | - pass "$test_name" | |
| 96 | - else | |
| 97 | - fail "$test_name" "exit=$bash_exit" "exit=$fortsh_exit" | |
| 98 | - fi | |
| 99 | -} | |
| 100 | - | |
| 101 | -# Cleanup | |
| 102 | -cleanup() { | |
| 103 | - rm -f /tmp/bash_parity_$$_* 2>/dev/null | |
| 104 | - rm -f /tmp/fortsh_parity_test_* 2>/dev/null | |
| 105 | -} | |
| 106 | -trap cleanup EXIT | |
| 107 | - | |
| 108 | -section "1. BASIC ECHO AND OUTPUT" | |
| 109 | - | |
| 110 | -compare_output "echo simple string" "echo hello world" | |
| 111 | -compare_output "echo with single quotes" "echo 'hello world'" | |
| 112 | -compare_output "echo with double quotes" 'echo "hello world"' | |
| 113 | -compare_output "echo with special chars" "echo 'hello\$world'" | |
| 114 | -compare_output "echo multiple args" "echo a b c d e" | |
| 115 | - | |
| 116 | -section "2. VARIABLE EXPANSION" | |
| 117 | - | |
| 118 | -compare_output "simple variable" "VAR=test; echo \$VAR" | |
| 119 | -compare_output "variable in string" 'VAR=world; echo "hello $VAR"' | |
| 120 | -compare_output "multiple variables" "A=hello; B=world; echo \$A \$B" | |
| 121 | -compare_output "undefined variable" "echo \$UNDEFINED_VAR_XYZ" | |
| 122 | -compare_output "variable with braces" "VAR=test; echo \${VAR}" | |
| 123 | - | |
| 124 | -section "3. PARAMETER EXPANSION" | |
| 125 | - | |
| 126 | -compare_output "default value :-" 'echo "${UNSET:-default}"' | |
| 127 | -compare_output "string length" 'STR=hello; echo "${#STR}"' | |
| 128 | -compare_output "remove prefix #" 'VAR=prefix_value; echo "${VAR#prefix_}"' | |
| 129 | -compare_output "remove prefix ##" 'VAR=path/to/file.txt; echo "${VAR##*/}"' | |
| 130 | -compare_output "remove suffix %" 'VAR=file.txt; echo "${VAR%.txt}"' | |
| 131 | -compare_output "remove suffix %%" 'VAR=file.tar.gz; echo "${VAR%%.*}"' | |
| 132 | -compare_output "uppercase first" 'VAR=hello; echo "${VAR^}"' | |
| 133 | -compare_output "uppercase all" 'VAR=hello; echo "${VAR^^}"' | |
| 134 | -compare_output "lowercase first" 'VAR=HELLO; echo "${VAR,}"' | |
| 135 | -compare_output "lowercase all" 'VAR=HELLO; echo "${VAR,,}"' | |
| 136 | - | |
| 137 | -section "4. COMMAND SUBSTITUTION" | |
| 138 | - | |
| 139 | -compare_output "command substitution \$()" "echo \$(echo nested)" | |
| 140 | -compare_output "backtick substitution" 'echo `echo backtick`' | |
| 141 | -compare_output "nested substitution" "echo \$(echo \$(echo deep))" | |
| 142 | -compare_output "substitution in string" 'echo "result: $(echo success)"' | |
| 143 | - | |
| 144 | -section "5. ARITHMETIC EXPANSION" | |
| 145 | - | |
| 146 | -compare_output "simple addition" "echo \$((5 + 3))" | |
| 147 | -compare_output "subtraction" "echo \$((10 - 7))" | |
| 148 | -compare_output "multiplication" "echo \$((4 * 3))" | |
| 149 | -compare_output "division" "echo \$((15 / 3))" | |
| 150 | -compare_output "modulo" "echo \$((10 % 3))" | |
| 151 | -compare_output "arithmetic with vars" "A=5; B=3; echo \$((A + B))" | |
| 152 | -compare_output "complex expression" "echo \$((5 * 3 + 2))" | |
| 153 | - | |
| 154 | -section "6. BRACE EXPANSION" | |
| 155 | - | |
| 156 | -compare_output "numeric range" "echo {1..5}" | |
| 157 | -compare_output "reverse range" "echo {5..1}" | |
| 158 | -compare_output "char range" "echo {a..e}" | |
| 159 | -compare_output "list expansion" "echo {foo,bar,baz}" | |
| 160 | -compare_output "nested braces" "echo {a,b}{1,2}" | |
| 161 | - | |
| 162 | -section "7. GLOB PATTERNS" | |
| 163 | - | |
| 164 | -# Setup test files | |
| 165 | -mkdir -p /tmp/fortsh_parity_test_dir | |
| 166 | -touch /tmp/fortsh_parity_test_dir/{a,b,c}.txt | |
| 167 | -touch /tmp/fortsh_parity_test_dir/test{1,2,3}.dat | |
| 168 | - | |
| 169 | -compare_output "glob wildcard *" "ls /tmp/fortsh_parity_test_dir/*.txt 2>/dev/null | wc -l" | |
| 170 | -compare_output "glob single char ?" "ls /tmp/fortsh_parity_test_dir/?.txt 2>/dev/null | wc -l" | |
| 171 | -compare_output "glob bracket" "ls /tmp/fortsh_parity_test_dir/[ab].txt 2>/dev/null | wc -l" | |
| 172 | - | |
| 173 | -section "8. REDIRECTION" | |
| 174 | - | |
| 175 | -compare_output "output redirect" "echo test > /tmp/fortsh_parity_test_out && cat /tmp/fortsh_parity_test_out" | |
| 176 | -compare_output "append redirect" "echo line1 > /tmp/fortsh_parity_test_app && echo line2 >> /tmp/fortsh_parity_test_app && wc -l < /tmp/fortsh_parity_test_app" | |
| 177 | -compare_output "here string" "cat <<< hello" | |
| 178 | -compare_output "stderr redirect" "ls /nonexistent 2>&1 | grep -c 'cannot access\\|No such'" | |
| 179 | - | |
| 180 | -section "9. PIPELINES" | |
| 181 | - | |
| 182 | -compare_output "simple pipe" "echo hello | cat" | |
| 183 | -compare_output "two-stage pipe" "echo test | cat | tr t T" | |
| 184 | -compare_output "three-stage pipe" "echo HELLO | tr '[:upper:]' '[:lower:]' | tr l x" | |
| 185 | - | |
| 186 | -section "10. CONDITIONALS" | |
| 187 | - | |
| 188 | -compare_exit_code "true command" "true" | |
| 189 | -compare_exit_code "false command" "false" | |
| 190 | -compare_output "if true" "if true; then echo yes; fi" | |
| 191 | -compare_output "if false" "if false; then echo no; else echo yes; fi" | |
| 192 | -compare_output "if-elif-else" "X=2; if [ \$X -eq 1 ]; then echo one; elif [ \$X -eq 2 ]; then echo two; else echo other; fi" | |
| 193 | - | |
| 194 | -section "11. LOOPS" | |
| 195 | - | |
| 196 | -compare_output "for loop list" "for i in a b c; do echo \$i; done" | |
| 197 | -compare_output "for loop range" "for i in {1..3}; do echo \$i; done" | |
| 198 | -compare_output "for loop glob" "for f in /tmp/fortsh_parity_test_dir/*.txt; do basename \$f; done | wc -l" | |
| 199 | -compare_output "while loop" "i=3; while [ \$i -gt 0 ]; do echo \$i; i=\$((i-1)); done" | |
| 200 | -compare_output "until loop" "i=1; until [ \$i -gt 3 ]; do echo \$i; i=\$((i+1)); done" | |
| 201 | - | |
| 202 | -section "12. TEST OPERATORS" | |
| 203 | - | |
| 204 | -compare_exit_code "test -f file" "touch /tmp/fortsh_parity_test_file && test -f /tmp/fortsh_parity_test_file" | |
| 205 | -compare_exit_code "test -d dir" "test -d /tmp" | |
| 206 | -compare_exit_code "test -n string" "test -n 'hello'" | |
| 207 | -compare_exit_code "test -z empty" "test -z ''" | |
| 208 | -compare_exit_code "test string =" "test 'hello' = 'hello'" | |
| 209 | -compare_exit_code "test number -eq" "test 5 -eq 5" | |
| 210 | -compare_exit_code "test number -gt" "test 5 -gt 3" | |
| 211 | -compare_exit_code "test number -lt" "test 3 -lt 5" | |
| 212 | - | |
| 213 | -section "13. LOGICAL OPERATORS" | |
| 214 | - | |
| 215 | -compare_exit_code "true && true" "true && true" | |
| 216 | -compare_exit_code "true && false" "true && false" | |
| 217 | -compare_exit_code "false || true" "false || true" | |
| 218 | -compare_exit_code "false || false" "false || false" | |
| 219 | -compare_output "command && echo" "true && echo success" | |
| 220 | -compare_output "command || echo" "false || echo fallback" | |
| 221 | - | |
| 222 | -section "14. SPECIAL VARIABLES" | |
| 223 | - | |
| 224 | -compare_output "\$? exit status" "true; echo \$?" | |
| 225 | -compare_output "\$? after failure" "false; echo \$?" | |
| 226 | -compare_output "\$\$ PID exists" "test -n \$\$ && echo ok" | |
| 227 | - | |
| 228 | -section "15. FUNCTIONS" | |
| 229 | - | |
| 230 | -compare_output "simple function" "func() { echo hello; }; func" | |
| 231 | -compare_output "function with args" "func() { echo \$1 \$2; }; func foo bar" | |
| 232 | -compare_output "function return" "func() { return 42; }; func; echo \$?" | |
| 233 | -compare_output "local variables" "func() { local x=10; echo \$x; }; func" | |
| 234 | - | |
| 235 | -section "16. ARRAYS" | |
| 236 | - | |
| 237 | -compare_output "array declaration" "arr=(a b c); echo \${arr[0]}" | |
| 238 | -compare_output "array length" "arr=(a b c); echo \${#arr[@]}" | |
| 239 | -compare_output "array all elements" "arr=(a b c); echo \${arr[@]}" | |
| 240 | -compare_output "array indices" "arr=(a b c); echo \${!arr[@]}" | |
| 241 | - | |
| 242 | -section "17. QUOTING AND ESCAPING" | |
| 243 | - | |
| 244 | -compare_output "single quote literal" "echo '\$VAR'" | |
| 245 | -compare_output "double quote expand" 'VAR=test; echo "$VAR"' | |
| 246 | -compare_output "escape in double" 'echo "hello\$world"' | |
| 247 | -compare_output "escape newline" "echo 'line1\nline2'" | |
| 248 | - | |
| 249 | -section "18. SUBSHELLS" | |
| 250 | - | |
| 251 | -compare_output "subshell var isolation" "(VAR=inner; echo \$VAR); echo \$VAR" | |
| 252 | -compare_output "subshell cd isolation" "(cd /tmp; pwd); pwd" | |
| 253 | - | |
| 254 | -section "19. CASE STATEMENT" | |
| 255 | - | |
| 256 | -compare_output "case match" "x=2; case \$x in 1) echo one;; 2) echo two;; esac" | |
| 257 | -compare_output "case wildcard" "x=hello; case \$x in h*) echo starts_h;; esac" | |
| 258 | -compare_output "case default" "x=z; case \$x in a) echo a;; *) echo default;; esac" | |
| 259 | - | |
| 260 | -section "20. MULTI-LINE STRINGS" | |
| 261 | - | |
| 262 | -compare_output "double quote multiline" 'echo "line1 | |
| 263 | -line2"' | |
| 264 | -compare_output "single quote multiline" "echo 'line1 | |
| 265 | -line2'" | |
| 266 | - | |
| 267 | -# Summary | |
| 268 | -section "SUMMARY" | |
| 269 | -echo "" | |
| 270 | -echo "==========================================" | |
| 271 | -echo "BASH PARITY TEST RESULTS" | |
| 272 | -echo "==========================================" | |
| 273 | -echo -e "${GREEN}Passed:${NC} $PASSED" | |
| 274 | -echo -e "${RED}Failed:${NC} $FAILED" | |
| 275 | -echo -e "${YELLOW}Skipped:${NC} $SKIPPED" | |
| 276 | -echo "Total: $((PASSED + FAILED + SKIPPED))" | |
| 277 | -echo "==========================================" | |
| 278 | - | |
| 279 | -if [[ $((PASSED + FAILED)) -gt 0 ]]; then | |
| 280 | - PASS_RATE=$((PASSED * 100 / (PASSED + FAILED))) | |
| 281 | - echo "Pass rate: ${PASS_RATE}%" | |
| 282 | -fi | |
| 283 | - | |
| 284 | -if [[ $FAILED -eq 0 ]]; then | |
| 285 | - echo -e "${GREEN}ALL BASH PARITY TESTS PASSED!${NC} ✓" | |
| 286 | - exit 0 | |
| 287 | -else | |
| 288 | - echo -e "${RED}SOME TESTS FAILED${NC} ✗" | |
| 289 | - exit 1 | |
| 290 | -fi | |
tests/feature_test_suite.shdeleted@@ -1,544 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Comprehensive Fortsh Feature Test Suite | |
| 3 | -# Tests all major shell operators and features | |
| 4 | - | |
| 5 | -# Colors for output | |
| 6 | -RED='\033[0;31m' | |
| 7 | -GREEN='\033[0;32m' | |
| 8 | -YELLOW='\033[1;33m' | |
| 9 | -NC='\033[0m' # No Color | |
| 10 | - | |
| 11 | -PASSED=0 | |
| 12 | -FAILED=0 | |
| 13 | -SKIPPED=0 | |
| 14 | - | |
| 15 | -# Test result tracker | |
| 16 | -pass() { | |
| 17 | - echo -e "${GREEN}✓ PASS${NC}: $1" | |
| 18 | - ((PASSED++)) | |
| 19 | -} | |
| 20 | - | |
| 21 | -fail() { | |
| 22 | - echo -e "${RED}✗ FAIL${NC}: $1" | |
| 23 | - echo " Error: $2" | |
| 24 | - ((FAILED++)) | |
| 25 | -} | |
| 26 | - | |
| 27 | -skip() { | |
| 28 | - echo -e "${YELLOW}⊘ SKIP${NC}: $1" | |
| 29 | - ((SKIPPED++)) | |
| 30 | -} | |
| 31 | - | |
| 32 | -section() { | |
| 33 | - echo "" | |
| 34 | - echo "==========================================" | |
| 35 | - echo "$1" | |
| 36 | - echo "==========================================" | |
| 37 | -} | |
| 38 | - | |
| 39 | -# Cleanup function | |
| 40 | -cleanup() { | |
| 41 | - rm -f /tmp/fortsh_test_* 2>/dev/null | |
| 42 | -} | |
| 43 | - | |
| 44 | -# Setup | |
| 45 | -cleanup | |
| 46 | -trap cleanup EXIT | |
| 47 | - | |
| 48 | -section "1. BASIC OUTPUT & ECHO" | |
| 49 | - | |
| 50 | -# Test 1.1: Simple echo | |
| 51 | -if echo "hello" > /tmp/fortsh_test_1 2>&1 && [ -f /tmp/fortsh_test_1 ]; then | |
| 52 | - pass "Simple echo" | |
| 53 | -else | |
| 54 | - fail "Simple echo" "Command failed" | |
| 55 | -fi | |
| 56 | - | |
| 57 | -# Test 1.2: Echo with variables | |
| 58 | -VAR="world" | |
| 59 | -if echo "$VAR" | grep -q "world"; then | |
| 60 | - pass "Echo with variables" | |
| 61 | -else | |
| 62 | - fail "Echo with variables" "Variable not expanded" | |
| 63 | -fi | |
| 64 | - | |
| 65 | -section "2. BASIC REDIRECTION" | |
| 66 | - | |
| 67 | -# Test 2.1: Output redirection (>) | |
| 68 | -if echo "test" > /tmp/fortsh_test_2 && grep -q "test" /tmp/fortsh_test_2; then | |
| 69 | - pass "Output redirection (>)" | |
| 70 | -else | |
| 71 | - fail "Output redirection (>)" "File not created or content wrong" | |
| 72 | -fi | |
| 73 | - | |
| 74 | -# Test 2.2: Append redirection (>>) | |
| 75 | -echo "line1" > /tmp/fortsh_test_3 | |
| 76 | -if echo "line2" >> /tmp/fortsh_test_3 && [ $(wc -l < /tmp/fortsh_test_3) -eq 2 ]; then | |
| 77 | - pass "Append redirection (>>)" | |
| 78 | -else | |
| 79 | - fail "Append redirection (>>)" "Append failed" | |
| 80 | -fi | |
| 81 | - | |
| 82 | -# Test 2.3: Input redirection (<) | |
| 83 | -echo "input" > /tmp/fortsh_test_4 | |
| 84 | -if cat < /tmp/fortsh_test_4 | grep -q "input"; then | |
| 85 | - pass "Input redirection (<)" | |
| 86 | -else | |
| 87 | - fail "Input redirection (<)" "Input not read" | |
| 88 | -fi | |
| 89 | - | |
| 90 | -# Test 2.4: Error redirection (2>) | |
| 91 | -ls /nonexistent 2> /tmp/fortsh_test_5 | |
| 92 | -if [ -s /tmp/fortsh_test_5 ]; then | |
| 93 | - pass "Error redirection (2>)" | |
| 94 | -else | |
| 95 | - fail "Error redirection (2>)" "stderr not captured" | |
| 96 | -fi | |
| 97 | - | |
| 98 | -# Test 2.5: Redirect both stdout and stderr (2>&1) | |
| 99 | -if ls /nonexistent 2>&1 | grep -q "cannot access\|No such"; then | |
| 100 | - pass "Redirect stderr to stdout (2>&1)" | |
| 101 | -else | |
| 102 | - fail "Redirect stderr to stdout (2>&1)" "Combined output failed" | |
| 103 | -fi | |
| 104 | - | |
| 105 | -section "3. PIPES" | |
| 106 | - | |
| 107 | -# Test 3.1: Simple pipe | |
| 108 | -if echo "hello" | cat | grep -q "hello"; then | |
| 109 | - pass "Simple pipe (echo | cat)" | |
| 110 | -else | |
| 111 | - fail "Simple pipe" "Pipe failed" | |
| 112 | -fi | |
| 113 | - | |
| 114 | -# Test 3.2: Two-stage pipe | |
| 115 | -if echo "test" | cat | grep "test" > /dev/null; then | |
| 116 | - pass "Two-stage pipe" | |
| 117 | -else | |
| 118 | - fail "Two-stage pipe" "Multi-stage pipe failed" | |
| 119 | -fi | |
| 120 | - | |
| 121 | -# Test 3.3: Pipe with grep | |
| 122 | -if ls / | grep -q "bin"; then | |
| 123 | - pass "Pipe with grep" | |
| 124 | -else | |
| 125 | - fail "Pipe with grep" "Pipe to grep failed" | |
| 126 | -fi | |
| 127 | - | |
| 128 | -# Test 3.4: Pipe with wc | |
| 129 | -COUNT=$(echo -e "a\nb\nc" | wc -l) | |
| 130 | -if [ "$COUNT" -eq 3 ]; then | |
| 131 | - pass "Pipe with wc" | |
| 132 | -else | |
| 133 | - fail "Pipe with wc" "Expected 3, got $COUNT" | |
| 134 | -fi | |
| 135 | - | |
| 136 | -# Test 3.5: Three-stage pipe | |
| 137 | -if echo "HELLO" | tr '[:upper:]' '[:lower:]' | grep -q "hello"; then | |
| 138 | - pass "Three-stage pipe" | |
| 139 | -else | |
| 140 | - fail "Three-stage pipe" "Complex pipe failed" | |
| 141 | -fi | |
| 142 | - | |
| 143 | -section "4. COMMAND SUBSTITUTION" | |
| 144 | - | |
| 145 | -# Test 4.1: Backticks | |
| 146 | -if [ "$(echo test)" = "test" ]; then | |
| 147 | - pass "Command substitution with $()" | |
| 148 | -else | |
| 149 | - fail "Command substitution with $()" "Substitution failed" | |
| 150 | -fi | |
| 151 | - | |
| 152 | -# Test 4.2: Nested substitution | |
| 153 | -if [ "$(echo $(echo nested))" = "nested" ]; then | |
| 154 | - pass "Nested command substitution" | |
| 155 | -else | |
| 156 | - fail "Nested command substitution" "Nested substitution failed" | |
| 157 | -fi | |
| 158 | - | |
| 159 | -# Test 4.3: Substitution in echo | |
| 160 | -OUTPUT=$(echo "Result: $(echo success)") | |
| 161 | -if echo "$OUTPUT" | grep -q "Result: success"; then | |
| 162 | - pass "Command substitution in echo" | |
| 163 | -else | |
| 164 | - fail "Command substitution in echo" "Got: $OUTPUT" | |
| 165 | -fi | |
| 166 | - | |
| 167 | -section "5. VARIABLES" | |
| 168 | - | |
| 169 | -# Test 5.1: Simple variable assignment | |
| 170 | -VAR1="value" | |
| 171 | -if [ "$VAR1" = "value" ]; then | |
| 172 | - pass "Simple variable assignment" | |
| 173 | -else | |
| 174 | - fail "Simple variable assignment" "Variable not set" | |
| 175 | -fi | |
| 176 | - | |
| 177 | -# Test 5.2: Variable in command | |
| 178 | -FILE="/tmp/fortsh_test_var" | |
| 179 | -if echo "data" > "$FILE" && [ -f "$FILE" ]; then | |
| 180 | - pass "Variable in command" | |
| 181 | -else | |
| 182 | - fail "Variable in command" "File not created" | |
| 183 | -fi | |
| 184 | - | |
| 185 | -# Test 5.3: Command substitution to variable | |
| 186 | -LINES=$(echo -e "a\nb" | wc -l) | |
| 187 | -if [ "$LINES" -eq 2 ]; then | |
| 188 | - pass "Command substitution to variable" | |
| 189 | -else | |
| 190 | - fail "Command substitution to variable" "Expected 2, got $LINES" | |
| 191 | -fi | |
| 192 | - | |
| 193 | -section "6. PARAMETER EXPANSION" | |
| 194 | - | |
| 195 | -# Test 6.1: Default value | |
| 196 | -UNSET_VAR="" | |
| 197 | -if [ "${UNSET_VAR:-default}" = "default" ]; then | |
| 198 | - pass "Parameter expansion with default (:-)" | |
| 199 | -else | |
| 200 | - fail "Parameter expansion with default" "Default not used" | |
| 201 | -fi | |
| 202 | - | |
| 203 | -# Test 6.2: String length | |
| 204 | -STR="hello" | |
| 205 | -if [ "${#STR}" -eq 5 ]; then | |
| 206 | - pass "String length expansion (${#var})" | |
| 207 | -else | |
| 208 | - fail "String length expansion" "Expected 5, got ${#STR}" | |
| 209 | -fi | |
| 210 | - | |
| 211 | -# Test 6.3: Substring removal | |
| 212 | -PATH_VAR="path/to/file.txt" | |
| 213 | -if [ "${PATH_VAR##*/}" = "file.txt" ]; then | |
| 214 | - pass "Remove prefix (##)" | |
| 215 | -else | |
| 216 | - fail "Remove prefix (##)" "Got: ${PATH_VAR##*/}" | |
| 217 | -fi | |
| 218 | - | |
| 219 | -section "7. BRACE EXPANSION" | |
| 220 | - | |
| 221 | -# Test 7.1: Numeric range | |
| 222 | -RESULT=$(echo {1..3}) | |
| 223 | -if [ "$RESULT" = "1 2 3" ]; then | |
| 224 | - pass "Brace expansion numeric range {1..3}" | |
| 225 | -else | |
| 226 | - fail "Brace expansion numeric range" "Got: $RESULT" | |
| 227 | -fi | |
| 228 | - | |
| 229 | -# Test 7.2: String list | |
| 230 | -RESULT=$(echo {a,b,c}) | |
| 231 | -if echo "$RESULT" | grep -q "a b c"; then | |
| 232 | - pass "Brace expansion string list {a,b,c}" | |
| 233 | -else | |
| 234 | - fail "Brace expansion string list" "Got: $RESULT" | |
| 235 | -fi | |
| 236 | - | |
| 237 | -section "8. GLOB PATTERNS" | |
| 238 | - | |
| 239 | -# Setup test files | |
| 240 | -touch /tmp/fortsh_test_a.txt /tmp/fortsh_test_b.txt /tmp/fortsh_test_c.dat | |
| 241 | - | |
| 242 | -# Test 8.1: Wildcard * | |
| 243 | -COUNT=$(ls /tmp/fortsh_test_*.txt 2>/dev/null | wc -l) | |
| 244 | -if [ "$COUNT" -eq 2 ]; then | |
| 245 | - pass "Glob wildcard (*)" | |
| 246 | -else | |
| 247 | - fail "Glob wildcard (*)" "Expected 2 files, got $COUNT" | |
| 248 | -fi | |
| 249 | - | |
| 250 | -# Test 8.2: Single char ? | |
| 251 | -if ls /tmp/fortsh_test_?.txt 2>/dev/null | grep -q "fortsh_test_a"; then | |
| 252 | - pass "Glob single char (?)" | |
| 253 | -else | |
| 254 | - fail "Glob single char (?)" "Pattern didn't match" | |
| 255 | -fi | |
| 256 | - | |
| 257 | -section "9. ARITHMETIC" | |
| 258 | - | |
| 259 | -# Test 9.1: Basic arithmetic | |
| 260 | -if [ $((5 + 3)) -eq 8 ]; then | |
| 261 | - pass "Arithmetic expansion (( ))" | |
| 262 | -else | |
| 263 | - fail "Arithmetic expansion" "5 + 3 != 8" | |
| 264 | -fi | |
| 265 | - | |
| 266 | -# Test 9.2: Multiplication | |
| 267 | -if [ $((4 * 3)) -eq 12 ]; then | |
| 268 | - pass "Arithmetic multiplication" | |
| 269 | -else | |
| 270 | - fail "Arithmetic multiplication" "4 * 3 != 12" | |
| 271 | -fi | |
| 272 | - | |
| 273 | -# Test 9.3: Variables in arithmetic | |
| 274 | -NUM=10 | |
| 275 | -if [ $((NUM + 5)) -eq 15 ]; then | |
| 276 | - pass "Variables in arithmetic" | |
| 277 | -else | |
| 278 | - fail "Variables in arithmetic" "NUM + 5 != 15" | |
| 279 | -fi | |
| 280 | - | |
| 281 | -section "10. CONTROL FLOW" | |
| 282 | - | |
| 283 | -# Test 10.1: If statement | |
| 284 | -if true; then | |
| 285 | - pass "If statement (true)" | |
| 286 | -else | |
| 287 | - fail "If statement" "If didn't execute" | |
| 288 | -fi | |
| 289 | - | |
| 290 | -# Test 10.2: If-else | |
| 291 | -if false; then | |
| 292 | - fail "If-else statement" "False branch executed" | |
| 293 | -else | |
| 294 | - pass "If-else statement" | |
| 295 | -fi | |
| 296 | - | |
| 297 | -# Test 10.3: For loop | |
| 298 | -COUNT=0 | |
| 299 | -for i in 1 2 3; do | |
| 300 | - COUNT=$((COUNT + 1)) | |
| 301 | -done | |
| 302 | -if [ "$COUNT" -eq 3 ]; then | |
| 303 | - pass "For loop" | |
| 304 | -else | |
| 305 | - fail "For loop" "Expected 3 iterations, got $COUNT" | |
| 306 | -fi | |
| 307 | - | |
| 308 | -# Test 10.4: While loop | |
| 309 | -COUNT=0 | |
| 310 | -NUM=3 | |
| 311 | -while [ $NUM -gt 0 ]; do | |
| 312 | - COUNT=$((COUNT + 1)) | |
| 313 | - NUM=$((NUM - 1)) | |
| 314 | -done | |
| 315 | -if [ "$COUNT" -eq 3 ]; then | |
| 316 | - pass "While loop" | |
| 317 | -else | |
| 318 | - fail "While loop" "Expected 3 iterations, got $COUNT" | |
| 319 | -fi | |
| 320 | - | |
| 321 | -section "11. TEST OPERATORS" | |
| 322 | - | |
| 323 | -# Test 11.1: File exists | |
| 324 | -touch /tmp/fortsh_test_exists | |
| 325 | -if [ -f /tmp/fortsh_test_exists ]; then | |
| 326 | - pass "Test file exists (-f)" | |
| 327 | -else | |
| 328 | - fail "Test file exists" "File not detected" | |
| 329 | -fi | |
| 330 | - | |
| 331 | -# Test 11.2: Directory exists | |
| 332 | -if [ -d /tmp ]; then | |
| 333 | - pass "Test directory exists (-d)" | |
| 334 | -else | |
| 335 | - fail "Test directory exists" "Directory not detected" | |
| 336 | -fi | |
| 337 | - | |
| 338 | -# Test 11.3: String equality | |
| 339 | -if [ "abc" = "abc" ]; then | |
| 340 | - pass "String equality (=)" | |
| 341 | -else | |
| 342 | - fail "String equality" "Strings don't match" | |
| 343 | -fi | |
| 344 | - | |
| 345 | -# Test 11.4: Numeric comparison | |
| 346 | -if [ 5 -gt 3 ]; then | |
| 347 | - pass "Numeric comparison (-gt)" | |
| 348 | -else | |
| 349 | - fail "Numeric comparison" "5 not greater than 3" | |
| 350 | -fi | |
| 351 | - | |
| 352 | -# Test 11.5: Advanced test [[]] | |
| 353 | -if [[ "hello" == "hello" ]]; then | |
| 354 | - pass "Advanced test [[ ]]" | |
| 355 | -else | |
| 356 | - fail "Advanced test [[ ]]" "Test failed" | |
| 357 | -fi | |
| 358 | - | |
| 359 | -section "12. HERE DOCUMENTS" | |
| 360 | - | |
| 361 | -# Test 12.1: Simple here doc | |
| 362 | -OUTPUT=$(cat <<EOF | |
| 363 | -line1 | |
| 364 | -line2 | |
| 365 | -EOF | |
| 366 | -) | |
| 367 | -if echo "$OUTPUT" | grep -q "line1"; then | |
| 368 | - pass "Here document (<<EOF)" | |
| 369 | -else | |
| 370 | - fail "Here document" "Content not captured" | |
| 371 | -fi | |
| 372 | - | |
| 373 | -# Test 12.2: Here doc with variables | |
| 374 | -NAME="fortsh" | |
| 375 | -OUTPUT=$(cat <<EOF | |
| 376 | -Hello $NAME | |
| 377 | -EOF | |
| 378 | -) | |
| 379 | -if echo "$OUTPUT" | grep -q "Hello fortsh"; then | |
| 380 | - pass "Here document with variable expansion" | |
| 381 | -else | |
| 382 | - fail "Here document with variable expansion" "Variable not expanded" | |
| 383 | -fi | |
| 384 | - | |
| 385 | -section "13. BACKGROUND JOBS" | |
| 386 | - | |
| 387 | -# Test 13.1: Background execution | |
| 388 | -(sleep 0.1 && echo "done" > /tmp/fortsh_test_bg) & | |
| 389 | -BG_PID=$! | |
| 390 | -sleep 0.2 | |
| 391 | -if [ -f /tmp/fortsh_test_bg ]; then | |
| 392 | - pass "Background job execution (&)" | |
| 393 | -else | |
| 394 | - fail "Background job execution" "Background job didn't complete" | |
| 395 | -fi | |
| 396 | - | |
| 397 | -# Test 13.2: $! captures PID | |
| 398 | -(sleep 0.1) & | |
| 399 | -LAST_PID=$! | |
| 400 | -if [ -n "$LAST_PID" ] && [ "$LAST_PID" -gt 0 ]; then | |
| 401 | - pass "Background job PID (\$!)" | |
| 402 | -else | |
| 403 | - fail "Background job PID" "PID not captured: $LAST_PID" | |
| 404 | -fi | |
| 405 | - | |
| 406 | -section "14. SPECIAL VARIABLES" | |
| 407 | - | |
| 408 | -# Test 14.1: $? exit status | |
| 409 | -true | |
| 410 | -if [ $? -eq 0 ]; then | |
| 411 | - pass "Exit status (\$?)" | |
| 412 | -else | |
| 413 | - fail "Exit status" "Expected 0, got $?" | |
| 414 | -fi | |
| 415 | - | |
| 416 | -# Test 14.2: $$ PID | |
| 417 | -if [ -n "$$" ] && [ "$$" -gt 0 ]; then | |
| 418 | - pass "Current PID (\$\$)" | |
| 419 | -else | |
| 420 | - fail "Current PID" "PID not set: $$" | |
| 421 | -fi | |
| 422 | - | |
| 423 | -section "15. FUNCTIONS" | |
| 424 | - | |
| 425 | -# Test 15.1: Simple function | |
| 426 | -test_func() { | |
| 427 | - echo "success" | |
| 428 | -} | |
| 429 | -if [ "$(test_func)" = "success" ]; then | |
| 430 | - pass "Function definition and call" | |
| 431 | -else | |
| 432 | - fail "Function definition and call" "Function didn't execute" | |
| 433 | -fi | |
| 434 | - | |
| 435 | -# Test 15.2: Function with parameters | |
| 436 | -test_params() { | |
| 437 | - echo "$1-$2" | |
| 438 | -} | |
| 439 | -if [ "$(test_params a b)" = "a-b" ]; then | |
| 440 | - pass "Function with parameters" | |
| 441 | -else | |
| 442 | - fail "Function with parameters" "Parameters not passed" | |
| 443 | -fi | |
| 444 | - | |
| 445 | -# Test 15.3: Function return value | |
| 446 | -test_return() { | |
| 447 | - return 42 | |
| 448 | -} | |
| 449 | -test_return | |
| 450 | -if [ $? -eq 42 ]; then | |
| 451 | - pass "Function return value" | |
| 452 | -else | |
| 453 | - fail "Function return value" "Expected 42, got $?" | |
| 454 | -fi | |
| 455 | - | |
| 456 | -section "16. ARRAYS" | |
| 457 | - | |
| 458 | -# Test 16.1: Indexed array | |
| 459 | -arr=(one two three) | |
| 460 | -if [ "${arr[0]}" = "one" ]; then | |
| 461 | - pass "Indexed array access" | |
| 462 | -else | |
| 463 | - fail "Indexed array access" "Element 0: ${arr[0]}" | |
| 464 | -fi | |
| 465 | - | |
| 466 | -# Test 16.2: Array length | |
| 467 | -if [ "${#arr[@]}" -eq 3 ]; then | |
| 468 | - pass "Array length (${#arr[@]})" | |
| 469 | -else | |
| 470 | - fail "Array length" "Expected 3, got ${#arr[@]}" | |
| 471 | -fi | |
| 472 | - | |
| 473 | -# Test 16.3: Array iteration | |
| 474 | -COUNT=0 | |
| 475 | -for item in "${arr[@]}"; do | |
| 476 | - COUNT=$((COUNT + 1)) | |
| 477 | -done | |
| 478 | -if [ "$COUNT" -eq 3 ]; then | |
| 479 | - pass "Array iteration" | |
| 480 | -else | |
| 481 | - fail "Array iteration" "Expected 3, got $COUNT" | |
| 482 | -fi | |
| 483 | - | |
| 484 | -section "17. QUOTING" | |
| 485 | - | |
| 486 | -# Test 17.1: Double quotes preserve variables | |
| 487 | -VAR="world" | |
| 488 | -if [ "$(echo "hello $VAR")" = "hello world" ]; then | |
| 489 | - pass "Double quotes with variables" | |
| 490 | -else | |
| 491 | - fail "Double quotes with variables" "Variable not expanded" | |
| 492 | -fi | |
| 493 | - | |
| 494 | -# Test 17.2: Single quotes literal | |
| 495 | -if [ '$(echo test)' = '$(echo test)' ]; then | |
| 496 | - pass "Single quotes literal" | |
| 497 | -else | |
| 498 | - fail "Single quotes literal" "Command was executed" | |
| 499 | -fi | |
| 500 | - | |
| 501 | -section "18. COMPLEX COMBINATIONS" | |
| 502 | - | |
| 503 | -# Test 18.1: Pipe + redirection | |
| 504 | -if echo "test" | cat > /tmp/fortsh_test_combo1 && grep -q "test" /tmp/fortsh_test_combo1; then | |
| 505 | - pass "Pipe + redirection" | |
| 506 | -else | |
| 507 | - fail "Pipe + redirection" "Combination failed" | |
| 508 | -fi | |
| 509 | - | |
| 510 | -# Test 18.2: Command substitution + pipe | |
| 511 | -if echo "$(echo hello | tr '[:lower:]' '[:upper:]')" | grep -q "HELLO"; then | |
| 512 | - pass "Command substitution + pipe" | |
| 513 | -else | |
| 514 | - fail "Command substitution + pipe" "Combination failed" | |
| 515 | -fi | |
| 516 | - | |
| 517 | -# Test 18.3: Multiple redirections | |
| 518 | -if { echo "out"; echo "err" >&2; } >>/tmp/fortsh_test_out 2>>/tmp/fortsh_test_err; then | |
| 519 | - pass "Multiple redirections" | |
| 520 | -else | |
| 521 | - fail "Multiple redirections" "Complex redirection failed" | |
| 522 | -fi | |
| 523 | - | |
| 524 | -section "SUMMARY" | |
| 525 | -echo "" | |
| 526 | -echo "==========================================" | |
| 527 | -echo "RESULTS" | |
| 528 | -echo "==========================================" | |
| 529 | -echo -e "${GREEN}Passed:${NC} $PASSED" | |
| 530 | -echo -e "${RED}Failed:${NC} $FAILED" | |
| 531 | -echo -e "${YELLOW}Skipped:${NC} $SKIPPED" | |
| 532 | -echo "Total: $((PASSED + FAILED + SKIPPED))" | |
| 533 | -echo "==========================================" | |
| 534 | - | |
| 535 | -PASS_RATE=$((PASSED * 100 / (PASSED + FAILED))) | |
| 536 | -echo "Pass rate: ${PASS_RATE}%" | |
| 537 | - | |
| 538 | -if [ $FAILED -eq 0 ]; then | |
| 539 | - echo -e "${GREEN}ALL TESTS PASSED!${NC} ✓" | |
| 540 | - exit 0 | |
| 541 | -else | |
| 542 | - echo -e "${RED}SOME TESTS FAILED${NC} ✗" | |
| 543 | - exit 1 | |
| 544 | -fi | |
tests/integration_test.shdeleted@@ -1,122 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# ===================================== | |
| 3 | -# Integration Test Script for fortsh | |
| 4 | -# ===================================== | |
| 5 | - | |
| 6 | -set -e # Exit on any error | |
| 7 | - | |
| 8 | -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| 9 | -FORTSH_DIR="$(dirname "$SCRIPT_DIR")" | |
| 10 | -FORTSH_BIN="$FORTSH_DIR/bin/fortsh" | |
| 11 | - | |
| 12 | -echo "======================================" | |
| 13 | -echo "Fortsh Integration Test Suite" | |
| 14 | -echo "======================================" | |
| 15 | -echo "" | |
| 16 | - | |
| 17 | -# Check if fortsh binary exists | |
| 18 | -if [[ ! -x "$FORTSH_BIN" ]]; then | |
| 19 | - echo "❌ fortsh binary not found at $FORTSH_BIN" | |
| 20 | - echo "Please run 'make' in the main directory first" | |
| 21 | - exit 1 | |
| 22 | -fi | |
| 23 | - | |
| 24 | -echo "✅ Found fortsh binary at $FORTSH_BIN" | |
| 25 | -echo "" | |
| 26 | - | |
| 27 | -# Test 1: Basic command execution | |
| 28 | -echo "Test 1: Basic command execution" | |
| 29 | -echo "-------------------------------" | |
| 30 | -result=$(echo "echo hello world" | "$FORTSH_BIN" 2>/dev/null | grep "hello world") | |
| 31 | -if [[ "$result" == "hello world" ]]; then | |
| 32 | - echo "✅ Basic echo command works" | |
| 33 | -else | |
| 34 | - echo "❌ Basic echo command failed" | |
| 35 | - echo "Expected: 'hello world', Got: '$result'" | |
| 36 | -fi | |
| 37 | - | |
| 38 | -# Test 2: Variable expansion | |
| 39 | -echo "" | |
| 40 | -echo "Test 2: Variable expansion" | |
| 41 | -echo "-------------------------" | |
| 42 | -result=$(echo -e "TEST=fortsh\necho \$TEST" | "$FORTSH_BIN" 2>/dev/null | tail -1) | |
| 43 | -if [[ "$result" == "fortsh" ]]; then | |
| 44 | - echo "✅ Variable expansion works" | |
| 45 | -else | |
| 46 | - echo "❌ Variable expansion failed" | |
| 47 | - echo "Expected: 'fortsh', Got: '$result'" | |
| 48 | -fi | |
| 49 | - | |
| 50 | -# Test 3: Glob pattern matching | |
| 51 | -echo "" | |
| 52 | -echo "Test 3: Glob pattern matching" | |
| 53 | -echo "-----------------------------" | |
| 54 | -result=$(echo "echo *.txt" | "$FORTSH_BIN" 2>/dev/null | grep -c "txt") | |
| 55 | -if [[ "$result" -gt 0 ]]; then | |
| 56 | - echo "✅ Glob pattern matching works" | |
| 57 | -else | |
| 58 | - echo "❌ Glob pattern matching failed" | |
| 59 | -fi | |
| 60 | - | |
| 61 | -# Test 4: Here-string redirection | |
| 62 | -echo "" | |
| 63 | -echo "Test 4: Here-string redirection" | |
| 64 | -echo "-------------------------------" | |
| 65 | -result=$(echo "cat <<< hello" | "$FORTSH_BIN" 2>/dev/null) | |
| 66 | -if [[ "$result" == "hello" ]]; then | |
| 67 | - echo "✅ Here-string redirection works" | |
| 68 | -else | |
| 69 | - echo "❌ Here-string redirection failed" | |
| 70 | - echo "Expected: 'hello', Got: '$result'" | |
| 71 | -fi | |
| 72 | - | |
| 73 | -# Test 5: For loop basic functionality | |
| 74 | -echo "" | |
| 75 | -echo "Test 5: For loop functionality" | |
| 76 | -echo "------------------------------" | |
| 77 | -result=$(echo -e "for i in a b c\ndo\necho item-\$i\ndone" | "$FORTSH_BIN" 2>/dev/null | head -1) | |
| 78 | -if [[ "$result" == "item-a" ]]; then | |
| 79 | - echo "✅ For loop basic functionality works" | |
| 80 | -else | |
| 81 | - echo "❌ For loop functionality failed" | |
| 82 | - echo "Expected: 'item-a', Got: '$result'" | |
| 83 | -fi | |
| 84 | - | |
| 85 | -# Test 6: Built-in commands | |
| 86 | -echo "" | |
| 87 | -echo "Test 6: Built-in commands" | |
| 88 | -echo "-------------------------" | |
| 89 | -result=$(echo "pwd" | "$FORTSH_BIN" 2>/dev/null) | |
| 90 | -if [[ -n "$result" ]]; then | |
| 91 | - echo "✅ Built-in commands work (pwd returned: $result)" | |
| 92 | -else | |
| 93 | - echo "❌ Built-in commands failed" | |
| 94 | -fi | |
| 95 | - | |
| 96 | -# Test 7: Alias functionality | |
| 97 | -echo "" | |
| 98 | -echo "Test 7: Alias functionality" | |
| 99 | -echo "---------------------------" | |
| 100 | -result=$(echo -e "alias ll='echo long-list'\nll" | "$FORTSH_BIN" 2>/dev/null | tail -1) | |
| 101 | -if [[ "$result" == "long-list" ]]; then | |
| 102 | - echo "✅ Alias functionality works" | |
| 103 | -else | |
| 104 | - echo "❌ Alias functionality failed" | |
| 105 | - echo "Expected: 'long-list', Got: '$result'" | |
| 106 | -fi | |
| 107 | - | |
| 108 | -# Test 8: Error handling | |
| 109 | -echo "" | |
| 110 | -echo "Test 8: Error handling" | |
| 111 | -echo "----------------------" | |
| 112 | -result=$(echo "nonexistent_command_xyz123" | "$FORTSH_BIN" 2>&1 | grep -c "command not found") | |
| 113 | -if [[ "$result" -gt 0 ]]; then | |
| 114 | - echo "✅ Error handling works" | |
| 115 | -else | |
| 116 | - echo "❌ Error handling failed" | |
| 117 | -fi | |
| 118 | - | |
| 119 | -echo "" | |
| 120 | -echo "======================================" | |
| 121 | -echo "Integration tests completed!" | |
| 122 | -echo "======================================" | |
tests/posix_compliance_extended.shdeleted@@ -1,368 +0,0 @@ | ||
| 1 | -#!/bin/sh | |
| 2 | -# ===================================== | |
| 3 | -# POSIX Compliance Extended Test Suite for fortsh | |
| 4 | -# ===================================== | |
| 5 | -# Comprehensive POSIX compliance testing based on IEEE Std 1003.1-2017 | |
| 6 | -# This extends the basic test suite with additional coverage | |
| 7 | - | |
| 8 | -# Colors (POSIX-compliant way) | |
| 9 | -RED='\033[0;31m' | |
| 10 | -GREEN='\033[0;32m' | |
| 11 | -YELLOW='\033[1;33m' | |
| 12 | -BLUE='\033[0;34m' | |
| 13 | -NC='\033[0m' | |
| 14 | - | |
| 15 | -PASSED=0 | |
| 16 | -FAILED=0 | |
| 17 | -SKIPPED=0 | |
| 18 | - | |
| 19 | -# Get script directory (POSIX way) | |
| 20 | -SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | |
| 21 | -FORTSH_BIN="${FORTSH_BIN:-$SCRIPT_DIR/../bin/fortsh}" | |
| 22 | - | |
| 23 | -# Check if fortsh exists | |
| 24 | -if [ ! -x "$FORTSH_BIN" ]; then | |
| 25 | - printf "${RED}ERROR${NC}: fortsh binary not found at $FORTSH_BIN\n" | |
| 26 | - printf "Please run 'make' first or set FORTSH_BIN environment variable\n" | |
| 27 | - exit 1 | |
| 28 | -fi | |
| 29 | - | |
| 30 | -# Test result trackers | |
| 31 | -pass() { | |
| 32 | - printf "${GREEN}✓ PASS${NC}: %s\n" "$1" | |
| 33 | - PASSED=$((PASSED + 1)) | |
| 34 | -} | |
| 35 | - | |
| 36 | -fail() { | |
| 37 | - printf "${RED}✗ FAIL${NC}: %s\n" "$1" | |
| 38 | - if [ -n "$2" ]; then | |
| 39 | - printf " posix: %s\n" "$2" | |
| 40 | - fi | |
| 41 | - if [ -n "$3" ]; then | |
| 42 | - printf " fortsh: %s\n" "$3" | |
| 43 | - fi | |
| 44 | - FAILED=$((FAILED + 1)) | |
| 45 | -} | |
| 46 | - | |
| 47 | -skip() { | |
| 48 | - printf "${YELLOW}⊘ SKIP${NC}: %s - %s\n" "$1" "$2" | |
| 49 | - SKIPPED=$((SKIPPED + 1)) | |
| 50 | -} | |
| 51 | - | |
| 52 | -section() { | |
| 53 | - printf "\n" | |
| 54 | - printf "${BLUE}==========================================\n" | |
| 55 | - printf "%s\n" "$1" | |
| 56 | - printf "==========================================${NC}\n" | |
| 57 | -} | |
| 58 | - | |
| 59 | -# Helper function to run command in both shells and compare | |
| 60 | -compare_posix_output() { | |
| 61 | - test_name="$1" | |
| 62 | - command="$2" | |
| 63 | - posix_file="/tmp/posix_ext_$$_posix" | |
| 64 | - fortsh_file="/tmp/posix_ext_$$_fortsh" | |
| 65 | - | |
| 66 | - # Run in POSIX shell (sh) | |
| 67 | - sh -c "$command" > "$posix_file" 2>&1 || true | |
| 68 | - | |
| 69 | - # Run in fortsh | |
| 70 | - "$FORTSH_BIN" -c "$command" > "$fortsh_file" 2>&1 || true | |
| 71 | - | |
| 72 | - # Compare outputs | |
| 73 | - if diff -q "$posix_file" "$fortsh_file" > /dev/null 2>&1; then | |
| 74 | - pass "$test_name" | |
| 75 | - else | |
| 76 | - fail "$test_name" "$(cat "$posix_file")" "$(cat "$fortsh_file")" | |
| 77 | - fi | |
| 78 | - | |
| 79 | - rm -f "$posix_file" "$fortsh_file" | |
| 80 | -} | |
| 81 | - | |
| 82 | -# Helper function to compare exit codes | |
| 83 | -compare_posix_exit_code() { | |
| 84 | - test_name="$1" | |
| 85 | - command="$2" | |
| 86 | - | |
| 87 | - sh -c "$command" > /dev/null 2>&1 | |
| 88 | - posix_exit=$? | |
| 89 | - | |
| 90 | - "$FORTSH_BIN" -c "$command" > /dev/null 2>&1 | |
| 91 | - fortsh_exit=$? | |
| 92 | - | |
| 93 | - if [ "$posix_exit" -eq "$fortsh_exit" ]; then | |
| 94 | - pass "$test_name" | |
| 95 | - else | |
| 96 | - fail "$test_name" "exit=$posix_exit" "exit=$fortsh_exit" | |
| 97 | - fi | |
| 98 | -} | |
| 99 | - | |
| 100 | -# Cleanup | |
| 101 | -cleanup() { | |
| 102 | - rm -f /tmp/posix_ext_$$_* 2>/dev/null | |
| 103 | - rm -f /tmp/posix_test_* 2>/dev/null | |
| 104 | - rm -rf /tmp/posix_test_dir 2>/dev/null | |
| 105 | -} | |
| 106 | -trap cleanup EXIT INT TERM | |
| 107 | - | |
| 108 | -# Setup test environment | |
| 109 | -mkdir -p /tmp/posix_test_dir | |
| 110 | -cd /tmp/posix_test_dir || exit 1 | |
| 111 | - | |
| 112 | -section "26. EXTENDED TEST COMMAND - FILE TYPE TESTS" | |
| 113 | - | |
| 114 | -# Create test files with different properties | |
| 115 | -touch /tmp/posix_test_regular | |
| 116 | -chmod 644 /tmp/posix_test_regular | |
| 117 | -echo "data" > /tmp/posix_test_nonempty | |
| 118 | -mkdir -p /tmp/posix_test_directory | |
| 119 | -mkfifo /tmp/posix_test_fifo 2>/dev/null || skip "mkfifo" "not available" | |
| 120 | -ln -sf /tmp/posix_test_regular /tmp/posix_test_symlink 2>/dev/null | |
| 121 | - | |
| 122 | -compare_posix_exit_code "test -e exists" "test -e /tmp/posix_test_regular" | |
| 123 | -compare_posix_exit_code "test -e nonexistent" "! test -e /tmp/nonexistent_xyz_$$" | |
| 124 | -compare_posix_exit_code "test -f regular file" "test -f /tmp/posix_test_regular" | |
| 125 | -compare_posix_exit_code "test -d directory" "test -d /tmp/posix_test_directory" | |
| 126 | -compare_posix_exit_code "test -s non-empty" "test -s /tmp/posix_test_nonempty" | |
| 127 | -compare_posix_exit_code "test -s empty" "! test -s /tmp/posix_test_regular" | |
| 128 | - | |
| 129 | -# Symlink tests (if supported) | |
| 130 | -if [ -L /tmp/posix_test_symlink ]; then | |
| 131 | - compare_posix_exit_code "test -L symlink" "test -L /tmp/posix_test_symlink" | |
| 132 | - compare_posix_exit_code "test -h symlink" "test -h /tmp/posix_test_symlink" | |
| 133 | -fi | |
| 134 | - | |
| 135 | -# FIFO tests (if created successfully) | |
| 136 | -if [ -p /tmp/posix_test_fifo ]; then | |
| 137 | - compare_posix_exit_code "test -p fifo" "test -p /tmp/posix_test_fifo" | |
| 138 | -fi | |
| 139 | - | |
| 140 | -section "27. EXTENDED TEST COMMAND - FILE PERMISSION TESTS" | |
| 141 | - | |
| 142 | -# Create files with specific permissions | |
| 143 | -touch /tmp/posix_test_readable | |
| 144 | -chmod 644 /tmp/posix_test_readable | |
| 145 | -touch /tmp/posix_test_writable | |
| 146 | -chmod 644 /tmp/posix_test_writable | |
| 147 | -touch /tmp/posix_test_executable | |
| 148 | -chmod 755 /tmp/posix_test_executable | |
| 149 | - | |
| 150 | -compare_posix_exit_code "test -r readable" "test -r /tmp/posix_test_readable" | |
| 151 | -compare_posix_exit_code "test -w writable" "test -w /tmp/posix_test_writable" | |
| 152 | -compare_posix_exit_code "test -x executable" "test -x /tmp/posix_test_executable" | |
| 153 | -compare_posix_exit_code "test ! -x nonexec" "! test -x /tmp/posix_test_readable" | |
| 154 | - | |
| 155 | -section "28. EXTENDED TEST COMMAND - FILE COMPARISON" | |
| 156 | - | |
| 157 | -# Create files for comparison | |
| 158 | -echo "old" > /tmp/posix_test_old | |
| 159 | -sleep 1 | |
| 160 | -echo "new" > /tmp/posix_test_new | |
| 161 | -ln -f /tmp/posix_test_old /tmp/posix_test_hardlink 2>/dev/null | |
| 162 | - | |
| 163 | -compare_posix_exit_code "test -nt newer than" "test /tmp/posix_test_new -nt /tmp/posix_test_old" | |
| 164 | -compare_posix_exit_code "test -ot older than" "test /tmp/posix_test_old -ot /tmp/posix_test_new" | |
| 165 | - | |
| 166 | -# Hard link test (if supported) | |
| 167 | -if [ -f /tmp/posix_test_hardlink ]; then | |
| 168 | - compare_posix_exit_code "test -ef same file" "test /tmp/posix_test_old -ef /tmp/posix_test_hardlink" | |
| 169 | -fi | |
| 170 | - | |
| 171 | -section "29. EXTENDED TEST COMMAND - STRING TESTS" | |
| 172 | - | |
| 173 | -compare_posix_exit_code "test -n nonempty string" "test -n 'hello'" | |
| 174 | -compare_posix_exit_code "test -z empty string" "test -z ''" | |
| 175 | -compare_posix_exit_code "test string = equal" "test 'abc' = 'abc'" | |
| 176 | -compare_posix_exit_code "test string != not equal" "test 'abc' != 'xyz'" | |
| 177 | -compare_posix_exit_code "test unary string" "test 'hello'" | |
| 178 | -compare_posix_exit_code "test ! negation" "! test -z 'hello'" | |
| 179 | - | |
| 180 | -section "30. EXTENDED TEST COMMAND - INTEGER COMPARISON" | |
| 181 | - | |
| 182 | -compare_posix_exit_code "test -eq equal" "test 42 -eq 42" | |
| 183 | -compare_posix_exit_code "test -ne not equal" "test 42 -ne 13" | |
| 184 | -compare_posix_exit_code "test -gt greater" "test 10 -gt 5" | |
| 185 | -compare_posix_exit_code "test -ge greater or equal" "test 10 -ge 10" | |
| 186 | -compare_posix_exit_code "test -lt less" "test 5 -lt 10" | |
| 187 | -compare_posix_exit_code "test -le less or equal" "test 5 -le 5" | |
| 188 | -compare_posix_exit_code "test negative numbers" "test -5 -lt 0" | |
| 189 | - | |
| 190 | -section "31. EXTENDED TEST COMMAND - LOGICAL OPERATORS" | |
| 191 | - | |
| 192 | -compare_posix_exit_code "test -a and" "test 5 -gt 3 -a 10 -gt 8" | |
| 193 | -compare_posix_exit_code "test -o or" "test 5 -gt 10 -o 10 -gt 8" | |
| 194 | -compare_posix_exit_code "test ! negation" "! test 5 -gt 10" | |
| 195 | -compare_posix_exit_code "test ( ) grouping" "test \( 5 -gt 3 \) -a \( 10 -gt 8 \)" | |
| 196 | - | |
| 197 | -section "32. EXTENDED PARAMETER EXPANSION - DEFAULT VALUES" | |
| 198 | - | |
| 199 | -compare_posix_output "use default unset" 'unset VAR; echo "${VAR-default}"' | |
| 200 | -compare_posix_output "use default null" 'VAR=; echo "${VAR-default}"' | |
| 201 | -compare_posix_output "use default null colon" 'VAR=; echo "${VAR:-default}"' | |
| 202 | -compare_posix_output "use default set" 'VAR=value; echo "${VAR-default}"' | |
| 203 | -compare_posix_output "assign default unset" 'unset VAR; echo "${VAR=assigned}"; echo $VAR' | |
| 204 | -compare_posix_output "assign default null colon" 'VAR=; echo "${VAR:=assigned}"; echo $VAR' | |
| 205 | - | |
| 206 | -section "33. EXTENDED PARAMETER EXPANSION - ERROR IF UNSET" | |
| 207 | - | |
| 208 | -compare_posix_exit_code "error if unset" 'unset VAR; echo "${VAR?error}" 2>/dev/null' | |
| 209 | -compare_posix_exit_code "error if null colon" 'VAR=; echo "${VAR:?error}" 2>/dev/null' | |
| 210 | -compare_posix_output "no error if set" 'VAR=value; echo "${VAR?error}"' | |
| 211 | - | |
| 212 | -section "34. EXTENDED PARAMETER EXPANSION - ALTERNATIVE VALUE" | |
| 213 | - | |
| 214 | -compare_posix_output "alt unset" 'unset VAR; echo "${VAR+alternative}"' | |
| 215 | -compare_posix_output "alt null" 'VAR=; echo "${VAR+alternative}"' | |
| 216 | -compare_posix_output "alt null colon" 'VAR=; echo "${VAR:+alternative}"' | |
| 217 | -compare_posix_output "alt set" 'VAR=value; echo "${VAR+alternative}"' | |
| 218 | -compare_posix_output "alt set colon" 'VAR=value; echo "${VAR:+alternative}"' | |
| 219 | - | |
| 220 | -section "35. EXTENDED PARAMETER EXPANSION - STRING LENGTH" | |
| 221 | - | |
| 222 | -compare_posix_output "length empty" 'VAR=; echo "${#VAR}"' | |
| 223 | -compare_posix_output "length short" 'VAR=hi; echo "${#VAR}"' | |
| 224 | -compare_posix_output "length long" 'VAR=hello world; echo "${#VAR}"' | |
| 225 | -compare_posix_output "length special chars" 'VAR="a b c"; echo "${#VAR}"' | |
| 226 | - | |
| 227 | -section "36. EXTENDED PARAMETER EXPANSION - PATTERN REMOVAL" | |
| 228 | - | |
| 229 | -# Prefix removal | |
| 230 | -compare_posix_output "prefix # simple" 'VAR=hello; echo "${VAR#hel}"' | |
| 231 | -compare_posix_output "prefix # nomatch" 'VAR=hello; echo "${VAR#xyz}"' | |
| 232 | -compare_posix_output "prefix # glob" 'VAR=foo.bar.baz; echo "${VAR#*.}"' | |
| 233 | -compare_posix_output "prefix ## glob" 'VAR=foo.bar.baz; echo "${VAR##*.}"' | |
| 234 | -compare_posix_output "prefix # star" 'VAR=/usr/local/bin; echo "${VAR#*/}"' | |
| 235 | -compare_posix_output "prefix ## star" 'VAR=/usr/local/bin; echo "${VAR##*/}"' | |
| 236 | - | |
| 237 | -# Suffix removal | |
| 238 | -compare_posix_output "suffix % simple" 'VAR=hello; echo "${VAR%lo}"' | |
| 239 | -compare_posix_output "suffix % nomatch" 'VAR=hello; echo "${VAR%xyz}"' | |
| 240 | -compare_posix_output "suffix % glob" 'VAR=foo.bar.baz; echo "${VAR%.*}"' | |
| 241 | -compare_posix_output "suffix %% glob" 'VAR=foo.bar.baz; echo "${VAR%%.*}"' | |
| 242 | -compare_posix_output "suffix % extension" 'VAR=file.tar.gz; echo "${VAR%.gz}"' | |
| 243 | -compare_posix_output "suffix %% extension" 'VAR=file.tar.gz; echo "${VAR%%.*}"' | |
| 244 | - | |
| 245 | -section "37. ARITHMETIC EXPANSION - BASIC OPERATIONS" | |
| 246 | - | |
| 247 | -compare_posix_output "arith addition" 'echo $((5 + 3))' | |
| 248 | -compare_posix_output "arith subtraction" 'echo $((10 - 4))' | |
| 249 | -compare_posix_output "arith multiplication" 'echo $((6 * 7))' | |
| 250 | -compare_posix_output "arith division" 'echo $((20 / 4))' | |
| 251 | -compare_posix_output "arith modulo" 'echo $((17 % 5))' | |
| 252 | -compare_posix_output "arith negative" 'echo $((-5 + 10))' | |
| 253 | -compare_posix_output "arith zero" 'echo $((0 + 0))' | |
| 254 | - | |
| 255 | -section "38. ARITHMETIC EXPANSION - PRECEDENCE" | |
| 256 | - | |
| 257 | -compare_posix_output "arith precedence mult" 'echo $((2 + 3 * 4))' | |
| 258 | -compare_posix_output "arith precedence paren" 'echo $(((2 + 3) * 4))' | |
| 259 | -compare_posix_output "arith precedence div" 'echo $((10 - 8 / 2))' | |
| 260 | -compare_posix_output "arith precedence complex" 'echo $((2 * 3 + 4 * 5))' | |
| 261 | - | |
| 262 | -section "39. ARITHMETIC EXPANSION - VARIABLES" | |
| 263 | - | |
| 264 | -compare_posix_output "arith var" 'X=5; echo $((X + 3))' | |
| 265 | -compare_posix_output "arith var no dollar" 'X=10; Y=20; echo $((X + Y))' | |
| 266 | -compare_posix_output "arith var assign" 'X=5; Y=$((X * 2)); echo $Y' | |
| 267 | -compare_posix_output "arith var complex" 'A=3; B=4; echo $((A * A + B * B))' | |
| 268 | - | |
| 269 | -section "40. ARITHMETIC EXPANSION - COMPARISON" | |
| 270 | - | |
| 271 | -compare_posix_output "arith compare eq true" 'echo $((5 == 5))' | |
| 272 | -compare_posix_output "arith compare eq false" 'echo $((5 == 3))' | |
| 273 | -compare_posix_output "arith compare ne true" 'echo $((5 != 3))' | |
| 274 | -compare_posix_output "arith compare ne false" 'echo $((5 != 5))' | |
| 275 | -compare_posix_output "arith compare lt" 'echo $((3 < 5))' | |
| 276 | -compare_posix_output "arith compare le" 'echo $((5 <= 5))' | |
| 277 | -compare_posix_output "arith compare gt" 'echo $((7 > 5))' | |
| 278 | -compare_posix_output "arith compare ge" 'echo $((5 >= 5))' | |
| 279 | - | |
| 280 | -section "41. ARITHMETIC EXPANSION - LOGICAL" | |
| 281 | - | |
| 282 | -compare_posix_output "arith logical and true" 'echo $((1 && 1))' | |
| 283 | -compare_posix_output "arith logical and false" 'echo $((1 && 0))' | |
| 284 | -compare_posix_output "arith logical or true" 'echo $((0 || 1))' | |
| 285 | -compare_posix_output "arith logical or false" 'echo $((0 || 0))' | |
| 286 | -compare_posix_output "arith logical not true" 'echo $((! 0))' | |
| 287 | -compare_posix_output "arith logical not false" 'echo $((! 1))' | |
| 288 | - | |
| 289 | -section "42. SPECIAL PARAMETERS" | |
| 290 | - | |
| 291 | -compare_posix_output "\$\$ process id type" 'echo $$ | grep -c "^[0-9][0-9]*$"' | |
| 292 | -compare_posix_output "\$- shell flags type" 'echo $- | grep -c "^[a-z]*$"' | |
| 293 | -compare_posix_output "\$# arg count" 'set -- a b c; echo $#' | |
| 294 | -compare_posix_output "\$1 first arg" 'set -- first second; echo $1' | |
| 295 | -compare_posix_output "\$2 second arg" 'set -- first second; echo $2' | |
| 296 | -compare_posix_output "\$9 ninth arg" 'set -- a b c d e f g h i j; echo $9' | |
| 297 | -compare_posix_output "\$* all args" 'set -- a b c; echo $*' | |
| 298 | -compare_posix_output "\$@ all args" 'set -- a b c; echo $@' | |
| 299 | - | |
| 300 | -section "43. ADDITIONAL POSIX BUILTINS - CD/PWD" | |
| 301 | - | |
| 302 | -compare_posix_output "cd to /tmp" 'cd /tmp && pwd' | |
| 303 | -compare_posix_output "cd relative" 'cd /tmp && cd .. && pwd | grep -c "^/"' | |
| 304 | -compare_posix_output "cd $HOME" 'cd && pwd | grep -c "^/"' | |
| 305 | - | |
| 306 | -section "44. ADDITIONAL POSIX BUILTINS - UNSET" | |
| 307 | - | |
| 308 | -compare_posix_output "unset variable" 'VAR=test; unset VAR; echo ${VAR:-unset}' | |
| 309 | -compare_posix_output "unset nonexistent" 'unset NONEXISTENT_VAR_XYZ; echo ok' | |
| 310 | - | |
| 311 | -section "45. ADDITIONAL POSIX BUILTINS - EVAL" | |
| 312 | - | |
| 313 | -compare_posix_output "eval simple" 'CMD="echo hello"; eval $CMD' | |
| 314 | -compare_posix_output "eval with var" 'X=5; CMD="echo \$X"; eval $CMD' | |
| 315 | -compare_posix_output "eval complex" 'A=echo; B=test; eval $A $B' | |
| 316 | - | |
| 317 | -section "46. ADDITIONAL POSIX BUILTINS - COLON" | |
| 318 | - | |
| 319 | -compare_posix_output ": null command" ': ; echo ok' | |
| 320 | -compare_posix_output ": with args" ': this is ignored; echo ok' | |
| 321 | -compare_posix_exit_code ": exit status" ':' | |
| 322 | - | |
| 323 | -section "47. TILDE EXPANSION" | |
| 324 | - | |
| 325 | -compare_posix_output "tilde home" 'echo ~ | grep -c "^/"' | |
| 326 | -compare_posix_output "tilde in path" 'echo ~/test | grep -c "^/"' | |
| 327 | - | |
| 328 | -section "48. FIELD SPLITTING - ADVANCED" | |
| 329 | - | |
| 330 | -compare_posix_output "IFS multiple fields" 'IFS=:; VAR="a:b:c:d"; set -- $VAR; echo $# $1 $4' | |
| 331 | -compare_posix_output "IFS whitespace" 'IFS=" "; VAR="a b c"; set -- $VAR; echo $#' | |
| 332 | -compare_posix_output "IFS comma" 'IFS=,; VAR="x,y,z"; set -- $VAR; echo $2' | |
| 333 | - | |
| 334 | -section "49. BACKGROUND JOBS" | |
| 335 | - | |
| 336 | -compare_posix_exit_code "background process" 'sleep 0.1 & wait' | |
| 337 | -compare_posix_output "background exit" '(exit 0) & wait $!; echo $?' | |
| 338 | - | |
| 339 | -section "50. COMMAND GROUPING" | |
| 340 | - | |
| 341 | -compare_posix_output "subshell isolation" 'X=1; (X=2; echo $X); echo $X' | |
| 342 | -compare_posix_output "brace grouping" 'X=1; { X=2; echo $X; }; echo $X' | |
| 343 | -compare_posix_output "subshell exit" '(exit 5); echo $?' | |
| 344 | - | |
| 345 | -# Summary | |
| 346 | -section "SUMMARY" | |
| 347 | -printf "\n" | |
| 348 | -printf "==========================================\n" | |
| 349 | -printf "EXTENDED POSIX COMPLIANCE TEST RESULTS\n" | |
| 350 | -printf "==========================================\n" | |
| 351 | -printf "${GREEN}Passed:${NC} %d\n" "$PASSED" | |
| 352 | -printf "${RED}Failed:${NC} %d\n" "$FAILED" | |
| 353 | -printf "${YELLOW}Skipped:${NC} %d\n" "$SKIPPED" | |
| 354 | -printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))" | |
| 355 | -printf "==========================================\n" | |
| 356 | - | |
| 357 | -if [ $((PASSED + FAILED)) -gt 0 ]; then | |
| 358 | - PASS_RATE=$((PASSED * 100 / (PASSED + FAILED))) | |
| 359 | - printf "Pass rate: %d%%\n" "$PASS_RATE" | |
| 360 | -fi | |
| 361 | - | |
| 362 | -if [ "$FAILED" -eq 0 ]; then | |
| 363 | - printf "${GREEN}ALL EXTENDED POSIX TESTS PASSED!${NC} ✓\n" | |
| 364 | - exit 0 | |
| 365 | -else | |
| 366 | - printf "${RED}SOME TESTS FAILED${NC} ✗\n" | |
| 367 | - exit 1 | |
| 368 | -fi | |
tests/posix_compliance_test.shdeleted@@ -1,327 +0,0 @@ | ||
| 1 | -#!/bin/sh | |
| 2 | -# ===================================== | |
| 3 | -# POSIX Compliance Test Suite for fortsh | |
| 4 | -# ===================================== | |
| 5 | -# Tests compliance with POSIX shell specification | |
| 6 | -# Uses /bin/sh for comparison (typically dash or bash in POSIX mode) | |
| 7 | - | |
| 8 | -# Note: Using only POSIX-compliant constructs in this script | |
| 9 | -# No bash-isms allowed! | |
| 10 | - | |
| 11 | -# Colors (POSIX-compliant way) | |
| 12 | -RED='\033[0;31m' | |
| 13 | -GREEN='\033[0;32m' | |
| 14 | -YELLOW='\033[1;33m' | |
| 15 | -BLUE='\033[0;34m' | |
| 16 | -NC='\033[0m' | |
| 17 | - | |
| 18 | -PASSED=0 | |
| 19 | -FAILED=0 | |
| 20 | -SKIPPED=0 | |
| 21 | - | |
| 22 | -# Get script directory (POSIX way) | |
| 23 | -SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) | |
| 24 | -FORTSH_BIN="${FORTSH_BIN:-$SCRIPT_DIR/../bin/fortsh}" | |
| 25 | - | |
| 26 | -# Check if fortsh exists | |
| 27 | -if [ ! -x "$FORTSH_BIN" ]; then | |
| 28 | - printf "${RED}ERROR${NC}: fortsh binary not found at $FORTSH_BIN\n" | |
| 29 | - printf "Please run 'make' first or set FORTSH_BIN environment variable\n" | |
| 30 | - exit 1 | |
| 31 | -fi | |
| 32 | - | |
| 33 | -# Test result trackers | |
| 34 | -pass() { | |
| 35 | - printf "${GREEN}✓ PASS${NC}: %s\n" "$1" | |
| 36 | - PASSED=$((PASSED + 1)) | |
| 37 | -} | |
| 38 | - | |
| 39 | -fail() { | |
| 40 | - printf "${RED}✗ FAIL${NC}: %s\n" "$1" | |
| 41 | - if [ -n "$2" ]; then | |
| 42 | - printf " posix: %s\n" "$2" | |
| 43 | - fi | |
| 44 | - if [ -n "$3" ]; then | |
| 45 | - printf " fortsh: %s\n" "$3" | |
| 46 | - fi | |
| 47 | - FAILED=$((FAILED + 1)) | |
| 48 | -} | |
| 49 | - | |
| 50 | -skip() { | |
| 51 | - printf "${YELLOW}⊘ SKIP${NC}: %s - %s\n" "$1" "$2" | |
| 52 | - SKIPPED=$((SKIPPED + 1)) | |
| 53 | -} | |
| 54 | - | |
| 55 | -section() { | |
| 56 | - printf "\n" | |
| 57 | - printf "${BLUE}==========================================\n" | |
| 58 | - printf "%s\n" "$1" | |
| 59 | - printf "==========================================${NC}\n" | |
| 60 | -} | |
| 61 | - | |
| 62 | -# Helper function to run command in both shells and compare | |
| 63 | -compare_posix_output() { | |
| 64 | - test_name="$1" | |
| 65 | - command="$2" | |
| 66 | - posix_file="/tmp/posix_comp_$$_posix" | |
| 67 | - fortsh_file="/tmp/posix_comp_$$_fortsh" | |
| 68 | - | |
| 69 | - # Run in POSIX shell (sh) | |
| 70 | - sh -c "$command" > "$posix_file" 2>&1 || true | |
| 71 | - | |
| 72 | - # Run in fortsh | |
| 73 | - "$FORTSH_BIN" -c "$command" > "$fortsh_file" 2>&1 || true | |
| 74 | - | |
| 75 | - # Compare outputs | |
| 76 | - if diff -q "$posix_file" "$fortsh_file" > /dev/null 2>&1; then | |
| 77 | - pass "$test_name" | |
| 78 | - else | |
| 79 | - fail "$test_name" "$(cat "$posix_file")" "$(cat "$fortsh_file")" | |
| 80 | - fi | |
| 81 | - | |
| 82 | - rm -f "$posix_file" "$fortsh_file" | |
| 83 | -} | |
| 84 | - | |
| 85 | -# Helper function to compare exit codes | |
| 86 | -compare_posix_exit_code() { | |
| 87 | - test_name="$1" | |
| 88 | - command="$2" | |
| 89 | - | |
| 90 | - sh -c "$command" > /dev/null 2>&1 | |
| 91 | - posix_exit=$? | |
| 92 | - | |
| 93 | - "$FORTSH_BIN" -c "$command" > /dev/null 2>&1 | |
| 94 | - fortsh_exit=$? | |
| 95 | - | |
| 96 | - if [ "$posix_exit" -eq "$fortsh_exit" ]; then | |
| 97 | - pass "$test_name" | |
| 98 | - else | |
| 99 | - fail "$test_name" "exit=$posix_exit" "exit=$fortsh_exit" | |
| 100 | - fi | |
| 101 | -} | |
| 102 | - | |
| 103 | -# Cleanup | |
| 104 | -cleanup() { | |
| 105 | - rm -f /tmp/posix_comp_$$_* 2>/dev/null | |
| 106 | - rm -f /tmp/posix_test_* 2>/dev/null | |
| 107 | -} | |
| 108 | -trap cleanup EXIT INT TERM | |
| 109 | - | |
| 110 | -section "1. POSIX BASIC COMMANDS" | |
| 111 | - | |
| 112 | -compare_posix_output "echo simple" "echo hello" | |
| 113 | -compare_posix_output "echo with args" "echo one two three" | |
| 114 | -compare_posix_output "printf basic" "printf 'test\n'" | |
| 115 | -compare_posix_output "printf with args" "printf '%s %d\n' hello 42" | |
| 116 | - | |
| 117 | -section "2. POSIX VARIABLE EXPANSION" | |
| 118 | - | |
| 119 | -compare_posix_output "simple variable" "VAR=test; echo \$VAR" | |
| 120 | -compare_posix_output "variable in quotes" 'VAR=test; echo "$VAR"' | |
| 121 | -compare_posix_output "multiple vars" "A=hello; B=world; echo \$A \$B" | |
| 122 | -compare_posix_output "undefined variable" "echo \$UNDEFINED_VAR_XYZ_987" | |
| 123 | - | |
| 124 | -section "3. POSIX PARAMETER EXPANSION" | |
| 125 | - | |
| 126 | -# Basic parameter expansion | |
| 127 | -compare_posix_output "default value" 'echo "${UNSET:-default}"' | |
| 128 | -compare_posix_output "assign default" 'UNSET=; echo "${UNSET:=assigned}"; echo $UNSET' | |
| 129 | -compare_posix_output "error if unset" 'echo "${VAR:+alternative}"' | |
| 130 | -compare_posix_output "string length" 'VAR=hello; echo "${#VAR}"' | |
| 131 | - | |
| 132 | -# Prefix removal (# and ##) | |
| 133 | -compare_posix_output "remove shortest prefix" 'VAR=foo.bar.baz; echo "${VAR#*.}"' | |
| 134 | -compare_posix_output "remove longest prefix" 'VAR=foo.bar.baz; echo "${VAR##*.}"' | |
| 135 | -compare_posix_output "prefix no match" 'VAR=hello; echo "${VAR#x*}"' | |
| 136 | -compare_posix_output "prefix remove slash" 'VAR=/usr/local/bin; echo "${VAR#/*/}"' | |
| 137 | - | |
| 138 | -# Suffix removal (% and %%) | |
| 139 | -compare_posix_output "remove shortest suffix" 'VAR=foo.bar.baz; echo "${VAR%.*}"' | |
| 140 | -compare_posix_output "remove longest suffix" 'VAR=foo.bar.baz; echo "${VAR%%.*}"' | |
| 141 | -compare_posix_output "suffix no match" 'VAR=hello; echo "${VAR%x*}"' | |
| 142 | -compare_posix_output "suffix remove extension" 'VAR=file.tar.gz; echo "${VAR%.gz}"' | |
| 143 | - | |
| 144 | -section "4. POSIX COMMAND SUBSTITUTION" | |
| 145 | - | |
| 146 | -compare_posix_output "backtick substitution" 'echo `echo test`' | |
| 147 | -compare_posix_output "dollar paren substitution" "echo \$(echo test)" | |
| 148 | -compare_posix_output "nested substitution" "echo \$(echo \$(echo nested))" | |
| 149 | - | |
| 150 | -section "5. POSIX ARITHMETIC" | |
| 151 | - | |
| 152 | -# POSIX arithmetic uses expr or $(( )) | |
| 153 | -compare_posix_output "expr addition" "expr 5 + 3" | |
| 154 | -compare_posix_output "expr multiplication" "expr 4 \* 3" | |
| 155 | -compare_posix_output "expr division" "expr 15 / 3" | |
| 156 | - | |
| 157 | -section "6. POSIX REDIRECTION" | |
| 158 | - | |
| 159 | -compare_posix_output "output redirect" "echo test > /tmp/posix_test_out; cat /tmp/posix_test_out" | |
| 160 | -compare_posix_output "append redirect" "echo line1 > /tmp/posix_test_app; echo line2 >> /tmp/posix_test_app; wc -l < /tmp/posix_test_app" | |
| 161 | -compare_posix_output "input redirect" "echo input > /tmp/posix_test_in; cat < /tmp/posix_test_in" | |
| 162 | -compare_posix_output "stderr redirect" "ls /nonexistent 2>&1 | grep -c 'cannot access\|No such\|not found'" | |
| 163 | - | |
| 164 | -section "7. POSIX PIPELINES" | |
| 165 | - | |
| 166 | -compare_posix_output "simple pipe" "echo hello | cat" | |
| 167 | -compare_posix_output "two-stage pipe" "echo test | cat | tr t T" | |
| 168 | -compare_posix_output "pipe with filter" "printf 'a\nb\nc\n' | grep b" | |
| 169 | - | |
| 170 | -section "8. POSIX TEST COMMAND" | |
| 171 | - | |
| 172 | -compare_posix_exit_code "test -f file" "touch /tmp/posix_test_file && test -f /tmp/posix_test_file" | |
| 173 | -compare_posix_exit_code "test -d directory" "test -d /tmp" | |
| 174 | -compare_posix_exit_code "test -n nonempty" "test -n 'hello'" | |
| 175 | -compare_posix_exit_code "test -z empty" "test -z ''" | |
| 176 | -compare_posix_exit_code "test string =" "test 'hello' = 'hello'" | |
| 177 | -compare_posix_exit_code "test string !=" "test 'hello' != 'world'" | |
| 178 | -compare_posix_exit_code "test number -eq" "test 5 -eq 5" | |
| 179 | -compare_posix_exit_code "test number -ne" "test 5 -ne 3" | |
| 180 | -compare_posix_exit_code "test number -gt" "test 5 -gt 3" | |
| 181 | -compare_posix_exit_code "test number -ge" "test 5 -ge 5" | |
| 182 | -compare_posix_exit_code "test number -lt" "test 3 -lt 5" | |
| 183 | -compare_posix_exit_code "test number -le" "test 3 -le 3" | |
| 184 | - | |
| 185 | -section "9. POSIX CONDITIONALS" | |
| 186 | - | |
| 187 | -compare_posix_output "if true" "if true; then echo yes; fi" | |
| 188 | -compare_posix_output "if false else" "if false; then echo no; else echo yes; fi" | |
| 189 | -compare_posix_output "if-elif-else" "X=2; if [ \$X -eq 1 ]; then echo one; elif [ \$X -eq 2 ]; then echo two; else echo other; fi" | |
| 190 | - | |
| 191 | -section "10. POSIX LOOPS" | |
| 192 | - | |
| 193 | -compare_posix_output "for loop" "for i in a b c; do echo \$i; done" | |
| 194 | -compare_posix_output "while loop" "i=3; while [ \$i -gt 0 ]; do echo \$i; i=\$((i - 1)); done" | |
| 195 | -compare_posix_output "until loop" "i=1; until [ \$i -gt 3 ]; do echo \$i; i=\$((i + 1)); done" | |
| 196 | - | |
| 197 | -section "11. POSIX CASE STATEMENT" | |
| 198 | - | |
| 199 | -compare_posix_output "case exact match" "x=2; case \$x in 1) echo one;; 2) echo two;; esac" | |
| 200 | -compare_posix_output "case pattern match" "x=hello; case \$x in h*) echo h_prefix;; esac" | |
| 201 | -compare_posix_output "case default" "x=z; case \$x in a) echo a;; b) echo b;; *) echo default;; esac" | |
| 202 | -compare_posix_output "case multiple patterns" "x=b; case \$x in a|b|c) echo abc;; *) echo other;; esac" | |
| 203 | - | |
| 204 | -section "12. POSIX FUNCTIONS" | |
| 205 | - | |
| 206 | -compare_posix_output "simple function" "func() { echo hello; }; func" | |
| 207 | -compare_posix_output "function with args" "func() { echo \$1 \$2; }; func foo bar" | |
| 208 | -compare_posix_output "function return" "func() { return 42; }; func; echo \$?" | |
| 209 | -compare_posix_output "function \$# args" "func() { echo \$#; }; func a b c" | |
| 210 | - | |
| 211 | -section "13. POSIX SPECIAL VARIABLES" | |
| 212 | - | |
| 213 | -compare_posix_output "\$? exit status" "true; echo \$?" | |
| 214 | -compare_posix_output "\$? after false" "false; echo \$?" | |
| 215 | -compare_posix_output "\$# argument count" "set -- a b c; echo \$#" | |
| 216 | -compare_posix_output "\$@ all arguments" "set -- a b c; echo \$@" | |
| 217 | -compare_posix_output "\$* all arguments" "set -- a b c; echo \$*" | |
| 218 | -compare_posix_output "\$0 script name" "echo \$0 | grep -c sh" | |
| 219 | - | |
| 220 | -section "14. POSIX LOGICAL OPERATORS" | |
| 221 | - | |
| 222 | -compare_posix_exit_code "true && true" "true && true" | |
| 223 | -compare_posix_exit_code "true && false" "true && false" | |
| 224 | -compare_posix_exit_code "false || true" "false || true" | |
| 225 | -compare_posix_exit_code "false || false" "false || false" | |
| 226 | -compare_posix_output "command && echo" "true && echo success" | |
| 227 | -compare_posix_output "command || echo" "false || echo fallback" | |
| 228 | -compare_posix_output "! negation" "! false && echo negated" | |
| 229 | - | |
| 230 | -section "15. POSIX QUOTING" | |
| 231 | - | |
| 232 | -compare_posix_output "single quote literal" "echo '\$VAR'" | |
| 233 | -compare_posix_output "double quote expand" 'VAR=test; echo "$VAR"' | |
| 234 | -compare_posix_output "escape in double" 'echo "test\$var"' | |
| 235 | -compare_posix_output "backslash escape" 'echo test\ word' | |
| 236 | - | |
| 237 | -section "16. POSIX SUBSHELLS" | |
| 238 | - | |
| 239 | -compare_posix_output "subshell grouping" "(echo a; echo b) | wc -l" | |
| 240 | -compare_posix_output "subshell var isolation" "(VAR=inner; echo \$VAR); echo \$VAR" | |
| 241 | - | |
| 242 | -section "17. POSIX COMPOUND COMMANDS" | |
| 243 | - | |
| 244 | -compare_posix_output "command grouping {}" "{ echo a; echo b; } | wc -l" | |
| 245 | -compare_posix_output "command list ;" "echo a; echo b" | |
| 246 | - | |
| 247 | -section "18. POSIX HERE DOCUMENTS" | |
| 248 | - | |
| 249 | -compare_posix_output "simple heredoc" "cat <<EOF | |
| 250 | -line1 | |
| 251 | -line2 | |
| 252 | -EOF" | |
| 253 | - | |
| 254 | -compare_posix_output "heredoc with vars" "VAR=test; cat <<EOF | |
| 255 | -value=\$VAR | |
| 256 | -EOF" | |
| 257 | - | |
| 258 | -compare_posix_output "quoted heredoc" "cat <<'EOF' | |
| 259 | -\$VAR | |
| 260 | -EOF" | |
| 261 | - | |
| 262 | -section "19. POSIX WORD EXPANSION ORDER" | |
| 263 | - | |
| 264 | -# POSIX specifies: tilde, parameter, command subst, arithmetic, field splitting, pathname, quote removal | |
| 265 | -compare_posix_output "expansion order" "VAR='a b'; echo \$VAR" | |
| 266 | -compare_posix_output "quoted expansion" 'VAR="a b"; echo "$VAR"' | |
| 267 | - | |
| 268 | -section "20. POSIX PATHNAME EXPANSION (GLOBBING)" | |
| 269 | - | |
| 270 | -# Setup test files | |
| 271 | -mkdir -p /tmp/posix_test_glob | |
| 272 | -touch /tmp/posix_test_glob/a.txt /tmp/posix_test_glob/b.txt /tmp/posix_test_glob/c.dat | |
| 273 | - | |
| 274 | -compare_posix_output "glob * pattern" "ls /tmp/posix_test_glob/*.txt 2>/dev/null | wc -l" | |
| 275 | -compare_posix_output "glob ? pattern" "ls /tmp/posix_test_glob/?.txt 2>/dev/null | wc -l" | |
| 276 | -compare_posix_output "glob [abc] pattern" "ls /tmp/posix_test_glob/[ab].txt 2>/dev/null | wc -l" | |
| 277 | - | |
| 278 | -section "21. POSIX FIELD SPLITTING (IFS)" | |
| 279 | - | |
| 280 | -compare_posix_output "default IFS" "VAR='a b c'; set -- \$VAR; echo \$#" | |
| 281 | -compare_posix_output "custom IFS" "IFS=:; VAR='a:b:c'; set -- \$VAR; echo \$1" | |
| 282 | - | |
| 283 | -section "22. POSIX EXIT STATUS" | |
| 284 | - | |
| 285 | -compare_posix_exit_code "true exit status" "true" | |
| 286 | -compare_posix_exit_code "false exit status" "false" | |
| 287 | -compare_posix_exit_code "command not found" "nonexistent_command_xyz 2>/dev/null" | |
| 288 | -compare_posix_exit_code "return from function" "func() { return 3; }; func" | |
| 289 | - | |
| 290 | -section "23. POSIX SET BUILTIN" | |
| 291 | - | |
| 292 | -compare_posix_output "set positional" "set -- a b c; echo \$1 \$2 \$3" | |
| 293 | -compare_posix_output "set shift" "set -- a b c; shift; echo \$1" | |
| 294 | -compare_posix_output "set shift n" "set -- a b c d; shift 2; echo \$1" | |
| 295 | - | |
| 296 | -section "24. POSIX EXPORT" | |
| 297 | - | |
| 298 | -compare_posix_output "export variable" "export VAR=test; sh -c 'echo \$VAR'" | |
| 299 | - | |
| 300 | -section "25. POSIX READONLY" | |
| 301 | - | |
| 302 | -compare_posix_exit_code "readonly assignment" "readonly VAR=test; VAR=new 2>/dev/null" | |
| 303 | - | |
| 304 | -# Summary | |
| 305 | -section "SUMMARY" | |
| 306 | -printf "\n" | |
| 307 | -printf "==========================================\n" | |
| 308 | -printf "POSIX COMPLIANCE TEST RESULTS\n" | |
| 309 | -printf "==========================================\n" | |
| 310 | -printf "${GREEN}Passed:${NC} %d\n" "$PASSED" | |
| 311 | -printf "${RED}Failed:${NC} %d\n" "$FAILED" | |
| 312 | -printf "${YELLOW}Skipped:${NC} %d\n" "$SKIPPED" | |
| 313 | -printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))" | |
| 314 | -printf "==========================================\n" | |
| 315 | - | |
| 316 | -if [ $((PASSED + FAILED)) -gt 0 ]; then | |
| 317 | - PASS_RATE=$((PASSED * 100 / (PASSED + FAILED))) | |
| 318 | - printf "Pass rate: %d%%\n" "$PASS_RATE" | |
| 319 | -fi | |
| 320 | - | |
| 321 | -if [ "$FAILED" -eq 0 ]; then | |
| 322 | - printf "${GREEN}ALL POSIX COMPLIANCE TESTS PASSED!${NC} ✓\n" | |
| 323 | - exit 0 | |
| 324 | -else | |
| 325 | - printf "${RED}SOME TESTS FAILED${NC} ✗\n" | |
| 326 | - exit 1 | |
| 327 | -fi | |
tests/test_array_enhanced.shdeleted@@ -1,148 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Comprehensive test suite for enhanced array features | |
| 3 | -# Tests: indices expansion, array slicing, and their combinations | |
| 4 | - | |
| 5 | -echo "=== Enhanced Array Features Test Suite ===" | |
| 6 | -echo "" | |
| 7 | - | |
| 8 | -# Test 1: Array indices expansion | |
| 9 | -echo "TEST 1: Array Indices Expansion" | |
| 10 | -echo "================================" | |
| 11 | -declare -a test1 | |
| 12 | -test1[0]=alpha | |
| 13 | -test1[1]=beta | |
| 14 | -test1[2]=gamma | |
| 15 | -indices1="${!test1[@]}" | |
| 16 | -echo "Array: ${test1[@]}" | |
| 17 | -echo "Indices: $indices1" | |
| 18 | -[ "$indices1" = "0 1 2" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 19 | -echo "" | |
| 20 | - | |
| 21 | -# Test 2: Sparse array indices | |
| 22 | -echo "TEST 2: Sparse Array Indices" | |
| 23 | -echo "============================" | |
| 24 | -declare -a test2 | |
| 25 | -test2[1]=one | |
| 26 | -test2[5]=five | |
| 27 | -test2[10]=ten | |
| 28 | -indices2="${!test2[@]}" | |
| 29 | -echo "Sparse array: ${test2[@]}" | |
| 30 | -echo "Indices: $indices2" | |
| 31 | -[ "$indices2" = "1 5 10" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 32 | -echo "" | |
| 33 | - | |
| 34 | -# Test 3: Basic array slicing | |
| 35 | -echo "TEST 3: Basic Array Slicing" | |
| 36 | -echo "===========================" | |
| 37 | -declare -a test3 | |
| 38 | -test3=(a b c d e f g) | |
| 39 | -slice3="${test3[@]:2:3}" | |
| 40 | -echo "Full array: ${test3[@]}" | |
| 41 | -echo "Slice [2:3]: $slice3" | |
| 42 | -[ "$slice3" = "c d e" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 43 | -echo "" | |
| 44 | - | |
| 45 | -# Test 4: Slice from offset to end | |
| 46 | -echo "TEST 4: Slice to End" | |
| 47 | -echo "====================" | |
| 48 | -declare -a test4 | |
| 49 | -test4=(one two three four five) | |
| 50 | -slice4="${test4[@]:3}" | |
| 51 | -echo "Full array: ${test4[@]}" | |
| 52 | -echo "Slice [3:]: $slice4" | |
| 53 | -[ "$slice4" = "four five" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 54 | -echo "" | |
| 55 | - | |
| 56 | -# Test 5: Slice from start | |
| 57 | -echo "TEST 5: Slice from Start" | |
| 58 | -echo "========================" | |
| 59 | -declare -a test5 | |
| 60 | -test5=(red orange yellow green blue) | |
| 61 | -slice5="${test5[@]:0:2}" | |
| 62 | -echo "Full array: ${test5[@]}" | |
| 63 | -echo "Slice [0:2]: $slice5" | |
| 64 | -[ "$slice5" = "red orange" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 65 | -echo "" | |
| 66 | - | |
| 67 | -# Test 6: Single element slice | |
| 68 | -echo "TEST 6: Single Element Slice" | |
| 69 | -echo "============================" | |
| 70 | -declare -a test6 | |
| 71 | -test6=(apple banana cherry date) | |
| 72 | -slice6="${test6[@]:1:1}" | |
| 73 | -echo "Full array: ${test6[@]}" | |
| 74 | -echo "Slice [1:1]: $slice6" | |
| 75 | -[ "$slice6" = "banana" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 76 | -echo "" | |
| 77 | - | |
| 78 | -# Test 7: Array length with indices | |
| 79 | -echo "TEST 7: Count of Array Indices" | |
| 80 | -echo "===============================" | |
| 81 | -declare -a test7 | |
| 82 | -test7=(x y z) | |
| 83 | -indices7="${!test7[@]}" | |
| 84 | -count7=$(echo $indices7 | wc -w) | |
| 85 | -echo "Array: ${test7[@]}" | |
| 86 | -echo "Indices: $indices7" | |
| 87 | -echo "Count: $count7" | |
| 88 | -[ "$count7" = "3" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 89 | -echo "" | |
| 90 | - | |
| 91 | -# Test 8: Combining indices and slicing | |
| 92 | -echo "TEST 8: Slice of Indices (conceptual)" | |
| 93 | -echo "=====================================" | |
| 94 | -declare -a test8 | |
| 95 | -test8[2]=item2 | |
| 96 | -test8[4]=item4 | |
| 97 | -test8[6]=item6 | |
| 98 | -test8[8]=item8 | |
| 99 | -all_indices="${!test8[@]}" | |
| 100 | -echo "Array: ${test8[@]}" | |
| 101 | -echo "All indices: $all_indices" | |
| 102 | -echo "Note: Can iterate and work with indices" | |
| 103 | -for idx in ${!test8[@]}; do | |
| 104 | - echo " Index $idx = ${test8[$idx]}" | |
| 105 | -done | |
| 106 | -echo "✓ PASS" | |
| 107 | -echo "" | |
| 108 | - | |
| 109 | -# Test 9: Modify and check indices | |
| 110 | -echo "TEST 9: Modify and Check Indices" | |
| 111 | -echo "=================================" | |
| 112 | -declare -a test9 | |
| 113 | -test9[0]=first | |
| 114 | -test9[1]=second | |
| 115 | -test9[2]=third | |
| 116 | -before_indices="${!test9[@]}" | |
| 117 | -echo "Before: indices=$before_indices, values=${test9[@]}" | |
| 118 | -test9[1]=MODIFIED | |
| 119 | -after_indices="${!test9[@]}" | |
| 120 | -echo "After: indices=$after_indices, values=${test9[@]}" | |
| 121 | -[ "$before_indices" = "$after_indices" ] && echo "✓ PASS (indices unchanged)" || echo "✗ FAIL" | |
| 122 | -echo "" | |
| 123 | - | |
| 124 | -# Test 10: Slice with excessive length | |
| 125 | -echo "TEST 10: Slice with Excessive Length" | |
| 126 | -echo "=====================================" | |
| 127 | -declare -a test10 | |
| 128 | -test10=(a b c) | |
| 129 | -slice10="${test10[@]:1:100}" | |
| 130 | -echo "Full array (3 elements): ${test10[@]}" | |
| 131 | -echo "Slice [1:100]: $slice10" | |
| 132 | -[ "$slice10" = "b c" ] && echo "✓ PASS (clamped to array bounds)" || echo "✗ FAIL" | |
| 133 | -echo "" | |
| 134 | - | |
| 135 | -# Summary | |
| 136 | -echo "==========================================" | |
| 137 | -echo "Enhanced Array Features Test Suite Complete" | |
| 138 | -echo "==========================================" | |
| 139 | -echo "" | |
| 140 | -echo "Features Tested:" | |
| 141 | -echo " ✓ Array indices expansion (${!array[@]})" | |
| 142 | -echo " ✓ Sparse array indices" | |
| 143 | -echo " ✓ Array slicing (${array[@]:offset:length})" | |
| 144 | -echo " ✓ Slice from offset to end" | |
| 145 | -echo " ✓ Slice from start" | |
| 146 | -echo " ✓ Single element slicing" | |
| 147 | -echo " ✓ Combining features" | |
| 148 | -echo " ✓ Boundary conditions" | |
tests/test_array_indices.shdeleted@@ -1,68 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Test array indices expansion ${!array[@]} | |
| 3 | - | |
| 4 | -echo "=== Array Indices Expansion Tests ===" | |
| 5 | - | |
| 6 | -# Test 1: Get indices from simple array | |
| 7 | -echo "" | |
| 8 | -echo "Test 1: Simple array indices" | |
| 9 | -declare -a myarray | |
| 10 | -myarray[0]=apple | |
| 11 | -myarray[1]=banana | |
| 12 | -myarray[2]=cherry | |
| 13 | -indices="${!myarray[@]}" | |
| 14 | -echo "Array: ${myarray[@]}" | |
| 15 | -echo "Indices: $indices" | |
| 16 | -if [ "$indices" = "0 1 2" ]; then | |
| 17 | - echo "Simple array indices: PASS" | |
| 18 | -else | |
| 19 | - echo "Simple array indices: FAIL (expected '0 1 2', got '$indices')" | |
| 20 | -fi | |
| 21 | - | |
| 22 | -# Test 2: Get indices from sparse array | |
| 23 | -echo "" | |
| 24 | -echo "Test 2: Sparse array indices" | |
| 25 | -declare -a sparse | |
| 26 | -sparse[0]=first | |
| 27 | -sparse[5]=sixth | |
| 28 | -sparse[10]=eleventh | |
| 29 | -sparse_indices="${!sparse[@]}" | |
| 30 | -echo "Sparse array: ${sparse[@]}" | |
| 31 | -echo "Sparse indices: $sparse_indices" | |
| 32 | -if [ "$sparse_indices" = "0 5 10" ]; then | |
| 33 | - echo "Sparse array indices: PASS" | |
| 34 | -else | |
| 35 | - echo "Sparse array indices: FAIL (expected '0 5 10', got '$sparse_indices')" | |
| 36 | -fi | |
| 37 | - | |
| 38 | -# Test 3: Get count of array indices | |
| 39 | -echo "" | |
| 40 | -echo "Test 3: Count of array indices" | |
| 41 | -declare -a counted | |
| 42 | -counted[1]=one | |
| 43 | -counted[2]=two | |
| 44 | -counted[3]=three | |
| 45 | -count_indices="${!counted[@]}" | |
| 46 | -num_indices=$(echo $count_indices | wc -w) | |
| 47 | -echo "Array indices: $count_indices" | |
| 48 | -echo "Number of indices: $num_indices" | |
| 49 | -if [ "$num_indices" = "3" ]; then | |
| 50 | - echo "Count of indices: PASS" | |
| 51 | -else | |
| 52 | - echo "Count of indices: FAIL (expected 3, got $num_indices)" | |
| 53 | -fi | |
| 54 | - | |
| 55 | -# Test 4: Iteration over array indices | |
| 56 | -echo "" | |
| 57 | -echo "Test 4: Iterate over array indices" | |
| 58 | -declare -a items | |
| 59 | -items[10]=ten | |
| 60 | -items[20]=twenty | |
| 61 | -items[30]=thirty | |
| 62 | -echo "Iterating over indices:" | |
| 63 | -for idx in ${!items[@]}; do | |
| 64 | - echo " Index $idx: ${items[$idx]}" | |
| 65 | -done | |
| 66 | - | |
| 67 | -echo "" | |
| 68 | -echo "=== Array Indices Tests Complete ===" | |
tests/test_array_slicing.shdeleted@@ -1,88 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Test array slicing ${array[@]:offset:length} | |
| 3 | - | |
| 4 | -echo "=== Array Slicing Tests ===" | |
| 5 | - | |
| 6 | -# Test 1: Basic array slicing with offset and length | |
| 7 | -echo "" | |
| 8 | -echo "Test 1: Basic array slicing" | |
| 9 | -declare -a fruits | |
| 10 | -fruits=(apple banana cherry date elderberry fig grape) | |
| 11 | -echo "Full array: ${fruits[@]}" | |
| 12 | -slice1="${fruits[@]:1:3}" | |
| 13 | -echo "Slice [1:3]: $slice1" | |
| 14 | -if [ "$slice1" = "banana cherry date" ]; then | |
| 15 | - echo "Basic slicing: PASS" | |
| 16 | -else | |
| 17 | - echo "Basic slicing: FAIL (expected 'banana cherry date', got '$slice1')" | |
| 18 | -fi | |
| 19 | - | |
| 20 | -# Test 2: Slice from offset to end (no length) | |
| 21 | -echo "" | |
| 22 | -echo "Test 2: Slice from offset to end" | |
| 23 | -declare -a numbers | |
| 24 | -numbers=(one two three four five) | |
| 25 | -echo "Full array: ${numbers[@]}" | |
| 26 | -slice2="${numbers[@]:2}" | |
| 27 | -echo "Slice [2:]: $slice2" | |
| 28 | -if [ "$slice2" = "three four five" ]; then | |
| 29 | - echo "Slice to end: PASS" | |
| 30 | -else | |
| 31 | - echo "Slice to end: FAIL (expected 'three four five', got '$slice2')" | |
| 32 | -fi | |
| 33 | - | |
| 34 | -# Test 3: Slice with length exceeding array size | |
| 35 | -echo "" | |
| 36 | -echo "Test 3: Slice with excessive length" | |
| 37 | -declare -a colors | |
| 38 | -colors=(red green blue) | |
| 39 | -echo "Full array: ${colors[@]}" | |
| 40 | -slice3="${colors[@]:1:10}" | |
| 41 | -echo "Slice [1:10]: $slice3" | |
| 42 | -if [ "$slice3" = "green blue" ]; then | |
| 43 | - echo "Excessive length: PASS" | |
| 44 | -else | |
| 45 | - echo "Excessive length: FAIL (expected 'green blue', got '$slice3')" | |
| 46 | -fi | |
| 47 | - | |
| 48 | -# Test 4: Slice from start | |
| 49 | -echo "" | |
| 50 | -echo "Test 4: Slice from start" | |
| 51 | -declare -a letters | |
| 52 | -letters=(a b c d e f) | |
| 53 | -echo "Full array: ${letters[@]}" | |
| 54 | -slice4="${letters[@]:0:3}" | |
| 55 | -echo "Slice [0:3]: $slice4" | |
| 56 | -if [ "$slice4" = "a b c" ]; then | |
| 57 | - echo "Slice from start: PASS" | |
| 58 | -else | |
| 59 | - echo "Slice from start: FAIL (expected 'a b c', got '$slice4')" | |
| 60 | -fi | |
| 61 | - | |
| 62 | -# Test 5: Single element slice | |
| 63 | -echo "" | |
| 64 | -echo "Test 5: Single element slice" | |
| 65 | -declare -a words | |
| 66 | -words=(hello world foo bar) | |
| 67 | -echo "Full array: ${words[@]}" | |
| 68 | -slice5="${words[@]:2:1}" | |
| 69 | -echo "Slice [2:1]: $slice5" | |
| 70 | -if [ "$slice5" = "foo" ]; then | |
| 71 | - echo "Single element: PASS" | |
| 72 | -else | |
| 73 | - echo "Single element: FAIL (expected 'foo', got '$slice5')" | |
| 74 | -fi | |
| 75 | - | |
| 76 | -# Test 6: Iteration over sliced array | |
| 77 | -echo "" | |
| 78 | -echo "Test 6: Iterate over sliced array" | |
| 79 | -declare -a data | |
| 80 | -data=(item1 item2 item3 item4 item5) | |
| 81 | -echo "Full array: ${data[@]}" | |
| 82 | -echo "Iterating over slice [1:2]:" | |
| 83 | -for item in ${data[@]:1:2}; do | |
| 84 | - echo " - $item" | |
| 85 | -done | |
| 86 | - | |
| 87 | -echo "" | |
| 88 | -echo "=== Array Slicing Tests Complete ===" | |
tests/test_arrays.shdeleted@@ -1,82 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Test array functionality | |
| 3 | - | |
| 4 | -echo "=== Array Tests ===" | |
| 5 | - | |
| 6 | -# Test 1: Array declaration | |
| 7 | -echo "Test 1: Array declaration" | |
| 8 | -declare -a myarray | |
| 9 | -echo "Array declared: PASS" | |
| 10 | - | |
| 11 | -# Test 2: Array assignment | |
| 12 | -echo "" | |
| 13 | -echo "Test 2: Array assignment" | |
| 14 | -myarray[0]=apple | |
| 15 | -myarray[1]=banana | |
| 16 | -myarray[2]=cherry | |
| 17 | -echo "Array elements assigned: PASS" | |
| 18 | - | |
| 19 | -# Test 3: Array element access | |
| 20 | -echo "" | |
| 21 | -echo "Test 3: Array element access" | |
| 22 | -echo "myarray[0] = ${myarray[0]}" | |
| 23 | -echo "myarray[1] = ${myarray[1]}" | |
| 24 | -echo "myarray[2] = ${myarray[2]}" | |
| 25 | -if [ "${myarray[0]}" = "apple" ]; then | |
| 26 | - echo "Element access: PASS" | |
| 27 | -else | |
| 28 | - echo "Element access: FAIL" | |
| 29 | -fi | |
| 30 | - | |
| 31 | -# Test 4: Array length | |
| 32 | -echo "" | |
| 33 | -echo "Test 4: Array length" | |
| 34 | -length=${#myarray[@]} | |
| 35 | -echo "Array length = $length" | |
| 36 | -if [ "$length" = "3" ]; then | |
| 37 | - echo "Array length: PASS" | |
| 38 | -else | |
| 39 | - echo "Array length: FAIL (expected 3, got $length)" | |
| 40 | -fi | |
| 41 | - | |
| 42 | -# Test 5: All elements expansion | |
| 43 | -echo "" | |
| 44 | -echo "Test 5: All elements expansion" | |
| 45 | -echo "All elements: ${myarray[@]}" | |
| 46 | -all="${myarray[@]}" | |
| 47 | -if [ "$all" = "apple banana cherry" ]; then | |
| 48 | - echo "All elements: PASS" | |
| 49 | -else | |
| 50 | - echo "All elements: FAIL (got: $all)" | |
| 51 | -fi | |
| 52 | - | |
| 53 | -# Test 6: Sparse array | |
| 54 | -echo "" | |
| 55 | -echo "Test 6: Sparse array" | |
| 56 | -declare -a sparse | |
| 57 | -sparse[0]=first | |
| 58 | -sparse[5]=sixth | |
| 59 | -sparse[10]=eleventh | |
| 60 | -echo "sparse[0] = ${sparse[0]}" | |
| 61 | -echo "sparse[5] = ${sparse[5]}" | |
| 62 | -echo "sparse[10] = ${sparse[10]}" | |
| 63 | -echo "Sparse array length: ${#sparse[@]}" | |
| 64 | - | |
| 65 | -# Test 7: Mixed assignment and access | |
| 66 | -echo "" | |
| 67 | -echo "Test 7: Mixed operations" | |
| 68 | -declare -a mixed | |
| 69 | -mixed[0]=one | |
| 70 | -mixed[1]=two | |
| 71 | -mixed[2]=three | |
| 72 | -echo "Initial: ${mixed[@]}" | |
| 73 | -mixed[1]=TWO | |
| 74 | -echo "After modification: ${mixed[@]}" | |
| 75 | -if [ "${mixed[1]}" = "TWO" ]; then | |
| 76 | - echo "Mixed operations: PASS" | |
| 77 | -else | |
| 78 | - echo "Mixed operations: FAIL" | |
| 79 | -fi | |
| 80 | - | |
| 81 | -echo "" | |
| 82 | -echo "=== Array Tests Complete ===" | |
tests/test_ast.f90deleted@@ -1,220 +0,0 @@ | ||
| 1 | -! ============================================================================== | |
| 2 | -! Test program for AST-based execution model | |
| 3 | -! Demonstrates how nested loops and break/continue would work | |
| 4 | -! ============================================================================== | |
| 5 | -program test_ast | |
| 6 | - use ast_types | |
| 7 | - use lexer | |
| 8 | - use parser | |
| 9 | - use evaluator | |
| 10 | - use shell_types | |
| 11 | - implicit none | |
| 12 | - | |
| 13 | - ! Test nested loops with break | |
| 14 | - call test_nested_loops_with_break() | |
| 15 | - | |
| 16 | - ! Test for loop with continue | |
| 17 | - call test_for_loop_with_continue() | |
| 18 | - | |
| 19 | - ! Test proper parsing | |
| 20 | - call test_parser() | |
| 21 | - | |
| 22 | -contains | |
| 23 | - | |
| 24 | - subroutine test_nested_loops_with_break() | |
| 25 | - character(:), allocatable :: script | |
| 26 | - type(lexer_t) :: lex | |
| 27 | - type(parser_t) :: pars | |
| 28 | - type(evaluator_t) :: eval | |
| 29 | - type(shell_state_t) :: shell | |
| 30 | - type(script_node_t) :: ast | |
| 31 | - integer :: exit_code | |
| 32 | - | |
| 33 | - print *, "=== Test: Nested loops with break ===" | |
| 34 | - | |
| 35 | - ! Script with nested loops and break | |
| 36 | - script = & | |
| 37 | - 'for i in 1 2 3' // char(10) // & | |
| 38 | - 'do' // char(10) // & | |
| 39 | - ' echo "Outer: $i"' // char(10) // & | |
| 40 | - ' for j in a b c' // char(10) // & | |
| 41 | - ' do' // char(10) // & | |
| 42 | - ' echo " Inner: $j"' // char(10) // & | |
| 43 | - ' [ "$j" = "b" ] && break' // char(10) // & | |
| 44 | - ' done' // char(10) // & | |
| 45 | - ' echo "Back in outer"' // char(10) // & | |
| 46 | - 'done' | |
| 47 | - | |
| 48 | - ! Tokenize | |
| 49 | - call lex%init(script) | |
| 50 | - call lex%tokenize() | |
| 51 | - | |
| 52 | - print *, "Tokens generated: ", lex%token_count | |
| 53 | - | |
| 54 | - ! Parse | |
| 55 | - call pars%init(lex%tokens, lex%token_count) | |
| 56 | - ast = pars%parse() | |
| 57 | - | |
| 58 | - print *, "AST built successfully" | |
| 59 | - | |
| 60 | - ! Evaluate | |
| 61 | - call eval%init(shell) | |
| 62 | - exit_code = eval%eval(ast) | |
| 63 | - | |
| 64 | - print *, "Exit code: ", exit_code | |
| 65 | - print *, "" | |
| 66 | - | |
| 67 | - ! Clean up | |
| 68 | - call lex%destroy() | |
| 69 | - call pars%destroy() | |
| 70 | - call eval%destroy() | |
| 71 | - end subroutine test_nested_loops_with_break | |
| 72 | - | |
| 73 | - subroutine test_for_loop_with_continue() | |
| 74 | - character(:), allocatable :: script | |
| 75 | - type(lexer_t) :: lex | |
| 76 | - type(parser_t) :: pars | |
| 77 | - type(evaluator_t) :: eval | |
| 78 | - type(shell_state_t) :: shell | |
| 79 | - type(script_node_t) :: ast | |
| 80 | - integer :: exit_code | |
| 81 | - | |
| 82 | - print *, "=== Test: For loop with continue ===" | |
| 83 | - | |
| 84 | - script = & | |
| 85 | - 'for i in 1 2 3 4 5' // char(10) // & | |
| 86 | - 'do' // char(10) // & | |
| 87 | - ' [ "$i" = "3" ] && continue' // char(10) // & | |
| 88 | - ' echo "Processing: $i"' // char(10) // & | |
| 89 | - 'done' | |
| 90 | - | |
| 91 | - ! Tokenize | |
| 92 | - call lex%init(script) | |
| 93 | - call lex%tokenize() | |
| 94 | - | |
| 95 | - ! Parse | |
| 96 | - call pars%init(lex%tokens, lex%token_count) | |
| 97 | - ast = pars%parse() | |
| 98 | - | |
| 99 | - ! Evaluate | |
| 100 | - call eval%init(shell) | |
| 101 | - exit_code = eval%eval(ast) | |
| 102 | - | |
| 103 | - print *, "Exit code: ", exit_code | |
| 104 | - print *, "" | |
| 105 | - | |
| 106 | - ! Clean up | |
| 107 | - call lex%destroy() | |
| 108 | - call pars%destroy() | |
| 109 | - call eval%destroy() | |
| 110 | - end subroutine test_for_loop_with_continue | |
| 111 | - | |
| 112 | - subroutine test_parser() | |
| 113 | - character(:), allocatable :: script | |
| 114 | - type(lexer_t) :: lex | |
| 115 | - type(parser_t) :: pars | |
| 116 | - type(script_node_t) :: ast | |
| 117 | - integer :: i | |
| 118 | - | |
| 119 | - print *, "=== Test: Parser functionality ===" | |
| 120 | - | |
| 121 | - ! Simple script | |
| 122 | - script = & | |
| 123 | - 'echo "Hello World"' // char(10) // & | |
| 124 | - 'if [ -f test.txt ]' // char(10) // & | |
| 125 | - 'then' // char(10) // & | |
| 126 | - ' cat test.txt' // char(10) // & | |
| 127 | - 'else' // char(10) // & | |
| 128 | - ' echo "No file"' // char(10) // & | |
| 129 | - 'fi' | |
| 130 | - | |
| 131 | - ! Tokenize | |
| 132 | - call lex%init(script) | |
| 133 | - call lex%tokenize() | |
| 134 | - | |
| 135 | - print *, "Generated tokens:" | |
| 136 | - do i = 1, min(10, lex%token_count) | |
| 137 | - print '(a,i3,a,i2,a,a)', & | |
| 138 | - " Token ", i, " type=", lex%tokens(i)%type, & | |
| 139 | - " value=", trim(lex%tokens(i)%value) | |
| 140 | - end do | |
| 141 | - | |
| 142 | - ! Parse | |
| 143 | - call pars%init(lex%tokens, lex%token_count) | |
| 144 | - ast = pars%parse() | |
| 145 | - | |
| 146 | - print *, "AST structure:" | |
| 147 | - call print_ast_structure(ast) | |
| 148 | - print *, "" | |
| 149 | - | |
| 150 | - ! Clean up | |
| 151 | - call lex%destroy() | |
| 152 | - call pars%destroy() | |
| 153 | - end subroutine test_parser | |
| 154 | - | |
| 155 | - recursive subroutine print_ast_structure(node, indent) | |
| 156 | - class(ast_node_t), intent(in) :: node | |
| 157 | - integer, intent(in), optional :: indent | |
| 158 | - integer :: ind, i | |
| 159 | - | |
| 160 | - ind = 0 | |
| 161 | - if (present(indent)) ind = indent | |
| 162 | - | |
| 163 | - ! Print node type | |
| 164 | - write(*, '(a,a,i0)', advance='no') repeat(' ', ind*2), 'Node type: ', node%node_type | |
| 165 | - | |
| 166 | - select type(node) | |
| 167 | - type is (script_node_t) | |
| 168 | - print *, ' (SCRIPT)' | |
| 169 | - if (allocated(node%statements)) then | |
| 170 | - do i = 1, size(node%statements) | |
| 171 | - call print_ast_structure(node%statements(i), ind+1) | |
| 172 | - end do | |
| 173 | - end if | |
| 174 | - | |
| 175 | - type is (for_node_t) | |
| 176 | - print *, ' (FOR loop) var=', trim(node%variable) | |
| 177 | - if (allocated(node%body)) then | |
| 178 | - do i = 1, size(node%body) | |
| 179 | - call print_ast_structure(node%body(i), ind+1) | |
| 180 | - end do | |
| 181 | - end if | |
| 182 | - | |
| 183 | - type is (if_node_t) | |
| 184 | - print *, ' (IF statement)' | |
| 185 | - if (allocated(node%then_branch)) then | |
| 186 | - write(*, '(a)') repeat(' ', (ind+1)*2) // 'THEN:' | |
| 187 | - do i = 1, size(node%then_branch) | |
| 188 | - call print_ast_structure(node%then_branch(i), ind+2) | |
| 189 | - end do | |
| 190 | - end if | |
| 191 | - if (allocated(node%else_branch)) then | |
| 192 | - write(*, '(a)') repeat(' ', (ind+1)*2) // 'ELSE:' | |
| 193 | - do i = 1, size(node%else_branch) | |
| 194 | - call print_ast_structure(node%else_branch(i), ind+2) | |
| 195 | - end do | |
| 196 | - end if | |
| 197 | - | |
| 198 | - type is (command_node_t) | |
| 199 | - print *, ' (COMMAND)' | |
| 200 | - if (allocated(node%words)) then | |
| 201 | - do i = 1, size(node%words) | |
| 202 | - select type(word => node%words(i)) | |
| 203 | - type is (word_node_t) | |
| 204 | - write(*, '(a,a)') repeat(' ', (ind+1)*2) // 'Word: ', trim(word%text) | |
| 205 | - end select | |
| 206 | - end do | |
| 207 | - end if | |
| 208 | - | |
| 209 | - type is (break_node_t) | |
| 210 | - print *, ' (BREAK) levels=', node%levels | |
| 211 | - | |
| 212 | - type is (continue_node_t) | |
| 213 | - print *, ' (CONTINUE) levels=', node%levels | |
| 214 | - | |
| 215 | - class default | |
| 216 | - print *, '' | |
| 217 | - end select | |
| 218 | - end subroutine print_ast_structure | |
| 219 | - | |
| 220 | -end program test_ast | |
tests/test_ast_simple.f90deleted@@ -1,134 +0,0 @@ | ||
| 1 | -! ============================================================================== | |
| 2 | -! Simple test for AST modules without dependencies | |
| 3 | -! ============================================================================== | |
| 4 | -program test_ast_simple | |
| 5 | - use ast_types | |
| 6 | - use lexer | |
| 7 | - implicit none | |
| 8 | - | |
| 9 | - call test_lexer() | |
| 10 | - call test_token_types() | |
| 11 | - | |
| 12 | -contains | |
| 13 | - | |
| 14 | - subroutine test_lexer() | |
| 15 | - type(lexer_t) :: lex | |
| 16 | - character(:), allocatable :: input | |
| 17 | - integer :: i | |
| 18 | - | |
| 19 | - print *, "=== Lexer Test ===" | |
| 20 | - | |
| 21 | - ! Test 1: Simple command | |
| 22 | - input = 'echo "Hello World"' | |
| 23 | - call lex%init(input) | |
| 24 | - call lex%tokenize() | |
| 25 | - | |
| 26 | - print *, "Input: ", input | |
| 27 | - print *, "Tokens: ", lex%token_count | |
| 28 | - do i = 1, lex%token_count | |
| 29 | - print '(a,i2,a,a,a)', " [", lex%tokens(i)%type, "] ", & | |
| 30 | - trim(lex%tokens(i)%value), & | |
| 31 | - get_token_name(lex%tokens(i)%type) | |
| 32 | - end do | |
| 33 | - print *, "" | |
| 34 | - | |
| 35 | - call lex%destroy() | |
| 36 | - | |
| 37 | - ! Test 2: For loop | |
| 38 | - input = 'for i in 1 2 3; do echo $i; done' | |
| 39 | - call lex%init(input) | |
| 40 | - call lex%tokenize() | |
| 41 | - | |
| 42 | - print *, "Input: ", input | |
| 43 | - print *, "Tokens: ", lex%token_count | |
| 44 | - do i = 1, lex%token_count | |
| 45 | - print '(a,i2,a,a,a)', " [", lex%tokens(i)%type, "] ", & | |
| 46 | - trim(lex%tokens(i)%value), & | |
| 47 | - get_token_name(lex%tokens(i)%type) | |
| 48 | - end do | |
| 49 | - print *, "" | |
| 50 | - | |
| 51 | - call lex%destroy() | |
| 52 | - | |
| 53 | - ! Test 3: Pipeline | |
| 54 | - input = 'cat file.txt | grep pattern | wc -l' | |
| 55 | - call lex%init(input) | |
| 56 | - call lex%tokenize() | |
| 57 | - | |
| 58 | - print *, "Input: ", input | |
| 59 | - print *, "Tokens: ", lex%token_count | |
| 60 | - do i = 1, lex%token_count | |
| 61 | - print '(a,i2,a,a,a)', " [", lex%tokens(i)%type, "] ", & | |
| 62 | - trim(lex%tokens(i)%value), & | |
| 63 | - get_token_name(lex%tokens(i)%type) | |
| 64 | - end do | |
| 65 | - print *, "" | |
| 66 | - | |
| 67 | - call lex%destroy() | |
| 68 | - | |
| 69 | - ! Test 4: Redirections | |
| 70 | - input = 'echo test > output.txt 2>&1' | |
| 71 | - call lex%init(input) | |
| 72 | - call lex%tokenize() | |
| 73 | - | |
| 74 | - print *, "Input: ", input | |
| 75 | - print *, "Tokens: ", lex%token_count | |
| 76 | - do i = 1, lex%token_count | |
| 77 | - print '(a,i2,a,a,a)', " [", lex%tokens(i)%type, "] ", & | |
| 78 | - trim(lex%tokens(i)%value), & | |
| 79 | - get_token_name(lex%tokens(i)%type) | |
| 80 | - end do | |
| 81 | - print *, "" | |
| 82 | - | |
| 83 | - call lex%destroy() | |
| 84 | - end subroutine test_lexer | |
| 85 | - | |
| 86 | - subroutine test_token_types() | |
| 87 | - print *, "=== Token Type Test ===" | |
| 88 | - print *, "Keywords are recognized:" | |
| 89 | - print *, " 'for' -> ", is_keyword('for') | |
| 90 | - print *, " 'if' -> ", is_keyword('if') | |
| 91 | - print *, " 'echo' -> ", is_keyword('echo') | |
| 92 | - print *, " 'break' -> ", is_keyword('break') | |
| 93 | - print *, "" | |
| 94 | - | |
| 95 | - print *, "Token types for keywords:" | |
| 96 | - print *, " 'for' -> ", keyword_token_type('for'), " (should be ", TOKEN_FOR, ")" | |
| 97 | - print *, " 'done' -> ", keyword_token_type('done'), " (should be ", TOKEN_DONE, ")" | |
| 98 | - print *, " 'break' -> ", keyword_token_type('break'), " (should be ", TOKEN_BREAK, ")" | |
| 99 | - print *, "" | |
| 100 | - end subroutine test_token_types | |
| 101 | - | |
| 102 | - function get_token_name(token_type) result(name) | |
| 103 | - integer, intent(in) :: token_type | |
| 104 | - character(20) :: name | |
| 105 | - | |
| 106 | - select case(token_type) | |
| 107 | - case(TOKEN_EOF); name = ' (EOF)' | |
| 108 | - case(TOKEN_WORD); name = ' (WORD)' | |
| 109 | - case(TOKEN_STRING); name = ' (STRING)' | |
| 110 | - case(TOKEN_VARIABLE); name = ' (VAR)' | |
| 111 | - case(TOKEN_SEMICOLON); name = ' (;)' | |
| 112 | - case(TOKEN_NEWLINE); name = ' (NEWLINE)' | |
| 113 | - case(TOKEN_PIPE); name = ' (|)' | |
| 114 | - case(TOKEN_AND); name = ' (&&)' | |
| 115 | - case(TOKEN_OR); name = ' (||)' | |
| 116 | - case(TOKEN_BACKGROUND); name = ' (&)' | |
| 117 | - case(TOKEN_REDIRECT_IN); name = ' (<)' | |
| 118 | - case(TOKEN_REDIRECT_OUT); name = ' (>)' | |
| 119 | - case(TOKEN_REDIRECT_APPEND); name = ' (>>)' | |
| 120 | - case(TOKEN_FOR); name = ' (FOR)' | |
| 121 | - case(TOKEN_IN); name = ' (IN)' | |
| 122 | - case(TOKEN_DO); name = ' (DO)' | |
| 123 | - case(TOKEN_DONE); name = ' (DONE)' | |
| 124 | - case(TOKEN_IF); name = ' (IF)' | |
| 125 | - case(TOKEN_THEN); name = ' (THEN)' | |
| 126 | - case(TOKEN_ELSE); name = ' (ELSE)' | |
| 127 | - case(TOKEN_FI); name = ' (FI)' | |
| 128 | - case(TOKEN_BREAK); name = ' (BREAK)' | |
| 129 | - case(TOKEN_CONTINUE); name = ' (CONTINUE)' | |
| 130 | - case default; name = ' (?)' | |
| 131 | - end select | |
| 132 | - end function get_token_name | |
| 133 | - | |
| 134 | -end program test_ast_simple | |
tests/test_bash_rematch.shdeleted@@ -1,242 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Comprehensive test suite for BASH_REMATCH array with regex matching | |
| 3 | - | |
| 4 | -echo "=== BASH_REMATCH Array Test Suite ===" | |
| 5 | -echo "" | |
| 6 | - | |
| 7 | -# Test 1: Basic capture group | |
| 8 | -echo "TEST 1: Basic Capture Group" | |
| 9 | -echo "============================" | |
| 10 | -str="John Doe" | |
| 11 | -if [[ $str =~ ^([A-Z][a-z]+)\ ([A-Z][a-z]+)$ ]]; then | |
| 12 | - echo "✓ PASS: Pattern matched" | |
| 13 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (expected: 'John Doe')" | |
| 14 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (expected: 'John')" | |
| 15 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (expected: 'Doe')" | |
| 16 | -else | |
| 17 | - echo "✗ FAIL: Pattern should match" | |
| 18 | -fi | |
| 19 | -echo "" | |
| 20 | - | |
| 21 | -# Test 2: Email address capture groups | |
| 22 | -echo "TEST 2: Email Address Capture" | |
| 23 | -echo "==============================" | |
| 24 | -email="user@example.com" | |
| 25 | -if [[ $email =~ ^([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,})$ ]]; then | |
| 26 | - echo "✓ PASS: Email pattern matched" | |
| 27 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full match)" | |
| 28 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (username)" | |
| 29 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (domain)" | |
| 30 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (TLD)" | |
| 31 | -else | |
| 32 | - echo "✗ FAIL: Email pattern should match" | |
| 33 | -fi | |
| 34 | -echo "" | |
| 35 | - | |
| 36 | -# Test 3: Phone number capture | |
| 37 | -echo "TEST 3: Phone Number Capture" | |
| 38 | -echo "=============================" | |
| 39 | -phone="(555) 123-4567" | |
| 40 | -if [[ $phone =~ ^\(([0-9]{3})\)\ ([0-9]{3})-([0-9]{4})$ ]]; then | |
| 41 | - echo "✓ PASS: Phone pattern matched" | |
| 42 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full match)" | |
| 43 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (area code)" | |
| 44 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (prefix)" | |
| 45 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (line number)" | |
| 46 | -else | |
| 47 | - echo "✗ FAIL: Phone pattern should match" | |
| 48 | -fi | |
| 49 | -echo "" | |
| 50 | - | |
| 51 | -# Test 4: Date extraction | |
| 52 | -echo "TEST 4: Date Extraction" | |
| 53 | -echo "=======================" | |
| 54 | -date="2025-10-10" | |
| 55 | -if [[ $date =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})$ ]]; then | |
| 56 | - echo "✓ PASS: Date pattern matched" | |
| 57 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full date)" | |
| 58 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (year)" | |
| 59 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (month)" | |
| 60 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (day)" | |
| 61 | -else | |
| 62 | - echo "✗ FAIL: Date pattern should match" | |
| 63 | -fi | |
| 64 | -echo "" | |
| 65 | - | |
| 66 | -# Test 5: URL parsing | |
| 67 | -echo "TEST 5: URL Parsing" | |
| 68 | -echo "===================" | |
| 69 | -url="https://example.com/path/to/file.html" | |
| 70 | -if [[ $url =~ ^(https?)://([^/]+)(/.*)?$ ]]; then | |
| 71 | - echo "✓ PASS: URL pattern matched" | |
| 72 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full URL)" | |
| 73 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (protocol)" | |
| 74 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (domain)" | |
| 75 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (path)" | |
| 76 | -else | |
| 77 | - echo "✗ FAIL: URL pattern should match" | |
| 78 | -fi | |
| 79 | -echo "" | |
| 80 | - | |
| 81 | -# Test 6: Version string parsing | |
| 82 | -echo "TEST 6: Version String Parsing" | |
| 83 | -echo "===============================" | |
| 84 | -version="v1.2.3-beta" | |
| 85 | -if [[ $version =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-(.+))?$ ]]; then | |
| 86 | - echo "✓ PASS: Version pattern matched" | |
| 87 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full version)" | |
| 88 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (major)" | |
| 89 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (minor)" | |
| 90 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (patch)" | |
| 91 | - echo " BASH_REMATCH[4]: '${BASH_REMATCH[4]}' (suffix with -)" | |
| 92 | - echo " BASH_REMATCH[5]: '${BASH_REMATCH[5]}' (suffix)" | |
| 93 | -else | |
| 94 | - echo "✗ FAIL: Version pattern should match" | |
| 95 | -fi | |
| 96 | -echo "" | |
| 97 | - | |
| 98 | -# Test 7: IPv4 address parsing | |
| 99 | -echo "TEST 7: IPv4 Address Parsing" | |
| 100 | -echo "=============================" | |
| 101 | -ip="192.168.1.100" | |
| 102 | -if [[ $ip =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$ ]]; then | |
| 103 | - echo "✓ PASS: IPv4 pattern matched" | |
| 104 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full IP)" | |
| 105 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (octet 1)" | |
| 106 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (octet 2)" | |
| 107 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (octet 3)" | |
| 108 | - echo " BASH_REMATCH[4]: '${BASH_REMATCH[4]}' (octet 4)" | |
| 109 | -else | |
| 110 | - echo "✗ FAIL: IPv4 pattern should match" | |
| 111 | -fi | |
| 112 | -echo "" | |
| 113 | - | |
| 114 | -# Test 8: Single capture group | |
| 115 | -echo "TEST 8: Single Capture Group" | |
| 116 | -echo "=============================" | |
| 117 | -str="Error: File not found" | |
| 118 | -if [[ $str =~ ^(Error|Warning): ]]; then | |
| 119 | - echo "✓ PASS: Single group matched" | |
| 120 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full match)" | |
| 121 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (severity)" | |
| 122 | -else | |
| 123 | - echo "✗ FAIL: Pattern should match" | |
| 124 | -fi | |
| 125 | -echo "" | |
| 126 | - | |
| 127 | -# Test 9: No capture groups (just full match) | |
| 128 | -echo "TEST 9: No Capture Groups" | |
| 129 | -echo "=========================" | |
| 130 | -str="12345" | |
| 131 | -if [[ $str =~ ^[0-9]+$ ]]; then | |
| 132 | - echo "✓ PASS: Pattern matched (no groups)" | |
| 133 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full match)" | |
| 134 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (should be empty)" | |
| 135 | -else | |
| 136 | - echo "✗ FAIL: Pattern should match" | |
| 137 | -fi | |
| 138 | -echo "" | |
| 139 | - | |
| 140 | -# Test 10: Nested groups | |
| 141 | -echo "TEST 10: Nested Groups" | |
| 142 | -echo "======================" | |
| 143 | -str="abc123def" | |
| 144 | -if [[ $str =~ ^([a-z]+)([0-9]+)([a-z]+)$ ]]; then | |
| 145 | - echo "✓ PASS: Multiple groups matched" | |
| 146 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}' (full match)" | |
| 147 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}' (first letters)" | |
| 148 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (numbers)" | |
| 149 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (last letters)" | |
| 150 | -else | |
| 151 | - echo "✗ FAIL: Pattern should match" | |
| 152 | -fi | |
| 153 | -echo "" | |
| 154 | - | |
| 155 | -# Test 11: Optional groups | |
| 156 | -echo "TEST 11: Optional Groups" | |
| 157 | -echo "========================" | |
| 158 | -str1="test" | |
| 159 | -str2="test-123" | |
| 160 | -if [[ $str1 =~ ^(test)(-([0-9]+))?$ ]]; then | |
| 161 | - echo "✓ PASS (1/2): Pattern matched without optional" | |
| 162 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}'" | |
| 163 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}'" | |
| 164 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (should be empty)" | |
| 165 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (should be empty)" | |
| 166 | -else | |
| 167 | - echo "✗ FAIL (1/2): Pattern should match" | |
| 168 | -fi | |
| 169 | - | |
| 170 | -if [[ $str2 =~ ^(test)(-([0-9]+))?$ ]]; then | |
| 171 | - echo "✓ PASS (2/2): Pattern matched with optional" | |
| 172 | - echo " BASH_REMATCH[0]: '${BASH_REMATCH[0]}'" | |
| 173 | - echo " BASH_REMATCH[1]: '${BASH_REMATCH[1]}'" | |
| 174 | - echo " BASH_REMATCH[2]: '${BASH_REMATCH[2]}' (optional part)" | |
| 175 | - echo " BASH_REMATCH[3]: '${BASH_REMATCH[3]}' (number)" | |
| 176 | -else | |
| 177 | - echo "✗ FAIL (2/2): Pattern should match" | |
| 178 | -fi | |
| 179 | -echo "" | |
| 180 | - | |
| 181 | -# Test 12: Using captures in script logic | |
| 182 | -echo "TEST 12: Practical Use - File Extension Parser" | |
| 183 | -echo "===============================================" | |
| 184 | -filename="document.tar.gz" | |
| 185 | -if [[ $filename =~ ^(.+)\.([^.]+)$ ]]; then | |
| 186 | - basename="${BASH_REMATCH[1]}" | |
| 187 | - extension="${BASH_REMATCH[2]}" | |
| 188 | - echo "✓ PASS: File parsed" | |
| 189 | - echo " Basename: '$basename'" | |
| 190 | - echo " Extension: '$extension'" | |
| 191 | -else | |
| 192 | - echo "✗ FAIL: Pattern should match" | |
| 193 | -fi | |
| 194 | -echo "" | |
| 195 | - | |
| 196 | -# Test 13: Array length check | |
| 197 | -echo "TEST 13: Array Length (Number of Captures)" | |
| 198 | -echo "===========================================" | |
| 199 | -str="abc-123-def" | |
| 200 | -if [[ $str =~ ^([a-z]+)-([0-9]+)-([a-z]+)$ ]]; then | |
| 201 | - echo "✓ PASS: Pattern matched with 3 groups" | |
| 202 | - echo " Array contents:" | |
| 203 | - echo " [0]: '${BASH_REMATCH[0]}'" | |
| 204 | - echo " [1]: '${BASH_REMATCH[1]}'" | |
| 205 | - echo " [2]: '${BASH_REMATCH[2]}'" | |
| 206 | - echo " [3]: '${BASH_REMATCH[3]}'" | |
| 207 | - echo " Array length: ${#BASH_REMATCH[@]}" | |
| 208 | -else | |
| 209 | - echo "✗ FAIL: Pattern should match" | |
| 210 | -fi | |
| 211 | -echo "" | |
| 212 | - | |
| 213 | -# Test 14: Loop through captures | |
| 214 | -echo "TEST 14: Iterate Through Captures" | |
| 215 | -echo "==================================" | |
| 216 | -str="a:b:c:d" | |
| 217 | -if [[ $str =~ ^([a-z]):([a-z]):([a-z]):([a-z])$ ]]; then | |
| 218 | - echo "✓ PASS: Pattern matched" | |
| 219 | - echo " Iterating through BASH_REMATCH:" | |
| 220 | - for i in {0..4}; do | |
| 221 | - echo " [$i]: '${BASH_REMATCH[$i]}'" | |
| 222 | - done | |
| 223 | -else | |
| 224 | - echo "✗ FAIL: Pattern should match" | |
| 225 | -fi | |
| 226 | -echo "" | |
| 227 | - | |
| 228 | -# Summary | |
| 229 | -echo "=========================================" | |
| 230 | -echo "BASH_REMATCH Array Test Suite Complete" | |
| 231 | -echo "=========================================" | |
| 232 | -echo "" | |
| 233 | -echo "Features Tested:" | |
| 234 | -echo " ✓ Full match capture (BASH_REMATCH[0])" | |
| 235 | -echo " ✓ Multiple capture groups" | |
| 236 | -echo " ✓ Email, phone, date, URL parsing" | |
| 237 | -echo " ✓ Optional groups" | |
| 238 | -echo " ✓ Nested groups" | |
| 239 | -echo " ✓ Array length and iteration" | |
| 240 | -echo " ✓ Practical use cases" | |
| 241 | -echo "" | |
| 242 | -echo "Note: Run this test with both bash and fortsh to compare" | |
tests/test_brace_expansion.shdeleted@@ -1,171 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Comprehensive test suite for brace expansion | |
| 3 | - | |
| 4 | -echo "=== Brace Expansion Test Suite ===" | |
| 5 | -echo "" | |
| 6 | - | |
| 7 | -# Test 1: List expansion | |
| 8 | -echo "TEST 1: List Expansion {a,b,c}" | |
| 9 | -echo "==============================" | |
| 10 | -result="{a,b,c}" | |
| 11 | -echo "Input: {a,b,c}" | |
| 12 | -echo "Output: $result" | |
| 13 | -expected="a b c" | |
| 14 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 15 | -echo "" | |
| 16 | - | |
| 17 | -# Test 2: Numeric range (ascending) | |
| 18 | -echo "TEST 2: Numeric Range {1..5}" | |
| 19 | -echo "=============================" | |
| 20 | -result="{1..5}" | |
| 21 | -echo "Input: {1..5}" | |
| 22 | -echo "Output: $result" | |
| 23 | -expected="1 2 3 4 5" | |
| 24 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 25 | -echo "" | |
| 26 | - | |
| 27 | -# Test 3: Numeric range (descending) | |
| 28 | -echo "TEST 3: Numeric Range {5..1}" | |
| 29 | -echo "=============================" | |
| 30 | -result="{5..1}" | |
| 31 | -echo "Input: {5..1}" | |
| 32 | -echo "Output: $result" | |
| 33 | -expected="5 4 3 2 1" | |
| 34 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 35 | -echo "" | |
| 36 | - | |
| 37 | -# Test 4: Alphabetic range (ascending) | |
| 38 | -echo "TEST 4: Alphabetic Range {a..e}" | |
| 39 | -echo "================================" | |
| 40 | -result="{a..e}" | |
| 41 | -echo "Input: {a..e}" | |
| 42 | -echo "Output: $result" | |
| 43 | -expected="a b c d e" | |
| 44 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 45 | -echo "" | |
| 46 | - | |
| 47 | -# Test 5: Alphabetic range (descending) | |
| 48 | -echo "TEST 5: Alphabetic Range {e..a}" | |
| 49 | -echo "================================" | |
| 50 | -result="{e..a}" | |
| 51 | -echo "Input: {e..a}" | |
| 52 | -echo "Output: $result" | |
| 53 | -expected="e d c b a" | |
| 54 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 55 | -echo "" | |
| 56 | - | |
| 57 | -# Test 6: Numeric range with step | |
| 58 | -echo "TEST 6: Step Expansion {1..10..2}" | |
| 59 | -echo "==================================" | |
| 60 | -result="{1..10..2}" | |
| 61 | -echo "Input: {1..10..2}" | |
| 62 | -echo "Output: $result" | |
| 63 | -expected="1 3 5 7 9" | |
| 64 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 65 | -echo "" | |
| 66 | - | |
| 67 | -# Test 7: Alphabetic range with step | |
| 68 | -echo "TEST 7: Step Expansion {a..z..3}" | |
| 69 | -echo "=================================" | |
| 70 | -result="{a..z..3}" | |
| 71 | -echo "Input: {a..z..3}" | |
| 72 | -echo "Output: $result" | |
| 73 | -expected="a d g j m p s v y" | |
| 74 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 75 | -echo "" | |
| 76 | - | |
| 77 | -# Test 8: List with multiple items | |
| 78 | -echo "TEST 8: Multiple Items {red,green,blue}" | |
| 79 | -echo "========================================" | |
| 80 | -result="{red,green,blue}" | |
| 81 | -echo "Input: {red,green,blue}" | |
| 82 | -echo "Output: $result" | |
| 83 | -expected="red green blue" | |
| 84 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 85 | -echo "" | |
| 86 | - | |
| 87 | -# Test 9: Zero-padded numbers | |
| 88 | -echo "TEST 9: Zero-padded {01..05}" | |
| 89 | -echo "============================" | |
| 90 | -result="{01..05}" | |
| 91 | -echo "Input: {01..05}" | |
| 92 | -echo "Output: $result" | |
| 93 | -# Note: Our implementation doesn't preserve zero-padding yet | |
| 94 | -expected_variations=("1 2 3 4 5" "01 02 03 04 05") | |
| 95 | -match=false | |
| 96 | -for exp in "${expected_variations[@]}"; do | |
| 97 | - if [ "$result" = "$exp" ]; then | |
| 98 | - match=true | |
| 99 | - break | |
| 100 | - fi | |
| 101 | -done | |
| 102 | -[ "$match" = true ] && echo "✓ PASS" || echo "✗ FAIL (got: $result)" | |
| 103 | -echo "" | |
| 104 | - | |
| 105 | -# Test 10: Longer range | |
| 106 | -echo "TEST 10: Longer Range {1..20}" | |
| 107 | -echo "==============================" | |
| 108 | -result="{1..20}" | |
| 109 | -echo "Input: {1..20}" | |
| 110 | -echo "Output: $result" | |
| 111 | -expected="1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20" | |
| 112 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL" | |
| 113 | -echo "" | |
| 114 | - | |
| 115 | -# Test 11: Single character items | |
| 116 | -echo "TEST 11: Single chars {x,y,z}" | |
| 117 | -echo "==============================" | |
| 118 | -result="{x,y,z}" | |
| 119 | -echo "Input: {x,y,z}" | |
| 120 | -echo "Output: $result" | |
| 121 | -expected="x y z" | |
| 122 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 123 | -echo "" | |
| 124 | - | |
| 125 | -# Test 12: echo command with braces | |
| 126 | -echo "TEST 12: echo with brace expansion" | |
| 127 | -echo "===================================" | |
| 128 | -echo "Command: echo {1,2,3}" | |
| 129 | -output=$(echo {1,2,3}) | |
| 130 | -echo "Output: $output" | |
| 131 | -# In bash, this outputs: 1 2 3 | |
| 132 | -[[ "$output" =~ [123] ]] && echo "✓ PASS" || echo "✗ FAIL" | |
| 133 | -echo "" | |
| 134 | - | |
| 135 | -# Test 13: Descending with step | |
| 136 | -echo "TEST 13: Descending with step {10..1..2}" | |
| 137 | -echo "=========================================" | |
| 138 | -result="{10..1..2}" | |
| 139 | -echo "Input: {10..1..2}" | |
| 140 | -echo "Output: $result" | |
| 141 | -expected="10 8 6 4 2" | |
| 142 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 143 | -echo "" | |
| 144 | - | |
| 145 | -# Test 14: Large step | |
| 146 | -echo "TEST 14: Large step {1..20..5}" | |
| 147 | -echo "===============================" | |
| 148 | -result="{1..20..5}" | |
| 149 | -echo "Input: {1..20..5}" | |
| 150 | -echo "Output: $result" | |
| 151 | -expected="1 6 11 16" | |
| 152 | -[ "$result" = "$expected" ] && echo "✓ PASS" || echo "✗ FAIL (expected: $expected, got: $result)" | |
| 153 | -echo "" | |
| 154 | - | |
| 155 | -# Summary | |
| 156 | -echo "=========================================" | |
| 157 | -echo "Brace Expansion Test Suite Complete" | |
| 158 | -echo "=========================================" | |
| 159 | -echo "" | |
| 160 | -echo "Features Tested:" | |
| 161 | -echo " ✓ List expansion {a,b,c}" | |
| 162 | -echo " ✓ Numeric range (ascending & descending)" | |
| 163 | -echo " ✓ Alphabetic range (ascending & descending)" | |
| 164 | -echo " ✓ Step expansion (numeric & alphabetic)" | |
| 165 | -echo " ✓ Various range sizes" | |
| 166 | -echo " ✓ Integration with echo command" | |
| 167 | -echo "" | |
| 168 | -echo "Note: Advanced features not yet implemented:" | |
| 169 | -echo " - Prefix/suffix: file{1,2}.txt" | |
| 170 | -echo " - Nested braces: {{a,b},{c,d}}" | |
| 171 | -echo " - Mixed content: {a..c}{1..3}" | |
tests/test_brace_expansion_proper.shdeleted@@ -1,120 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Proper brace expansion tests (unquoted, as used in real shells) | |
| 3 | - | |
| 4 | -echo "=== Proper Brace Expansion Tests ===" | |
| 5 | -echo "" | |
| 6 | - | |
| 7 | -# Test 1: List expansion with echo | |
| 8 | -echo "TEST 1: echo {a,b,c}" | |
| 9 | -echo "====================" | |
| 10 | -echo {a,b,c} | |
| 11 | -echo "Expected: a b c (or: a b c on separate line)" | |
| 12 | -echo "✓ PASS (if you see: a b c above)" | |
| 13 | -echo "" | |
| 14 | - | |
| 15 | -# Test 2: Numeric range | |
| 16 | -echo "TEST 2: echo {1..5}" | |
| 17 | -echo "===================" | |
| 18 | -echo {1..5} | |
| 19 | -echo "Expected: 1 2 3 4 5" | |
| 20 | -echo "✓ PASS (if correct above)" | |
| 21 | -echo "" | |
| 22 | - | |
| 23 | -# Test 3: Descending numeric range | |
| 24 | -echo "TEST 3: echo {5..1}" | |
| 25 | -echo "===================" | |
| 26 | -echo {5..1} | |
| 27 | -echo "Expected: 5 4 3 2 1" | |
| 28 | -echo "✓ PASS (if correct above)" | |
| 29 | -echo "" | |
| 30 | - | |
| 31 | -# Test 4: Alphabetic range | |
| 32 | -echo "TEST 4: echo {a..e}" | |
| 33 | -echo "===================" | |
| 34 | -echo {a..e} | |
| 35 | -echo "Expected: a b c d e" | |
| 36 | -echo "✓ PASS (if correct above)" | |
| 37 | -echo "" | |
| 38 | - | |
| 39 | -# Test 5: Step expansion | |
| 40 | -echo "TEST 5: echo {1..10..2}" | |
| 41 | -echo "=======================" | |
| 42 | -echo {1..10..2} | |
| 43 | -echo "Expected: 1 3 5 7 9" | |
| 44 | -echo "✓ PASS (if correct above)" | |
| 45 | -echo "" | |
| 46 | - | |
| 47 | -# Test 6: Alphabetic with step | |
| 48 | -echo "TEST 6: echo {a..z..5}" | |
| 49 | -echo "======================" | |
| 50 | -echo {a..z..5} | |
| 51 | -echo "Expected: a f k p u z" | |
| 52 | -echo "✓ PASS (if correct above)" | |
| 53 | -echo "" | |
| 54 | - | |
| 55 | -# Test 7: Mixed items | |
| 56 | -echo "TEST 7: echo {red,green,blue}" | |
| 57 | -echo "==============================" | |
| 58 | -echo {red,green,blue} | |
| 59 | -echo "Expected: red green blue" | |
| 60 | -echo "✓ PASS (if correct above)" | |
| 61 | -echo "" | |
| 62 | - | |
| 63 | -# Test 8: Single item (no expansion) | |
| 64 | -echo "TEST 8: echo {single}" | |
| 65 | -echo "=====================" | |
| 66 | -echo {single} | |
| 67 | -echo "Expected: {single} (no expansion)" | |
| 68 | -echo "✓ PASS (if correct above)" | |
| 69 | -echo "" | |
| 70 | - | |
| 71 | -# Test 9: No braces | |
| 72 | -echo "TEST 9: echo normal" | |
| 73 | -echo "===================" | |
| 74 | -echo normal | |
| 75 | -echo "Expected: normal" | |
| 76 | -echo "✓ PASS (if correct above)" | |
| 77 | -echo "" | |
| 78 | - | |
| 79 | -# Test 10: Range with for loop | |
| 80 | -echo "TEST 10: for loop with {1..3}" | |
| 81 | -echo "==============================" | |
| 82 | -for i in {1..3}; do | |
| 83 | - echo " Iteration $i" | |
| 84 | -done | |
| 85 | -echo "Expected: Iteration 1, 2, 3" | |
| 86 | -echo "✓ PASS (if correct above)" | |
| 87 | -echo "" | |
| 88 | - | |
| 89 | -# Test 11: Multiple braces in one command | |
| 90 | -echo "TEST 11: echo {a,b} {1,2}" | |
| 91 | -echo "=========================" | |
| 92 | -echo {a,b} {1,2} | |
| 93 | -echo "Expected: a b 1 2" | |
| 94 | -echo "✓ PASS (if correct above)" | |
| 95 | -echo "" | |
| 96 | - | |
| 97 | -# Test 12: Descending with step | |
| 98 | -echo "TEST 12: echo {10..1..2}" | |
| 99 | -echo "========================" | |
| 100 | -echo {10..1..2} | |
| 101 | -echo "Expected: 10 8 6 4 2" | |
| 102 | -echo "✓ PASS (if correct above)" | |
| 103 | -echo "" | |
| 104 | - | |
| 105 | -# Summary | |
| 106 | -echo "=========================================" | |
| 107 | -echo "Brace Expansion Test Complete" | |
| 108 | -echo "=========================================" | |
| 109 | -echo "" | |
| 110 | -echo "Manual verification required." | |
| 111 | -echo "Compare actual output with expected output above." | |
| 112 | -echo "" | |
| 113 | -echo "Features tested:" | |
| 114 | -echo " - List expansion: {a,b,c}" | |
| 115 | -echo " - Numeric ranges: {1..10}" | |
| 116 | -echo " - Alphabetic ranges: {a..z}" | |
| 117 | -echo " - Step expansion: {1..10..2}" | |
| 118 | -echo " - Descending ranges" | |
| 119 | -echo " - Multiple brace groups" | |
| 120 | -echo " - For loop integration" | |
tests/test_getopts.shdeleted@@ -1,161 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Test suite for getopts builtin | |
| 3 | - | |
| 4 | -echo "=== getopts Builtin Test Suite ===" | |
| 5 | -echo "" | |
| 6 | - | |
| 7 | -# Test 1: Basic option parsing | |
| 8 | -echo "TEST 1: Basic Option Parsing" | |
| 9 | -echo "=============================" | |
| 10 | -test_basic() { | |
| 11 | - while getopts "abc" opt; do | |
| 12 | - case $opt in | |
| 13 | - a) echo "Option -a found" ;; | |
| 14 | - b) echo "Option -b found" ;; | |
| 15 | - c) echo "Option -c found" ;; | |
| 16 | - \?) echo "Invalid option: -$OPTARG" ;; | |
| 17 | - esac | |
| 18 | - done | |
| 19 | -} | |
| 20 | - | |
| 21 | -set -- -a -b -c | |
| 22 | -test_basic | |
| 23 | -[ $? -eq 0 ] && echo "✓ PASS: Basic option parsing" || echo "✗ FAIL" | |
| 24 | -echo "" | |
| 25 | - | |
| 26 | -# Test 2: Options with arguments | |
| 27 | -echo "TEST 2: Options with Arguments" | |
| 28 | -echo "==============================" | |
| 29 | -test_with_args() { | |
| 30 | - while getopts "a:b:c" opt; do | |
| 31 | - case $opt in | |
| 32 | - a) echo "Option -a with arg: $OPTARG" ;; | |
| 33 | - b) echo "Option -b with arg: $OPTARG" ;; | |
| 34 | - c) echo "Option -c (no arg)" ;; | |
| 35 | - \?) echo "Invalid option: -$OPTARG" ;; | |
| 36 | - esac | |
| 37 | - done | |
| 38 | -} | |
| 39 | - | |
| 40 | -set -- -a value1 -b value2 -c | |
| 41 | -OPTIND=1 # Reset OPTIND | |
| 42 | -test_with_args | |
| 43 | -echo "✓ PASS: Options with arguments" | |
| 44 | -echo "" | |
| 45 | - | |
| 46 | -# Test 3: Combined option format (-ovalue) | |
| 47 | -echo "TEST 3: Combined Option Format" | |
| 48 | -echo "==============================" | |
| 49 | -test_combined() { | |
| 50 | - while getopts "o:" opt; do | |
| 51 | - case $opt in | |
| 52 | - o) echo "Option -o with arg: $OPTARG" ;; | |
| 53 | - \?) echo "Invalid option: -$OPTARG" ;; | |
| 54 | - esac | |
| 55 | - done | |
| 56 | -} | |
| 57 | - | |
| 58 | -set -- -ofilename.txt | |
| 59 | -OPTIND=1 | |
| 60 | -test_combined | |
| 61 | -echo "✓ PASS: Combined format" | |
| 62 | -echo "" | |
| 63 | - | |
| 64 | -# Test 4: Invalid option handling | |
| 65 | -echo "TEST 4: Invalid Option Handling" | |
| 66 | -echo "===============================" | |
| 67 | -test_invalid() { | |
| 68 | - while getopts "abc" opt; do | |
| 69 | - case $opt in | |
| 70 | - a|b|c) echo "Valid option: -$opt" ;; | |
| 71 | - \?) echo "Invalid option detected: $OPTARG" ;; | |
| 72 | - esac | |
| 73 | - done | |
| 74 | -} | |
| 75 | - | |
| 76 | -set -- -a -x -b | |
| 77 | -OPTIND=1 | |
| 78 | -test_invalid | |
| 79 | -echo "✓ PASS: Invalid option handling" | |
| 80 | -echo "" | |
| 81 | - | |
| 82 | -# Test 5: OPTIND tracking | |
| 83 | -echo "TEST 5: OPTIND Tracking" | |
| 84 | -echo "=======================" | |
| 85 | -set -- -a -b arg1 arg2 | |
| 86 | -OPTIND=1 | |
| 87 | -while getopts "ab" opt; do | |
| 88 | - echo "Processing -$opt, OPTIND=$OPTIND" | |
| 89 | -done | |
| 90 | -echo "After processing options, OPTIND=$OPTIND" | |
| 91 | -echo "Remaining args: ${@:$OPTIND}" | |
| 92 | -echo "✓ PASS: OPTIND tracking" | |
| 93 | -echo "" | |
| 94 | - | |
| 95 | -# Test 6: Mixed options and arguments | |
| 96 | -echo "TEST 6: Mixed Options and Arguments" | |
| 97 | -echo "====================================" | |
| 98 | -parse_mixed() { | |
| 99 | - local verbose=false | |
| 100 | - local output="" | |
| 101 | - | |
| 102 | - while getopts "vo:" opt; do | |
| 103 | - case $opt in | |
| 104 | - v) verbose=true ;; | |
| 105 | - o) output="$OPTARG" ;; | |
| 106 | - \?) return 1 ;; | |
| 107 | - esac | |
| 108 | - done | |
| 109 | - | |
| 110 | - shift $((OPTIND-1)) | |
| 111 | - | |
| 112 | - echo "verbose=$verbose" | |
| 113 | - echo "output=$output" | |
| 114 | - echo "remaining args: $@" | |
| 115 | -} | |
| 116 | - | |
| 117 | -set -- -v -o output.txt file1.txt file2.txt | |
| 118 | -OPTIND=1 | |
| 119 | -parse_mixed | |
| 120 | -echo "✓ PASS: Mixed options and arguments" | |
| 121 | -echo "" | |
| 122 | - | |
| 123 | -# Test 7: Multiple uses in same script | |
| 124 | -echo "TEST 7: Multiple Uses in Same Script" | |
| 125 | -echo "====================================" | |
| 126 | -first_parse() { | |
| 127 | - OPTIND=1 | |
| 128 | - while getopts "ab" opt; do | |
| 129 | - echo "First parse: -$opt" | |
| 130 | - done | |
| 131 | -} | |
| 132 | - | |
| 133 | -second_parse() { | |
| 134 | - OPTIND=1 | |
| 135 | - while getopts "xy" opt; do | |
| 136 | - echo "Second parse: -$opt" | |
| 137 | - done | |
| 138 | -} | |
| 139 | - | |
| 140 | -set -- -a -b | |
| 141 | -first_parse | |
| 142 | - | |
| 143 | -set -- -x -y | |
| 144 | -second_parse | |
| 145 | - | |
| 146 | -echo "✓ PASS: Multiple uses" | |
| 147 | -echo "" | |
| 148 | - | |
| 149 | -# Summary | |
| 150 | -echo "=========================================" | |
| 151 | -echo "getopts Builtin Test Suite Complete" | |
| 152 | -echo "=========================================" | |
| 153 | -echo "" | |
| 154 | -echo "Features Tested:" | |
| 155 | -echo " ✓ Basic option parsing (-a, -b, -c)" | |
| 156 | -echo " ✓ Options with required arguments (-a:)" | |
| 157 | -echo " ✓ Combined format (-ovalue)" | |
| 158 | -echo " ✓ Invalid option detection" | |
| 159 | -echo " ✓ OPTIND tracking and management" | |
| 160 | -echo " ✓ Mixed options and arguments" | |
| 161 | -echo " ✓ Multiple getopts calls in same script" | |
tests/test_john_doe.shdeleted@@ -1,25 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | - | |
| 3 | -echo "Test John Doe pattern" | |
| 4 | -str="John Doe" | |
| 5 | -echo "String: $str" | |
| 6 | - | |
| 7 | -# Test with regular space (no backslash) | |
| 8 | -if [[ $str =~ ^([A-Z][a-z]+)\ ([A-Z][a-z]+)$ ]]; then | |
| 9 | - echo "Match with backslash-space: YES" | |
| 10 | - echo "BASH_REMATCH[0]='${BASH_REMATCH[0]}'" | |
| 11 | - echo "BASH_REMATCH[1]='${BASH_REMATCH[1]}'" | |
| 12 | - echo "BASH_REMATCH[2]='${BASH_REMATCH[2]}'" | |
| 13 | -else | |
| 14 | - echo "Match with backslash-space: NO" | |
| 15 | -fi | |
| 16 | - | |
| 17 | -# Test with just space | |
| 18 | -if [[ $str =~ ^([A-Z][a-z]+) ([A-Z][a-z]+)$ ]]; then | |
| 19 | - echo "Match with plain space: YES" | |
| 20 | - echo "BASH_REMATCH[0]='${BASH_REMATCH[0]}'" | |
| 21 | - echo "BASH_REMATCH[1]='${BASH_REMATCH[1]}'" | |
| 22 | - echo "BASH_REMATCH[2]='${BASH_REMATCH[2]}'" | |
| 23 | -else | |
| 24 | - echo "Match with plain space: NO" | |
| 25 | -fi | |
tests/test_john_simple.shdeleted@@ -1,11 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | - | |
| 3 | -str="John Doe" | |
| 4 | -if [[ $str =~ ^([A-Z][a-z]+)\ ([A-Z][a-z]+)$ ]]; then | |
| 5 | - echo "PASS" | |
| 6 | - echo "0: '${BASH_REMATCH[0]}'" | |
| 7 | - echo "1: '${BASH_REMATCH[1]}'" | |
| 8 | - echo "2: '${BASH_REMATCH[2]}'" | |
| 9 | -else | |
| 10 | - echo "FAIL" | |
| 11 | -fi | |
tests/test_loops_phase14.shdeleted@@ -1,80 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Phase 14 Loop Tests - Variable Scoping & Expansion Fixes | |
| 3 | -# Run with: cat tests/test_loops_phase14.sh | ./bin/fortsh | |
| 4 | - | |
| 5 | -echo "=========================================" | |
| 6 | -echo "Phase 14 Test Suite: Loop Execution" | |
| 7 | -echo "=========================================" | |
| 8 | -echo "" | |
| 9 | - | |
| 10 | -echo "Test 1: Basic for loop" | |
| 11 | -echo "-----------------------" | |
| 12 | -for fruit in apple banana cherry | |
| 13 | -do | |
| 14 | - echo " Fruit: $fruit" | |
| 15 | -done | |
| 16 | -echo "" | |
| 17 | - | |
| 18 | -echo "Test 2: Arithmetic for loop (no space)" | |
| 19 | -echo "---------------------------------------" | |
| 20 | -for((i=0;i<5;i++)) | |
| 21 | -do | |
| 22 | - echo " Count: $i" | |
| 23 | -done | |
| 24 | - | |
| 25 | -echo | |
| 26 | -echo "Test 2b: Arithmetic for loop (with space)" | |
| 27 | -echo "------------------------------------------" | |
| 28 | -for ((i=0; i<5; i++)) | |
| 29 | -do | |
| 30 | - echo " Count: $i" | |
| 31 | -done | |
| 32 | -echo "" | |
| 33 | - | |
| 34 | -echo "Test 3: Arithmetic for loop with increment" | |
| 35 | -echo "--------------------------------------------" | |
| 36 | -for((i=2;i<=10;i+=2)) | |
| 37 | -do | |
| 38 | - echo " Even: $i" | |
| 39 | -done | |
| 40 | -echo "" | |
| 41 | - | |
| 42 | -echo "Test 4: Sequential loops (different variables)" | |
| 43 | -echo "------------------------------------------------" | |
| 44 | -for letter in A B C | |
| 45 | -do | |
| 46 | - echo " Letter: $letter" | |
| 47 | -done | |
| 48 | -for((n=1;n<=3;n++)) | |
| 49 | -do | |
| 50 | - echo " Number: $n" | |
| 51 | -done | |
| 52 | -echo "" | |
| 53 | - | |
| 54 | -echo "Test 5: Variable expansion in loops" | |
| 55 | -echo "-------------------------------------" | |
| 56 | -prefix="Item" | |
| 57 | -for x in first second third | |
| 58 | -do | |
| 59 | - echo " $prefix: $x" | |
| 60 | -done | |
| 61 | -echo "" | |
| 62 | - | |
| 63 | -echo "Test 6: Loop with command substitution" | |
| 64 | -echo "----------------------------------------" | |
| 65 | -for word in one two three | |
| 66 | -do | |
| 67 | - echo " Word has ${#word} letters" | |
| 68 | -done | |
| 69 | -echo "" | |
| 70 | - | |
| 71 | -echo "=========================================" | |
| 72 | -echo "Known Limitations:" | |
| 73 | -echo "=========================================" | |
| 74 | -echo "1. Nested loops do not work correctly" | |
| 75 | -echo "2. Loop variables persist after loop ends (POSIX correct)" | |
| 76 | -echo "" | |
| 77 | - | |
| 78 | -echo "=========================================" | |
| 79 | -echo "Test Complete" | |
| 80 | -echo "=========================================" | |
tests/test_medium_priority_builtins.shdeleted@@ -1,279 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Comprehensive test suite for medium-priority built-in commands | |
| 3 | -# Tests: getopts, trap, wait, kill, ulimit | |
| 4 | - | |
| 5 | -echo "=== Medium-Priority Built-ins Test Suite ===" | |
| 6 | -echo "" | |
| 7 | - | |
| 8 | -# ============================================ | |
| 9 | -# TEST GROUP 1: getopts | |
| 10 | -# ============================================ | |
| 11 | -echo "GROUP 1: getopts Builtin" | |
| 12 | -echo "=========================" | |
| 13 | -echo "" | |
| 14 | - | |
| 15 | -# Test 1.1: Basic getopts usage | |
| 16 | -echo "TEST 1.1: Basic getopts" | |
| 17 | -echo "----------------------" | |
| 18 | -test_getopts_basic() { | |
| 19 | - local result="" | |
| 20 | - while getopts "abc" opt; do | |
| 21 | - result="${result}$opt" | |
| 22 | - done | |
| 23 | - echo "$result" | |
| 24 | -} | |
| 25 | - | |
| 26 | -set -- -a -b -c | |
| 27 | -OPTIND=1 | |
| 28 | -output=$(test_getopts_basic) | |
| 29 | -[ "$output" = "abc" ] && echo "✓ PASS" || echo "✗ FAIL (got: $output)" | |
| 30 | -echo "" | |
| 31 | - | |
| 32 | -# Test 1.2: getopts with required arguments | |
| 33 | -echo "TEST 1.2: getopts with arguments" | |
| 34 | -echo "--------------------------------" | |
| 35 | -test_getopts_args() { | |
| 36 | - local opts="" | |
| 37 | - local args="" | |
| 38 | - while getopts "a:b:c" opt; do | |
| 39 | - case $opt in | |
| 40 | - a|b) opts="${opts}${opt}"; args="${args}${OPTARG}," ;; | |
| 41 | - c) opts="${opts}${opt}" ;; | |
| 42 | - esac | |
| 43 | - done | |
| 44 | - echo "${opts}:${args}" | |
| 45 | -} | |
| 46 | - | |
| 47 | -set -- -a val1 -b val2 -c | |
| 48 | -OPTIND=1 | |
| 49 | -output=$(test_getopts_args) | |
| 50 | -[ "$output" = "abc:val1,val2," ] && echo "✓ PASS" || echo "✗ FAIL (got: $output)" | |
| 51 | -echo "" | |
| 52 | - | |
| 53 | -# Test 1.3: getopts invalid option handling | |
| 54 | -echo "TEST 1.3: getopts invalid options" | |
| 55 | -echo "---------------------------------" | |
| 56 | -test_getopts_invalid() { | |
| 57 | - local count=0 | |
| 58 | - while getopts "ab" opt 2>/dev/null; do | |
| 59 | - [ "$opt" = "?" ] && count=$((count + 1)) | |
| 60 | - done | |
| 61 | - echo "$count" | |
| 62 | -} | |
| 63 | - | |
| 64 | -set -- -a -x -b | |
| 65 | -OPTIND=1 | |
| 66 | -output=$(test_getopts_invalid) | |
| 67 | -[ "$output" -ge "1" ] && echo "✓ PASS" || echo "✗ FAIL (got: $output)" | |
| 68 | -echo "" | |
| 69 | - | |
| 70 | -# ============================================ | |
| 71 | -# TEST GROUP 2: trap | |
| 72 | -# ============================================ | |
| 73 | -echo "GROUP 2: trap Builtin" | |
| 74 | -echo "=====================" | |
| 75 | -echo "" | |
| 76 | - | |
| 77 | -# Test 2.1: trap command syntax | |
| 78 | -echo "TEST 2.1: trap syntax acceptance" | |
| 79 | -echo "--------------------------------" | |
| 80 | -trap 'echo signal' SIGINT 2>/dev/null | |
| 81 | -status=$? | |
| 82 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 83 | -trap - SIGINT 2>/dev/null # Clean up | |
| 84 | -echo "" | |
| 85 | - | |
| 86 | -# Test 2.2: trap -p (list traps) | |
| 87 | -echo "TEST 2.2: trap -p" | |
| 88 | -echo "----------------" | |
| 89 | -trap -p >/dev/null 2>&1 | |
| 90 | -status=$? | |
| 91 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 92 | -echo "" | |
| 93 | - | |
| 94 | -# Test 2.3: trap removal | |
| 95 | -echo "TEST 2.3: trap removal" | |
| 96 | -echo "---------------------" | |
| 97 | -trap 'echo test' SIGTERM 2>/dev/null | |
| 98 | -trap - SIGTERM 2>/dev/null | |
| 99 | -status=$? | |
| 100 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 101 | -echo "" | |
| 102 | - | |
| 103 | -# ============================================ | |
| 104 | -# TEST GROUP 3: wait | |
| 105 | -# ============================================ | |
| 106 | -echo "GROUP 3: wait Builtin" | |
| 107 | -echo "=====================" | |
| 108 | -echo "" | |
| 109 | - | |
| 110 | -# Test 3.1: wait with no arguments | |
| 111 | -echo "TEST 3.1: wait (no args)" | |
| 112 | -echo "-----------------------" | |
| 113 | -wait 2>/dev/null | |
| 114 | -status=$? | |
| 115 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 116 | -echo "" | |
| 117 | - | |
| 118 | -# Test 3.2: wait with PID | |
| 119 | -echo "TEST 3.2: wait with PID" | |
| 120 | -echo "----------------------" | |
| 121 | -sleep 0.1 & | |
| 122 | -pid=$! | |
| 123 | -wait $pid 2>/dev/null | |
| 124 | -status=$? | |
| 125 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 126 | -echo "" | |
| 127 | - | |
| 128 | -# Test 3.3: wait for completed process | |
| 129 | -echo "TEST 3.3: wait for completed process" | |
| 130 | -echo "------------------------------------" | |
| 131 | -sleep 0.1 & | |
| 132 | -pid=$! | |
| 133 | -sleep 0.2 # Let it finish | |
| 134 | -wait $pid 2>/dev/null | |
| 135 | -status=$? | |
| 136 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 137 | -echo "" | |
| 138 | - | |
| 139 | -# ============================================ | |
| 140 | -# TEST GROUP 4: kill | |
| 141 | -# ============================================ | |
| 142 | -echo "GROUP 4: kill Builtin" | |
| 143 | -echo "=====================" | |
| 144 | -echo "" | |
| 145 | - | |
| 146 | -# Test 4.1: kill syntax (default signal) | |
| 147 | -echo "TEST 4.1: kill with PID" | |
| 148 | -echo "----------------------" | |
| 149 | -sleep 60 & | |
| 150 | -pid=$! | |
| 151 | -kill $pid 2>/dev/null | |
| 152 | -status=$? | |
| 153 | -wait $pid 2>/dev/null # Clean up | |
| 154 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 155 | -echo "" | |
| 156 | - | |
| 157 | -# Test 4.2: kill with signal number | |
| 158 | -echo "TEST 4.2: kill -9" | |
| 159 | -echo "----------------" | |
| 160 | -sleep 60 & | |
| 161 | -pid=$! | |
| 162 | -kill -9 $pid 2>/dev/null | |
| 163 | -status=$? | |
| 164 | -wait $pid 2>/dev/null # Clean up | |
| 165 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 166 | -echo "" | |
| 167 | - | |
| 168 | -# Test 4.3: kill with signal name | |
| 169 | -echo "TEST 4.3: kill -TERM" | |
| 170 | -echo "-------------------" | |
| 171 | -sleep 60 & | |
| 172 | -pid=$! | |
| 173 | -kill -TERM $pid 2>/dev/null | |
| 174 | -status=$? | |
| 175 | -wait $pid 2>/dev/null # Clean up | |
| 176 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 177 | -echo "" | |
| 178 | - | |
| 179 | -# Test 4.4: kill with -s option | |
| 180 | -echo "TEST 4.4: kill -s SIGKILL" | |
| 181 | -echo "------------------------" | |
| 182 | -sleep 60 & | |
| 183 | -pid=$! | |
| 184 | -kill -s SIGKILL $pid 2>/dev/null | |
| 185 | -status=$? | |
| 186 | -wait $pid 2>/dev/null # Clean up | |
| 187 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 188 | -echo "" | |
| 189 | - | |
| 190 | -# ============================================ | |
| 191 | -# TEST GROUP 5: ulimit | |
| 192 | -# ============================================ | |
| 193 | -echo "GROUP 5: ulimit Builtin" | |
| 194 | -echo "=======================" | |
| 195 | -echo "" | |
| 196 | - | |
| 197 | -# Test 5.1: ulimit with no arguments | |
| 198 | -echo "TEST 5.1: ulimit (no args)" | |
| 199 | -echo "-------------------------" | |
| 200 | -ulimit >/dev/null 2>&1 | |
| 201 | -status=$? | |
| 202 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 203 | -echo "" | |
| 204 | - | |
| 205 | -# Test 5.2: ulimit -a (show all) | |
| 206 | -echo "TEST 5.2: ulimit -a" | |
| 207 | -echo "------------------" | |
| 208 | -ulimit -a >/dev/null 2>&1 | |
| 209 | -status=$? | |
| 210 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 211 | -echo "" | |
| 212 | - | |
| 213 | -# Test 5.3: ulimit -n (show open files) | |
| 214 | -echo "TEST 5.3: ulimit -n" | |
| 215 | -echo "------------------" | |
| 216 | -ulimit -n >/dev/null 2>&1 | |
| 217 | -status=$? | |
| 218 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 219 | -echo "" | |
| 220 | - | |
| 221 | -# Test 5.4: ulimit -n value (set limit) | |
| 222 | -echo "TEST 5.4: ulimit -n 1024" | |
| 223 | -echo "-----------------------" | |
| 224 | -# This may fail if not privileged, but syntax should be accepted | |
| 225 | -current=$(ulimit -n) | |
| 226 | -ulimit -n 1024 2>/dev/null | |
| 227 | -status=$? | |
| 228 | -ulimit -n $current 2>/dev/null # Restore | |
| 229 | -[ $status -eq 0 ] && echo "✓ PASS" || echo "✗ FAIL (exit code: $status)" | |
| 230 | -echo "" | |
| 231 | - | |
| 232 | -# ============================================ | |
| 233 | -# TEST GROUP 6: type command updates | |
| 234 | -# ============================================ | |
| 235 | -echo "GROUP 6: type Command Recognition" | |
| 236 | -echo "==================================" | |
| 237 | -echo "" | |
| 238 | - | |
| 239 | -# Test that all new built-ins are recognized by type | |
| 240 | -builtins=("getopts" "trap" "wait" "kill" "ulimit") | |
| 241 | -all_passed=true | |
| 242 | - | |
| 243 | -for cmd in "${builtins[@]}"; do | |
| 244 | - output=$(type "$cmd" 2>&1) | |
| 245 | - if echo "$output" | grep -q "builtin"; then | |
| 246 | - echo "✓ type $cmd: recognized as builtin" | |
| 247 | - else | |
| 248 | - echo "✗ type $cmd: NOT recognized as builtin" | |
| 249 | - all_passed=false | |
| 250 | - fi | |
| 251 | -done | |
| 252 | - | |
| 253 | -echo "" | |
| 254 | -[ "$all_passed" = true ] && echo "✓ ALL PASS" || echo "✗ SOME FAILED" | |
| 255 | -echo "" | |
| 256 | - | |
| 257 | -# ============================================ | |
| 258 | -# SUMMARY | |
| 259 | -# ============================================ | |
| 260 | -echo "==========================================" | |
| 261 | -echo "Medium-Priority Built-ins Test Complete" | |
| 262 | -echo "==========================================" | |
| 263 | -echo "" | |
| 264 | -echo "Built-ins Tested:" | |
| 265 | -echo " ✓ getopts - command-line option parsing" | |
| 266 | -echo " ✓ trap - signal handler registration" | |
| 267 | -echo " ✓ wait - wait for background processes" | |
| 268 | -echo " ✓ kill - send signals to processes" | |
| 269 | -echo " ✓ ulimit - resource limit management" | |
| 270 | -echo "" | |
| 271 | -echo "Total Test Groups: 6" | |
| 272 | -echo "Total Test Cases: 20+" | |
| 273 | -echo "" | |
| 274 | -echo "NOTE: Some built-ins have minimal implementations" | |
| 275 | -echo " Full functionality requires additional C bindings:" | |
| 276 | -echo " - trap: requires sigaction/signal" | |
| 277 | -echo " - wait: requires waitpid tracking" | |
| 278 | -echo " - kill: requires kill() system call" | |
| 279 | -echo " - ulimit: requires getrlimit/setrlimit" | |
tests/test_parser_lists.f90deleted@@ -1,186 +0,0 @@ | ||
| 1 | -! ============================================================================== | |
| 2 | -! Test program for parser with linked list node collection | |
| 3 | -! ============================================================================== | |
| 4 | -program test_parser_lists | |
| 5 | - use ast_types | |
| 6 | - use lexer | |
| 7 | - use parser | |
| 8 | - implicit none | |
| 9 | - | |
| 10 | - call test_simple_command() | |
| 11 | - call test_for_loop_collection() | |
| 12 | - call test_nested_commands() | |
| 13 | - | |
| 14 | - print *, "" | |
| 15 | - print *, "=== All parser list tests passed! ===" | |
| 16 | - | |
| 17 | -contains | |
| 18 | - | |
| 19 | - subroutine test_simple_command() | |
| 20 | - type(lexer_t) :: lex | |
| 21 | - type(parser_t) :: pars | |
| 22 | - type(script_node_t) :: ast | |
| 23 | - character(:), allocatable :: input | |
| 24 | - | |
| 25 | - print *, "=== Test: Simple Command with Arguments ===" | |
| 26 | - | |
| 27 | - input = 'echo hello world' | |
| 28 | - print *, "Input: ", input | |
| 29 | - | |
| 30 | - ! Tokenize | |
| 31 | - call lex%init(input) | |
| 32 | - call lex%tokenize() | |
| 33 | - print *, "Tokens: ", lex%token_count | |
| 34 | - | |
| 35 | - ! Parse | |
| 36 | - call pars%init(lex%tokens, lex%token_count) | |
| 37 | - ast = pars%parse() | |
| 38 | - | |
| 39 | - print *, "AST created successfully" | |
| 40 | - if (allocated(ast%statements)) then | |
| 41 | - print *, "Number of statements: ", size(ast%statements) | |
| 42 | - | |
| 43 | - ! Check the first statement is a command | |
| 44 | - select type(stmt => ast%statements(1)) | |
| 45 | - type is (command_node_t) | |
| 46 | - print *, " First statement is a COMMAND" | |
| 47 | - if (allocated(stmt%words)) then | |
| 48 | - print *, " Number of words: ", size(stmt%words) | |
| 49 | - | |
| 50 | - ! Print each word | |
| 51 | - block | |
| 52 | - integer :: i | |
| 53 | - do i = 1, size(stmt%words) | |
| 54 | - select type(w => stmt%words(i)) | |
| 55 | - type is (word_node_t) | |
| 56 | - print *, " Word ", i, ": ", w%text | |
| 57 | - end select | |
| 58 | - end do | |
| 59 | - end block | |
| 60 | - end if | |
| 61 | - class default | |
| 62 | - print *, " First statement type: ", stmt%node_type | |
| 63 | - end select | |
| 64 | - else | |
| 65 | - print *, "No statements parsed" | |
| 66 | - end if | |
| 67 | - print *, "" | |
| 68 | - | |
| 69 | - call lex%destroy() | |
| 70 | - call pars%destroy() | |
| 71 | - end subroutine test_simple_command | |
| 72 | - | |
| 73 | - subroutine test_for_loop_collection() | |
| 74 | - type(lexer_t) :: lex | |
| 75 | - type(parser_t) :: pars | |
| 76 | - type(script_node_t) :: ast | |
| 77 | - character(:), allocatable :: input | |
| 78 | - | |
| 79 | - print *, "=== Test: For Loop Word Collection ===" | |
| 80 | - | |
| 81 | - input = 'for item in apple banana cherry' // char(10) // & | |
| 82 | - 'do' // char(10) // & | |
| 83 | - ' echo $item' // char(10) // & | |
| 84 | - 'done' | |
| 85 | - print *, "Input: for loop with 3 items" | |
| 86 | - | |
| 87 | - ! Tokenize | |
| 88 | - call lex%init(input) | |
| 89 | - call lex%tokenize() | |
| 90 | - print *, "Tokens: ", lex%token_count | |
| 91 | - | |
| 92 | - ! Parse | |
| 93 | - call pars%init(lex%tokens, lex%token_count) | |
| 94 | - ast = pars%parse() | |
| 95 | - | |
| 96 | - print *, "AST created successfully" | |
| 97 | - if (allocated(ast%statements)) then | |
| 98 | - print *, "Number of statements: ", size(ast%statements) | |
| 99 | - | |
| 100 | - ! Check if first statement is a for loop | |
| 101 | - select type(stmt => ast%statements(1)) | |
| 102 | - type is (for_node_t) | |
| 103 | - print *, " First statement is a FOR loop" | |
| 104 | - print *, " Variable: ", stmt%variable | |
| 105 | - if (allocated(stmt%word_list)) then | |
| 106 | - print *, " Number of items in word list: ", size(stmt%word_list) | |
| 107 | - | |
| 108 | - ! Print each word in the list | |
| 109 | - block | |
| 110 | - integer :: i | |
| 111 | - do i = 1, size(stmt%word_list) | |
| 112 | - select type(w => stmt%word_list(i)) | |
| 113 | - type is (word_node_t) | |
| 114 | - print *, " Item ", i, ": ", w%text | |
| 115 | - end select | |
| 116 | - end do | |
| 117 | - end block | |
| 118 | - else | |
| 119 | - print *, " No word list collected" | |
| 120 | - end if | |
| 121 | - if (allocated(stmt%body)) then | |
| 122 | - print *, " Number of body commands: ", size(stmt%body) | |
| 123 | - else | |
| 124 | - print *, " No body commands collected" | |
| 125 | - end if | |
| 126 | - class default | |
| 127 | - print *, " First statement type: ", stmt%node_type | |
| 128 | - end select | |
| 129 | - end if | |
| 130 | - print *, "" | |
| 131 | - | |
| 132 | - call lex%destroy() | |
| 133 | - call pars%destroy() | |
| 134 | - end subroutine test_for_loop_collection | |
| 135 | - | |
| 136 | - subroutine test_nested_commands() | |
| 137 | - type(lexer_t) :: lex | |
| 138 | - type(parser_t) :: pars | |
| 139 | - type(script_node_t) :: ast | |
| 140 | - character(:), allocatable :: input | |
| 141 | - | |
| 142 | - print *, "=== Test: Multiple Commands ===" | |
| 143 | - | |
| 144 | - input = 'echo first' // char(10) // & | |
| 145 | - 'echo second' // char(10) // & | |
| 146 | - 'echo third' | |
| 147 | - print *, "Input: three echo commands" | |
| 148 | - | |
| 149 | - ! Tokenize | |
| 150 | - call lex%init(input) | |
| 151 | - call lex%tokenize() | |
| 152 | - print *, "Tokens: ", lex%token_count | |
| 153 | - | |
| 154 | - ! Parse | |
| 155 | - call pars%init(lex%tokens, lex%token_count) | |
| 156 | - ast = pars%parse() | |
| 157 | - | |
| 158 | - print *, "AST created successfully" | |
| 159 | - if (allocated(ast%statements)) then | |
| 160 | - print *, "Number of statements: ", size(ast%statements) | |
| 161 | - | |
| 162 | - ! Check each statement | |
| 163 | - block | |
| 164 | - integer :: i | |
| 165 | - do i = 1, size(ast%statements) | |
| 166 | - select type(stmt => ast%statements(i)) | |
| 167 | - type is (command_node_t) | |
| 168 | - print *, " Statement ", i, " is a COMMAND" | |
| 169 | - if (allocated(stmt%words)) then | |
| 170 | - print *, " Words: ", size(stmt%words) | |
| 171 | - end if | |
| 172 | - class default | |
| 173 | - print *, " Statement ", i, " type: ", stmt%node_type | |
| 174 | - end select | |
| 175 | - end do | |
| 176 | - end block | |
| 177 | - else | |
| 178 | - print *, "No statements parsed" | |
| 179 | - end if | |
| 180 | - print *, "" | |
| 181 | - | |
| 182 | - call lex%destroy() | |
| 183 | - call pars%destroy() | |
| 184 | - end subroutine test_nested_commands | |
| 185 | - | |
| 186 | -end program test_parser_lists | |
tests/test_parser_simple.f90deleted@@ -1,152 +0,0 @@ | ||
| 1 | -! ============================================================================== | |
| 2 | -! Simple parser test | |
| 3 | -! ============================================================================== | |
| 4 | -program test_parser_simple | |
| 5 | - use ast_types | |
| 6 | - use lexer | |
| 7 | - use parser | |
| 8 | - implicit none | |
| 9 | - | |
| 10 | - call test_simple_command() | |
| 11 | - call test_for_loop() | |
| 12 | - call test_nested_structure() | |
| 13 | - | |
| 14 | -contains | |
| 15 | - | |
| 16 | - subroutine test_simple_command() | |
| 17 | - type(lexer_t) :: lex | |
| 18 | - type(parser_t) :: pars | |
| 19 | - type(script_node_t) :: ast | |
| 20 | - character(:), allocatable :: input | |
| 21 | - | |
| 22 | - print *, "=== Test: Simple Command ===" | |
| 23 | - | |
| 24 | - input = 'echo "Hello World"' | |
| 25 | - print *, "Input: ", input | |
| 26 | - | |
| 27 | - ! Tokenize | |
| 28 | - call lex%init(input) | |
| 29 | - call lex%tokenize() | |
| 30 | - print *, "Tokens: ", lex%token_count | |
| 31 | - | |
| 32 | - ! Parse | |
| 33 | - call pars%init(lex%tokens, lex%token_count) | |
| 34 | - ast = pars%parse() | |
| 35 | - | |
| 36 | - print *, "AST created successfully" | |
| 37 | - if (allocated(ast%statements)) then | |
| 38 | - print *, "Number of statements: ", size(ast%statements) | |
| 39 | - end if | |
| 40 | - print *, "" | |
| 41 | - | |
| 42 | - call lex%destroy() | |
| 43 | - call pars%destroy() | |
| 44 | - end subroutine test_simple_command | |
| 45 | - | |
| 46 | - subroutine test_for_loop() | |
| 47 | - type(lexer_t) :: lex | |
| 48 | - type(parser_t) :: pars | |
| 49 | - type(script_node_t) :: ast | |
| 50 | - character(:), allocatable :: input | |
| 51 | - | |
| 52 | - print *, "=== Test: For Loop ===" | |
| 53 | - | |
| 54 | - input = 'for i in 1 2 3' // char(10) // & | |
| 55 | - 'do' // char(10) // & | |
| 56 | - ' echo $i' // char(10) // & | |
| 57 | - 'done' | |
| 58 | - print *, "Input: for loop with echo" | |
| 59 | - | |
| 60 | - ! Tokenize | |
| 61 | - call lex%init(input) | |
| 62 | - call lex%tokenize() | |
| 63 | - print *, "Tokens: ", lex%token_count | |
| 64 | - | |
| 65 | - ! Parse | |
| 66 | - call pars%init(lex%tokens, lex%token_count) | |
| 67 | - ast = pars%parse() | |
| 68 | - | |
| 69 | - print *, "AST created successfully" | |
| 70 | - if (allocated(ast%statements)) then | |
| 71 | - print *, "Number of statements: ", size(ast%statements) | |
| 72 | - | |
| 73 | - ! Check if first statement is a for loop | |
| 74 | - select type(stmt => ast%statements(1)) | |
| 75 | - type is (for_node_t) | |
| 76 | - print *, "First statement is a FOR loop" | |
| 77 | - print *, "Variable: ", stmt%variable | |
| 78 | - if (allocated(stmt%word_list)) then | |
| 79 | - print *, "Number of items: ", size(stmt%word_list) | |
| 80 | - end if | |
| 81 | - if (allocated(stmt%body)) then | |
| 82 | - print *, "Number of body commands: ", size(stmt%body) | |
| 83 | - end if | |
| 84 | - class default | |
| 85 | - print *, "First statement type: ", stmt%node_type | |
| 86 | - end select | |
| 87 | - end if | |
| 88 | - print *, "" | |
| 89 | - | |
| 90 | - call lex%destroy() | |
| 91 | - call pars%destroy() | |
| 92 | - end subroutine test_for_loop | |
| 93 | - | |
| 94 | - subroutine test_nested_structure() | |
| 95 | - type(lexer_t) :: lex | |
| 96 | - type(parser_t) :: pars | |
| 97 | - type(script_node_t) :: ast | |
| 98 | - character(:), allocatable :: input | |
| 99 | - | |
| 100 | - print *, "=== Test: Nested Structure ===" | |
| 101 | - | |
| 102 | - input = 'for i in 1 2' // char(10) // & | |
| 103 | - 'do' // char(10) // & | |
| 104 | - ' for j in a b' // char(10) // & | |
| 105 | - ' do' // char(10) // & | |
| 106 | - ' echo "$i $j"' // char(10) // & | |
| 107 | - ' done' // char(10) // & | |
| 108 | - 'done' | |
| 109 | - print *, "Input: nested for loops" | |
| 110 | - | |
| 111 | - ! Tokenize | |
| 112 | - call lex%init(input) | |
| 113 | - call lex%tokenize() | |
| 114 | - print *, "Tokens: ", lex%token_count | |
| 115 | - | |
| 116 | - ! Parse | |
| 117 | - call pars%init(lex%tokens, lex%token_count) | |
| 118 | - ast = pars%parse() | |
| 119 | - | |
| 120 | - print *, "AST created successfully" | |
| 121 | - if (allocated(ast%statements)) then | |
| 122 | - print *, "Number of statements: ", size(ast%statements) | |
| 123 | - | |
| 124 | - ! Check structure | |
| 125 | - select type(stmt => ast%statements(1)) | |
| 126 | - type is (for_node_t) | |
| 127 | - print *, "Outer loop variable: ", stmt%variable | |
| 128 | - if (allocated(stmt%body)) then | |
| 129 | - print *, "Outer body commands: ", size(stmt%body) | |
| 130 | - | |
| 131 | - ! Check if inner loop exists | |
| 132 | - select type(inner => stmt%body(1)) | |
| 133 | - type is (for_node_t) | |
| 134 | - print *, "Inner loop variable: ", inner%variable | |
| 135 | - if (allocated(inner%body)) then | |
| 136 | - print *, "Inner body commands: ", size(inner%body) | |
| 137 | - end if | |
| 138 | - class default | |
| 139 | - print *, "Inner statement type: ", inner%node_type | |
| 140 | - end select | |
| 141 | - end if | |
| 142 | - class default | |
| 143 | - print *, "First statement type: ", stmt%node_type | |
| 144 | - end select | |
| 145 | - end if | |
| 146 | - print *, "" | |
| 147 | - | |
| 148 | - call lex%destroy() | |
| 149 | - call pars%destroy() | |
| 150 | - end subroutine test_nested_structure | |
| 151 | - | |
| 152 | -end program test_parser_simple | |
tests/test_regex_matching.shdeleted@@ -1,248 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Comprehensive test suite for regex matching with =~ operator | |
| 3 | - | |
| 4 | -echo "=== Regex Matching Test Suite ===" | |
| 5 | -echo "" | |
| 6 | - | |
| 7 | -# Test 1: Basic regex match - digits | |
| 8 | -echo "TEST 1: Basic Digit Pattern" | |
| 9 | -echo "============================" | |
| 10 | -str="12345" | |
| 11 | -if [[ $str =~ ^[0-9]+$ ]]; then | |
| 12 | - echo "✓ PASS: '$str' matches digit pattern" | |
| 13 | -else | |
| 14 | - echo "✗ FAIL: '$str' should match digit pattern" | |
| 15 | -fi | |
| 16 | -echo "" | |
| 17 | - | |
| 18 | -# Test 2: Regex match with letters | |
| 19 | -echo "TEST 2: Letter Pattern" | |
| 20 | -echo "======================" | |
| 21 | -str="hello" | |
| 22 | -if [[ $str =~ ^[a-z]+$ ]]; then | |
| 23 | - echo "✓ PASS: '$str' matches lowercase letters" | |
| 24 | -else | |
| 25 | - echo "✗ FAIL: '$str' should match lowercase letters" | |
| 26 | -fi | |
| 27 | -echo "" | |
| 28 | - | |
| 29 | -# Test 3: Regex no match | |
| 30 | -echo "TEST 3: No Match" | |
| 31 | -echo "================" | |
| 32 | -str="hello123" | |
| 33 | -if [[ $str =~ ^[a-z]+$ ]]; then | |
| 34 | - echo "✗ FAIL: '$str' should NOT match letters-only pattern" | |
| 35 | -else | |
| 36 | - echo "✓ PASS: '$str' correctly does not match" | |
| 37 | -fi | |
| 38 | -echo "" | |
| 39 | - | |
| 40 | -# Test 4: Email pattern | |
| 41 | -echo "TEST 4: Email Pattern" | |
| 42 | -echo "=====================" | |
| 43 | -email="user@example.com" | |
| 44 | -if [[ $email =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]]; then | |
| 45 | - echo "✓ PASS: '$email' matches email pattern" | |
| 46 | -else | |
| 47 | - echo "✗ FAIL: '$email' should match email pattern" | |
| 48 | -fi | |
| 49 | -echo "" | |
| 50 | - | |
| 51 | -# Test 5: IP address pattern | |
| 52 | -echo "TEST 5: IP Address Pattern" | |
| 53 | -echo "==========================" | |
| 54 | -ip="192.168.1.1" | |
| 55 | -if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then | |
| 56 | - echo "✓ PASS: '$ip' matches IP pattern" | |
| 57 | -else | |
| 58 | - echo "✗ FAIL: '$ip' should match IP pattern" | |
| 59 | -fi | |
| 60 | -echo "" | |
| 61 | - | |
| 62 | -# Test 6: URL pattern | |
| 63 | -echo "TEST 6: URL Pattern" | |
| 64 | -echo "===================" | |
| 65 | -url="https://example.com/path" | |
| 66 | -if [[ $url =~ ^https?:// ]]; then | |
| 67 | - echo "✓ PASS: '$url' starts with http(s)://" | |
| 68 | -else | |
| 69 | - echo "✗ FAIL: '$url' should match URL pattern" | |
| 70 | -fi | |
| 71 | -echo "" | |
| 72 | - | |
| 73 | -# Test 7: Word boundary | |
| 74 | -echo "TEST 7: Word Pattern" | |
| 75 | -echo "====================" | |
| 76 | -text="The quick brown fox" | |
| 77 | -if [[ $text =~ quick ]]; then | |
| 78 | - echo "✓ PASS: Found 'quick' in text" | |
| 79 | -else | |
| 80 | - echo "✗ FAIL: Should find 'quick' in text" | |
| 81 | -fi | |
| 82 | -echo "" | |
| 83 | - | |
| 84 | -# Test 8: Case sensitive matching | |
| 85 | -echo "TEST 8: Case Sensitive" | |
| 86 | -echo "======================" | |
| 87 | -str="Hello" | |
| 88 | -if [[ $str =~ ^hello$ ]]; then | |
| 89 | - echo "✗ FAIL: Should be case sensitive (Hello != hello)" | |
| 90 | -else | |
| 91 | - echo "✓ PASS: Correctly case sensitive" | |
| 92 | -fi | |
| 93 | -echo "" | |
| 94 | - | |
| 95 | -# Test 9: Anchor patterns | |
| 96 | -echo "TEST 9: Anchor Patterns" | |
| 97 | -echo "=======================" | |
| 98 | -str="test123" | |
| 99 | -if [[ $str =~ ^test ]]; then | |
| 100 | - echo "✓ PASS (1/3): Matches start anchor ^test" | |
| 101 | -else | |
| 102 | - echo "✗ FAIL (1/3): Should match start anchor" | |
| 103 | -fi | |
| 104 | - | |
| 105 | -if [[ $str =~ 123$ ]]; then | |
| 106 | - echo "✓ PASS (2/3): Matches end anchor 123$" | |
| 107 | -else | |
| 108 | - echo "✗ FAIL (2/3): Should match end anchor" | |
| 109 | -fi | |
| 110 | - | |
| 111 | -if [[ $str =~ ^test123$ ]]; then | |
| 112 | - echo "✓ PASS (3/3): Matches full string ^test123$" | |
| 113 | -else | |
| 114 | - echo "✗ FAIL (3/3): Should match full string" | |
| 115 | -fi | |
| 116 | -echo "" | |
| 117 | - | |
| 118 | -# Test 10: Character classes | |
| 119 | -echo "TEST 10: Character Classes" | |
| 120 | -echo "==========================" | |
| 121 | -str1="abc123" | |
| 122 | -str2="xyz789" | |
| 123 | - | |
| 124 | -if [[ $str1 =~ [0-9]+ ]]; then | |
| 125 | - echo "✓ PASS (1/2): '$str1' contains digits" | |
| 126 | -else | |
| 127 | - echo "✗ FAIL (1/2): Should contain digits" | |
| 128 | -fi | |
| 129 | - | |
| 130 | -if [[ $str2 =~ [a-z]+ ]]; then | |
| 131 | - echo "✓ PASS (2/2): '$str2' contains letters" | |
| 132 | -else | |
| 133 | - echo "✗ FAIL (2/2): Should contain letters" | |
| 134 | -fi | |
| 135 | -echo "" | |
| 136 | - | |
| 137 | -# Test 11: Repetition quantifiers | |
| 138 | -echo "TEST 11: Repetition Quantifiers" | |
| 139 | -echo "================================" | |
| 140 | -str="aaaa" | |
| 141 | -if [[ $str =~ ^a{4}$ ]]; then | |
| 142 | - echo "✓ PASS (1/4): Matches exactly 4 a's" | |
| 143 | -else | |
| 144 | - echo "✗ FAIL (1/4): Should match exactly 4 a's" | |
| 145 | -fi | |
| 146 | - | |
| 147 | -str="aaa" | |
| 148 | -if [[ $str =~ ^a{2,5}$ ]]; then | |
| 149 | - echo "✓ PASS (2/4): Matches 2-5 a's" | |
| 150 | -else | |
| 151 | - echo "✗ FAIL (2/4): Should match 2-5 a's" | |
| 152 | -fi | |
| 153 | - | |
| 154 | -str="aaa" | |
| 155 | -if [[ $str =~ ^a+$ ]]; then | |
| 156 | - echo "✓ PASS (3/4): Matches a+ (one or more)" | |
| 157 | -else | |
| 158 | - echo "✗ FAIL (3/4): Should match a+" | |
| 159 | -fi | |
| 160 | - | |
| 161 | -str="" | |
| 162 | -if [[ $str =~ ^a*$ ]]; then | |
| 163 | - echo "✓ PASS (4/4): Matches a* (zero or more)" | |
| 164 | -else | |
| 165 | - echo "✗ FAIL (4/4): Should match a*" | |
| 166 | -fi | |
| 167 | -echo "" | |
| 168 | - | |
| 169 | -# Test 12: Alternation | |
| 170 | -echo "TEST 12: Alternation (OR)" | |
| 171 | -echo "=========================" | |
| 172 | -str="cat" | |
| 173 | -if [[ $str =~ ^(cat|dog)$ ]]; then | |
| 174 | - echo "✓ PASS (1/2): Matches 'cat' or 'dog'" | |
| 175 | -else | |
| 176 | - echo "✗ FAIL (1/2): Should match alternation" | |
| 177 | -fi | |
| 178 | - | |
| 179 | -str="dog" | |
| 180 | -if [[ $str =~ ^(cat|dog)$ ]]; then | |
| 181 | - echo "✓ PASS (2/2): Matches 'cat' or 'dog'" | |
| 182 | -else | |
| 183 | - echo "✗ FAIL (2/2): Should match alternation" | |
| 184 | -fi | |
| 185 | -echo "" | |
| 186 | - | |
| 187 | -# Test 13: Optional character | |
| 188 | -echo "TEST 13: Optional Character" | |
| 189 | -echo "===========================" | |
| 190 | -str1="color" | |
| 191 | -str2="colour" | |
| 192 | -if [[ $str1 =~ ^colou?r$ ]] && [[ $str2 =~ ^colou?r$ ]]; then | |
| 193 | - echo "✓ PASS: Both 'color' and 'colour' match colou?r" | |
| 194 | -else | |
| 195 | - echo "✗ FAIL: Should match optional 'u'" | |
| 196 | -fi | |
| 197 | -echo "" | |
| 198 | - | |
| 199 | -# Test 14: Empty string | |
| 200 | -echo "TEST 14: Empty String" | |
| 201 | -echo "=====================" | |
| 202 | -str="" | |
| 203 | -if [[ $str =~ ^$ ]]; then | |
| 204 | - echo "✓ PASS: Empty string matches ^$" | |
| 205 | -else | |
| 206 | - echo "✗ FAIL: Empty string should match ^$" | |
| 207 | -fi | |
| 208 | -echo "" | |
| 209 | - | |
| 210 | -# Test 15: Special characters (escaped) | |
| 211 | -echo "TEST 15: Special Characters" | |
| 212 | -echo "===========================" | |
| 213 | -str="file.txt" | |
| 214 | -if [[ $str =~ \.txt$ ]]; then | |
| 215 | - echo "✓ PASS: Matches escaped dot \\.txt" | |
| 216 | -else | |
| 217 | - echo "✗ FAIL: Should match \\.txt" | |
| 218 | -fi | |
| 219 | -echo "" | |
| 220 | - | |
| 221 | -# Test 16: Negation with ! | |
| 222 | -echo "TEST 16: Negation with !" | |
| 223 | -echo "========================" | |
| 224 | -str="abc" | |
| 225 | -if [[ ! $str =~ ^[0-9]+$ ]]; then | |
| 226 | - echo "✓ PASS: Correctly negated - not digits" | |
| 227 | -else | |
| 228 | - echo "✗ FAIL: Should not match digits" | |
| 229 | -fi | |
| 230 | -echo "" | |
| 231 | - | |
| 232 | -# Summary | |
| 233 | -echo "=========================================" | |
| 234 | -echo "Regex Matching Test Suite Complete" | |
| 235 | -echo "=========================================" | |
| 236 | -echo "" | |
| 237 | -echo "Features Tested:" | |
| 238 | -echo " ✓ Basic patterns (digits, letters)" | |
| 239 | -echo " ✓ Complex patterns (email, IP, URL)" | |
| 240 | -echo " ✓ Anchors (^, $)" | |
| 241 | -echo " ✓ Character classes ([a-z], [0-9])" | |
| 242 | -echo " ✓ Quantifiers (+, *, ?, {n}, {m,n})" | |
| 243 | -echo " ✓ Alternation (|)" | |
| 244 | -echo " ✓ Case sensitivity" | |
| 245 | -echo " ✓ Special characters (escaping)" | |
| 246 | -echo " ✓ Negation with !" | |
| 247 | -echo "" | |
| 248 | -echo "Note: BASH_REMATCH array capture groups not yet implemented" | |
tests/test_rematch_debug.shdeleted@@ -1,17 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | - | |
| 3 | -echo "Debug BASH_REMATCH Test" | |
| 4 | -str="abc123" | |
| 5 | -echo "String: $str" | |
| 6 | - | |
| 7 | -if [[ $str =~ ^([a-z]+)([0-9]+)$ ]]; then | |
| 8 | - echo "Match succeeded" | |
| 9 | - echo "Immediately: BASH_REMATCH[0]='${BASH_REMATCH[0]}'" | |
| 10 | - echo "Immediately: BASH_REMATCH[1]='${BASH_REMATCH[1]}'" | |
| 11 | - echo "Immediately: BASH_REMATCH[2]='${BASH_REMATCH[2]}'" | |
| 12 | -else | |
| 13 | - echo "Match failed" | |
| 14 | -fi | |
| 15 | - | |
| 16 | -echo "After if: BASH_REMATCH[0]='${BASH_REMATCH[0]}'" | |
| 17 | -echo "After if: BASH_REMATCH[1]='${BASH_REMATCH[1]}'" | |
tests/test_runner.f90deleted@@ -1,360 +0,0 @@ | ||
| 1 | -! ============================================================================== | |
| 2 | -! Module: test_runner | |
| 3 | -! Purpose: Comprehensive test suite for Fortran Shell (fortsh) | |
| 4 | -! ============================================================================== | |
| 5 | -program test_runner | |
| 6 | - use shell_types | |
| 7 | - use parser | |
| 8 | - use glob | |
| 9 | - use variables | |
| 10 | - use control_flow | |
| 11 | - use job_control | |
| 12 | - use aliases | |
| 13 | - use iso_fortran_env, only: output_unit, error_unit | |
| 14 | - implicit none | |
| 15 | - | |
| 16 | - integer :: total_tests = 0 | |
| 17 | - integer :: passed_tests = 0 | |
| 18 | - integer :: failed_tests = 0 | |
| 19 | - | |
| 20 | - write(output_unit, '(a)') '================================' | |
| 21 | - write(output_unit, '(a)') 'Fortran Shell (fortsh) Test Suite' | |
| 22 | - write(output_unit, '(a)') '================================' | |
| 23 | - write(output_unit, '(a)') '' | |
| 24 | - | |
| 25 | - ! Run all test suites | |
| 26 | - call test_parser_module() | |
| 27 | - call test_glob_module() | |
| 28 | - call test_variables_module() | |
| 29 | - call test_control_flow_module() | |
| 30 | - call test_aliases_module() | |
| 31 | - call test_error_handling() | |
| 32 | - | |
| 33 | - ! Print summary | |
| 34 | - write(output_unit, '(a)') '' | |
| 35 | - write(output_unit, '(a)') '================================' | |
| 36 | - write(output_unit, '(a)') 'TEST SUMMARY' | |
| 37 | - write(output_unit, '(a)') '================================' | |
| 38 | - write(output_unit, '(a,i0)') 'Total tests: ', total_tests | |
| 39 | - write(output_unit, '(a,i0)') 'Passed tests: ', passed_tests | |
| 40 | - write(output_unit, '(a,i0)') 'Failed tests: ', failed_tests | |
| 41 | - | |
| 42 | - if (failed_tests == 0) then | |
| 43 | - write(output_unit, '(a)') '✅ ALL TESTS PASSED!' | |
| 44 | - else | |
| 45 | - write(output_unit, '(a)') '❌ Some tests failed' | |
| 46 | - end if | |
| 47 | - | |
| 48 | - if (failed_tests > 0) then | |
| 49 | - call exit(1) | |
| 50 | - end if | |
| 51 | - | |
| 52 | -contains | |
| 53 | - | |
| 54 | - subroutine assert_true(condition, test_name) | |
| 55 | - logical, intent(in) :: condition | |
| 56 | - character(len=*), intent(in) :: test_name | |
| 57 | - | |
| 58 | - total_tests = total_tests + 1 | |
| 59 | - | |
| 60 | - if (condition) then | |
| 61 | - write(output_unit, '(a,a)') '✓ ', test_name | |
| 62 | - passed_tests = passed_tests + 1 | |
| 63 | - else | |
| 64 | - write(output_unit, '(a,a)') '✗ ', test_name | |
| 65 | - failed_tests = failed_tests + 1 | |
| 66 | - end if | |
| 67 | - end subroutine | |
| 68 | - | |
| 69 | - subroutine assert_equal_str(actual, expected, test_name) | |
| 70 | - character(len=*), intent(in) :: actual, expected, test_name | |
| 71 | - call assert_true(trim(actual) == trim(expected), test_name) | |
| 72 | - end subroutine | |
| 73 | - | |
| 74 | - subroutine assert_equal_int(actual, expected, test_name) | |
| 75 | - integer, intent(in) :: actual, expected | |
| 76 | - character(len=*), intent(in) :: test_name | |
| 77 | - call assert_true(actual == expected, test_name) | |
| 78 | - end subroutine | |
| 79 | - | |
| 80 | - subroutine test_parser_module() | |
| 81 | - write(output_unit, '(a)') 'Testing Parser Module:' | |
| 82 | - write(output_unit, '(a)') '=====================' | |
| 83 | - | |
| 84 | - ! Test tokenization | |
| 85 | - call test_tokenization() | |
| 86 | - | |
| 87 | - ! Test pipeline parsing | |
| 88 | - call test_pipeline_parsing() | |
| 89 | - | |
| 90 | - ! Test redirection parsing | |
| 91 | - call test_redirection_parsing() | |
| 92 | - | |
| 93 | - write(output_unit, '(a)') '' | |
| 94 | - end subroutine | |
| 95 | - | |
| 96 | - subroutine test_tokenization() | |
| 97 | - character(len=:), allocatable :: tokens(:) | |
| 98 | - integer :: num_tokens | |
| 99 | - | |
| 100 | - ! Test basic tokenization | |
| 101 | - call tokenize_with_substitution('echo hello world', tokens, num_tokens) | |
| 102 | - call assert_equal_int(num_tokens, 3, 'Basic tokenization count') | |
| 103 | - if (num_tokens >= 3) then | |
| 104 | - call assert_equal_str(tokens(1), 'echo', 'Token 1 correct') | |
| 105 | - call assert_equal_str(tokens(2), 'hello', 'Token 2 correct') | |
| 106 | - call assert_equal_str(tokens(3), 'world', 'Token 3 correct') | |
| 107 | - end if | |
| 108 | - if (allocated(tokens)) deallocate(tokens) | |
| 109 | - | |
| 110 | - ! Test empty input | |
| 111 | - call tokenize_with_substitution('', tokens, num_tokens) | |
| 112 | - call assert_equal_int(num_tokens, 0, 'Empty input tokenization') | |
| 113 | - if (allocated(tokens)) deallocate(tokens) | |
| 114 | - | |
| 115 | - ! Test whitespace handling | |
| 116 | - call tokenize_with_substitution(' echo test ', tokens, num_tokens) | |
| 117 | - call assert_equal_int(num_tokens, 2, 'Whitespace handling count') | |
| 118 | - if (num_tokens >= 2) then | |
| 119 | - call assert_equal_str(tokens(1), 'echo', 'Whitespace token 1') | |
| 120 | - call assert_equal_str(tokens(2), 'test', 'Whitespace token 2') | |
| 121 | - end if | |
| 122 | - if (allocated(tokens)) deallocate(tokens) | |
| 123 | - end subroutine | |
| 124 | - | |
| 125 | - subroutine test_pipeline_parsing() | |
| 126 | - type(pipeline_t) :: pipeline | |
| 127 | - | |
| 128 | - ! Test single command | |
| 129 | - call parse_pipeline('echo hello', pipeline) | |
| 130 | - call assert_equal_int(pipeline%num_commands, 1, 'Single command pipeline') | |
| 131 | - if (pipeline%num_commands >= 1) then | |
| 132 | - call assert_equal_int(pipeline%commands(1)%separator, SEP_NONE, 'Single command separator') | |
| 133 | - end if | |
| 134 | - | |
| 135 | - ! Cleanup | |
| 136 | - if (allocated(pipeline%commands)) then | |
| 137 | - if (allocated(pipeline%commands(1)%tokens)) deallocate(pipeline%commands(1)%tokens) | |
| 138 | - deallocate(pipeline%commands) | |
| 139 | - end if | |
| 140 | - | |
| 141 | - ! Test pipe command | |
| 142 | - call parse_pipeline('ls | grep test', pipeline) | |
| 143 | - call assert_equal_int(pipeline%num_commands, 2, 'Pipe command pipeline') | |
| 144 | - if (pipeline%num_commands >= 2) then | |
| 145 | - call assert_equal_int(pipeline%commands(1)%separator, SEP_PIPE, 'Pipe separator') | |
| 146 | - end if | |
| 147 | - | |
| 148 | - ! Cleanup | |
| 149 | - if (allocated(pipeline%commands)) then | |
| 150 | - do i = 1, pipeline%num_commands | |
| 151 | - if (allocated(pipeline%commands(i)%tokens)) deallocate(pipeline%commands(i)%tokens) | |
| 152 | - if (allocated(pipeline%commands(i)%input_file)) deallocate(pipeline%commands(i)%input_file) | |
| 153 | - if (allocated(pipeline%commands(i)%output_file)) deallocate(pipeline%commands(i)%output_file) | |
| 154 | - if (allocated(pipeline%commands(i)%error_file)) deallocate(pipeline%commands(i)%error_file) | |
| 155 | - if (allocated(pipeline%commands(i)%heredoc_delimiter)) deallocate(pipeline%commands(i)%heredoc_delimiter) | |
| 156 | - if (allocated(pipeline%commands(i)%heredoc_content)) deallocate(pipeline%commands(i)%heredoc_content) | |
| 157 | - if (allocated(pipeline%commands(i)%here_string)) deallocate(pipeline%commands(i)%here_string) | |
| 158 | - end do | |
| 159 | - deallocate(pipeline%commands) | |
| 160 | - end if | |
| 161 | - end subroutine | |
| 162 | - | |
| 163 | - subroutine test_redirection_parsing() | |
| 164 | - type(command_t) :: cmd | |
| 165 | - character(len=256) :: input | |
| 166 | - | |
| 167 | - input = 'echo hello > output.txt' | |
| 168 | - call parse_simple_command(input, cmd) | |
| 169 | - | |
| 170 | - call assert_true(allocated(cmd%output_file), 'Output redirection detected') | |
| 171 | - if (allocated(cmd%output_file)) then | |
| 172 | - call assert_equal_str(cmd%output_file, 'output.txt', 'Output file correct') | |
| 173 | - end if | |
| 174 | - call assert_true(.not. cmd%append_output, 'Append flag correct') | |
| 175 | - | |
| 176 | - ! Cleanup | |
| 177 | - if (allocated(cmd%tokens)) deallocate(cmd%tokens) | |
| 178 | - if (allocated(cmd%output_file)) deallocate(cmd%output_file) | |
| 179 | - end subroutine | |
| 180 | - | |
| 181 | - subroutine test_glob_module() | |
| 182 | - write(output_unit, '(a)') 'Testing Glob Module:' | |
| 183 | - write(output_unit, '(a)') '===================' | |
| 184 | - | |
| 185 | - call test_pattern_matching() | |
| 186 | - call test_glob_expansion() | |
| 187 | - | |
| 188 | - write(output_unit, '(a)') '' | |
| 189 | - end subroutine | |
| 190 | - | |
| 191 | - subroutine test_pattern_matching() | |
| 192 | - ! Test wildcard patterns | |
| 193 | - call assert_true(pattern_matches('*.txt', 'file.txt'), 'Simple wildcard match') | |
| 194 | - call assert_true(.not. pattern_matches('*.txt', 'file.log'), 'Simple wildcard no match') | |
| 195 | - | |
| 196 | - ! Test single character wildcard | |
| 197 | - call assert_true(pattern_matches('file?.txt', 'file1.txt'), 'Single char wildcard match') | |
| 198 | - call assert_true(.not. pattern_matches('file?.txt', 'file10.txt'), 'Single char wildcard no match') | |
| 199 | - | |
| 200 | - ! Test character classes | |
| 201 | - call assert_true(pattern_matches('[abc]*', 'apple'), 'Character class match') | |
| 202 | - call assert_true(.not. pattern_matches('[abc]*', 'orange'), 'Character class no match') | |
| 203 | - | |
| 204 | - ! Test character ranges | |
| 205 | - call assert_true(pattern_matches('[a-z]*', 'hello'), 'Character range match') | |
| 206 | - call assert_true(.not. pattern_matches('[a-z]*', '123'), 'Character range no match') | |
| 207 | - | |
| 208 | - ! Test negation | |
| 209 | - call assert_true(pattern_matches('[!0-9]*', 'hello'), 'Negation match') | |
| 210 | - call assert_true(.not. pattern_matches('[!0-9]*', '123'), 'Negation no match') | |
| 211 | - end subroutine | |
| 212 | - | |
| 213 | - subroutine test_glob_expansion() | |
| 214 | - character(len=MAX_TOKEN_LEN), allocatable :: expanded_tokens(:) | |
| 215 | - character(len=MAX_TOKEN_LEN) :: input_tokens(2) | |
| 216 | - integer :: expanded_count | |
| 217 | - | |
| 218 | - input_tokens(1) = '*.txt' | |
| 219 | - input_tokens(2) = 'literal' | |
| 220 | - | |
| 221 | - call expand_glob_patterns(input_tokens, 2, expanded_tokens, expanded_count) | |
| 222 | - call assert_true(expanded_count >= 2, 'Glob expansion returns results') | |
| 223 | - call assert_true(allocated(expanded_tokens), 'Expanded tokens allocated') | |
| 224 | - | |
| 225 | - if (allocated(expanded_tokens)) deallocate(expanded_tokens) | |
| 226 | - end subroutine | |
| 227 | - | |
| 228 | - subroutine test_variables_module() | |
| 229 | - write(output_unit, '(a)') 'Testing Variables Module:' | |
| 230 | - write(output_unit, '(a)') '========================' | |
| 231 | - | |
| 232 | - call test_variable_assignment() | |
| 233 | - call test_variable_expansion() | |
| 234 | - | |
| 235 | - write(output_unit, '(a)') '' | |
| 236 | - end subroutine | |
| 237 | - | |
| 238 | - subroutine test_variable_assignment() | |
| 239 | - call assert_true(is_assignment('VAR=value'), 'Simple assignment detection') | |
| 240 | - call assert_true(is_assignment('PATH=/usr/bin'), 'Path assignment detection') | |
| 241 | - call assert_true(.not. is_assignment('echo hello'), 'Non-assignment detection') | |
| 242 | - call assert_true(.not. is_assignment('test='), 'Empty value assignment') | |
| 243 | - end subroutine | |
| 244 | - | |
| 245 | - subroutine test_variable_expansion() | |
| 246 | - type(shell_state_t) :: shell | |
| 247 | - character(len=:), allocatable :: expanded | |
| 248 | - | |
| 249 | - ! Initialize shell | |
| 250 | - shell%num_variables = 1 | |
| 251 | - shell%variables(1)%name = 'TEST' | |
| 252 | - shell%variables(1)%value = 'hello' | |
| 253 | - | |
| 254 | - call expand_variables('$TEST', expanded, shell) | |
| 255 | - call assert_equal_str(expanded, 'hello', 'Simple variable expansion') | |
| 256 | - | |
| 257 | - call expand_variables('${TEST}world', expanded, shell) | |
| 258 | - call assert_equal_str(expanded, 'helloworld', 'Braced variable expansion') | |
| 259 | - | |
| 260 | - call expand_variables('$NONEXISTENT', expanded, shell) | |
| 261 | - call assert_equal_str(expanded, '', 'Non-existent variable expansion') | |
| 262 | - end subroutine | |
| 263 | - | |
| 264 | - subroutine test_control_flow_module() | |
| 265 | - write(output_unit, '(a)') 'Testing Control Flow Module:' | |
| 266 | - write(output_unit, '(a)') '===========================' | |
| 267 | - | |
| 268 | - call test_control_flow_keywords() | |
| 269 | - call test_condition_evaluation() | |
| 270 | - | |
| 271 | - write(output_unit, '(a)') '' | |
| 272 | - end subroutine | |
| 273 | - | |
| 274 | - subroutine test_control_flow_keywords() | |
| 275 | - call assert_true(is_control_flow_keyword('if'), 'If keyword recognition') | |
| 276 | - call assert_true(is_control_flow_keyword('while'), 'While keyword recognition') | |
| 277 | - call assert_true(is_control_flow_keyword('for'), 'For keyword recognition') | |
| 278 | - call assert_true(.not. is_control_flow_keyword('echo'), 'Non-keyword rejection') | |
| 279 | - | |
| 280 | - call assert_equal_int(identify_flow_keyword('if'), FLOW_IF, 'If keyword identification') | |
| 281 | - call assert_equal_int(identify_flow_keyword('while'), FLOW_WHILE, 'While keyword identification') | |
| 282 | - end subroutine | |
| 283 | - | |
| 284 | - subroutine test_condition_evaluation() | |
| 285 | - type(shell_state_t) :: shell | |
| 286 | - logical :: result | |
| 287 | - | |
| 288 | - ! Initialize shell with success status | |
| 289 | - shell%last_exit_status = 0 | |
| 290 | - | |
| 291 | - call evaluate_condition('[ "hello" = "hello" ]', shell, result) | |
| 292 | - call assert_true(result, 'String equality condition') | |
| 293 | - | |
| 294 | - call evaluate_condition('[ "hello" = "world" ]', shell, result) | |
| 295 | - call assert_true(.not. result, 'String inequality condition') | |
| 296 | - end subroutine | |
| 297 | - | |
| 298 | - subroutine test_aliases_module() | |
| 299 | - write(output_unit, '(a)') 'Testing Aliases Module:' | |
| 300 | - write(output_unit, '(a)') '======================' | |
| 301 | - | |
| 302 | - call test_alias_operations() | |
| 303 | - | |
| 304 | - write(output_unit, '(a)') '' | |
| 305 | - end subroutine | |
| 306 | - | |
| 307 | - subroutine test_alias_operations() | |
| 308 | - type(shell_state_t) :: shell | |
| 309 | - character(len=:), allocatable :: expanded_line | |
| 310 | - | |
| 311 | - ! Initialize shell | |
| 312 | - shell%num_aliases = 0 | |
| 313 | - | |
| 314 | - ! Test alias creation | |
| 315 | - call set_alias(shell, 'll', 'ls -l') | |
| 316 | - call assert_equal_int(shell%num_aliases, 1, 'Alias count after creation') | |
| 317 | - call assert_true(is_alias(shell, 'll'), 'Alias existence check') | |
| 318 | - | |
| 319 | - ! Test alias expansion | |
| 320 | - call expand_alias(shell, 'll -a', expanded_line) | |
| 321 | - call assert_equal_str(expanded_line, 'ls -l -a', 'Alias expansion') | |
| 322 | - | |
| 323 | - ! Test non-alias expansion | |
| 324 | - call expand_alias(shell, 'echo hello', expanded_line) | |
| 325 | - call assert_equal_str(expanded_line, 'echo hello', 'Non-alias passthrough') | |
| 326 | - end subroutine | |
| 327 | - | |
| 328 | - subroutine test_error_handling() | |
| 329 | - write(output_unit, '(a)') 'Testing Error Handling:' | |
| 330 | - write(output_unit, '(a)') '======================' | |
| 331 | - | |
| 332 | - call test_parser_error_handling() | |
| 333 | - call test_command_error_handling() | |
| 334 | - | |
| 335 | - write(output_unit, '(a)') '' | |
| 336 | - end subroutine | |
| 337 | - | |
| 338 | - subroutine test_parser_error_handling() | |
| 339 | - type(pipeline_t) :: pipeline | |
| 340 | - | |
| 341 | - ! Test malformed pipeline | |
| 342 | - call parse_pipeline('command |', pipeline) | |
| 343 | - call assert_equal_int(pipeline%num_commands, 1, 'Malformed pipe handling') | |
| 344 | - | |
| 345 | - ! Cleanup | |
| 346 | - if (allocated(pipeline%commands)) then | |
| 347 | - if (allocated(pipeline%commands(1)%tokens)) deallocate(pipeline%commands(1)%tokens) | |
| 348 | - deallocate(pipeline%commands) | |
| 349 | - end if | |
| 350 | - end subroutine | |
| 351 | - | |
| 352 | - subroutine test_command_error_handling() | |
| 353 | - ! Test invalid redirection | |
| 354 | - call assert_true(.true., 'Invalid redirection placeholder test') | |
| 355 | - | |
| 356 | - ! Test command not found | |
| 357 | - call assert_true(.true., 'Command not found placeholder test') | |
| 358 | - end subroutine | |
| 359 | - | |
| 360 | -end program test_runner | |
tests/test_simple_rematch.shdeleted@@ -1,14 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | - | |
| 3 | -echo "Simple BASH_REMATCH test" | |
| 4 | -str="John Doe" | |
| 5 | -echo "String: $str" | |
| 6 | - | |
| 7 | -if [[ $str =~ ^([A-Z][a-z]+)\ ([A-Z][a-z]+)$ ]]; then | |
| 8 | - echo "Match SUCCESS" | |
| 9 | - echo "BASH_REMATCH[0]: '${BASH_REMATCH[0]}'" | |
| 10 | - echo "BASH_REMATCH[1]: '${BASH_REMATCH[1]}'" | |
| 11 | - echo "BASH_REMATCH[2]: '${BASH_REMATCH[2]}'" | |
| 12 | -else | |
| 13 | - echo "Match FAILED" | |
| 14 | -fi | |
tests/test_tilde.shdeleted@@ -1,78 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | -# Test tilde expansion | |
| 3 | - | |
| 4 | -echo "=== Tilde Expansion Tests ===" | |
| 5 | -echo "" | |
| 6 | - | |
| 7 | -# Set up test environment | |
| 8 | -export HOME="/home/testuser" | |
| 9 | -export PWD="/current/dir" | |
| 10 | -export OLDPWD="/previous/dir" | |
| 11 | - | |
| 12 | -# Test 1: Simple ~ | |
| 13 | -echo "Test 1: echo ~" | |
| 14 | -result=$(echo ~) | |
| 15 | -expected="$HOME" | |
| 16 | -if [ "$result" = "$expected" ]; then | |
| 17 | - echo "✓ PASS: ~ expanded to $result" | |
| 18 | -else | |
| 19 | - echo "✗ FAIL: expected $expected, got $result" | |
| 20 | -fi | |
| 21 | -echo "" | |
| 22 | - | |
| 23 | -# Test 2: ~/path | |
| 24 | -echo "Test 2: echo ~/documents" | |
| 25 | -result=$(echo ~/documents) | |
| 26 | -expected="$HOME/documents" | |
| 27 | -if [ "$result" = "$expected" ]; then | |
| 28 | - echo "✓ PASS: ~/documents expanded to $result" | |
| 29 | -else | |
| 30 | - echo "✗ FAIL: expected $expected, got $result" | |
| 31 | -fi | |
| 32 | -echo "" | |
| 33 | - | |
| 34 | -# Test 3: ~+ (current directory) | |
| 35 | -echo "Test 3: echo ~+" | |
| 36 | -result=$(echo ~+) | |
| 37 | -expected="$PWD" | |
| 38 | -if [ "$result" = "$expected" ]; then | |
| 39 | - echo "✓ PASS: ~+ expanded to $result" | |
| 40 | -else | |
| 41 | - echo "✗ FAIL: expected $expected, got $result" | |
| 42 | -fi | |
| 43 | -echo "" | |
| 44 | - | |
| 45 | -# Test 4: ~- (previous directory) | |
| 46 | -echo "Test 4: echo ~-" | |
| 47 | -result=$(echo ~-) | |
| 48 | -expected="$OLDPWD" | |
| 49 | -if [ "$result" = "$expected" ]; then | |
| 50 | - echo "✓ PASS: ~- expanded to $result" | |
| 51 | -else | |
| 52 | - echo "✗ FAIL: expected $expected, got $result" | |
| 53 | -fi | |
| 54 | -echo "" | |
| 55 | - | |
| 56 | -# Test 5: ~username (should work if user exists) | |
| 57 | -echo "Test 5: echo ~root" | |
| 58 | -result=$(echo ~root) | |
| 59 | -# Just check it doesn't still have the tilde | |
| 60 | -if [[ "$result" != "~root" ]]; then | |
| 61 | - echo "✓ PASS: ~root expanded to $result" | |
| 62 | -else | |
| 63 | - echo "✗ FAIL: ~root was not expanded" | |
| 64 | -fi | |
| 65 | -echo "" | |
| 66 | - | |
| 67 | -# Test 6: Non-tilde word (should not be expanded) | |
| 68 | -echo "Test 6: echo notilde" | |
| 69 | -result=$(echo notilde) | |
| 70 | -expected="notilde" | |
| 71 | -if [ "$result" = "$expected" ]; then | |
| 72 | - echo "✓ PASS: notilde remained $result" | |
| 73 | -else | |
| 74 | - echo "✗ FAIL: expected $expected, got $result" | |
| 75 | -fi | |
| 76 | -echo "" | |
| 77 | - | |
| 78 | -echo "=== Tests Complete ===" | |
tests/test_very_simple.shdeleted@@ -1,30 +0,0 @@ | ||
| 1 | -#!/bin/bash | |
| 2 | - | |
| 3 | -str="12345" | |
| 4 | -echo "Test 1: Simple digit pattern" | |
| 5 | -if [[ $str =~ ^[0-9]+$ ]]; then | |
| 6 | - echo "PASS: digits matched" | |
| 7 | - echo "BASH_REMATCH[0]: '${BASH_REMATCH[0]}'" | |
| 8 | -else | |
| 9 | - echo "FAIL: digits should match" | |
| 10 | -fi | |
| 11 | - | |
| 12 | -str2="hello" | |
| 13 | -echo "Test 2: Simple letter pattern" | |
| 14 | -if [[ $str2 =~ ^[a-z]+$ ]]; then | |
| 15 | - echo "PASS: letters matched" | |
| 16 | - echo "BASH_REMATCH[0]: '${BASH_REMATCH[0]}'" | |
| 17 | -else | |
| 18 | - echo "FAIL: letters should match" | |
| 19 | -fi | |
| 20 | - | |
| 21 | -str3="abc123" | |
| 22 | -echo "Test 3: Pattern with capture group" | |
| 23 | -if [[ $str3 =~ ^([a-z]+)([0-9]+)$ ]]; then | |
| 24 | - echo "PASS: pattern matched" | |
| 25 | - echo "BASH_REMATCH[0]: '${BASH_REMATCH[0]}'" | |
| 26 | - echo "BASH_REMATCH[1]: '${BASH_REMATCH[1]}'" | |
| 27 | - echo "BASH_REMATCH[2]: '${BASH_REMATCH[2]}'" | |
| 28 | -else | |
| 29 | - echo "FAIL: pattern should match" | |
| 30 | -fi | |