Bash · 7651 bytes Raw Blame History
1 #!/bin/sh
2 # =====================================
3 # POSIX Compliance - Job Control Tests
4 # =====================================
5 # Tests for POSIX job control features (jobs, fg, bg, job specs)
6
7 # Colors (POSIX-compliant way)
8 RED='\033[0;31m'
9 GREEN='\033[0;32m'
10 YELLOW='\033[1;33m'
11 BLUE='\033[0;34m'
12 NC='\033[0m'
13
14 # Test identification
15 TEST_PREFIX="[posix-jobcontrol]"
16 CURRENT_SECTION=""
17 TEST_NUM=0
18
19 PASSED=0
20 FAILED=0
21 SKIPPED=0
22 FAILED_TESTS_LIST=""
23
24 # Get script directory (POSIX way)
25 SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
26 RUSH_BIN="${RUSH_BIN:-$SCRIPT_DIR/../target/release/rush}"
27
28 # Check if fortsh exists
29 if [ ! -x "$RUSH_BIN" ]; then
30 printf "${RED}ERROR${NC}: rush binary not found at $RUSH_BIN\n"
31 printf "Please run 'make' first or set RUSH_BIN environment variable\n"
32 exit 1
33 fi
34
35 # Test result trackers
36 pass() {
37 TEST_NUM=$((TEST_NUM + 1))
38 printf "${GREEN}✓ PASS${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s\n" "$1"
39 PASSED=$((PASSED + 1))
40 }
41
42 fail() {
43 TEST_NUM=$((TEST_NUM + 1))
44 TEST_ID="${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}"
45 printf "${RED}✗ FAIL${NC} ${TEST_ID}: %s\n" "$1"
46 FAILED_TESTS_LIST="${FAILED_TESTS_LIST} ${TEST_ID}: $1\n"
47 if [ -n "$2" ]; then
48 printf " expected: %s\n" "$2"
49 fi
50 if [ -n "$3" ]; then
51 printf " got: %s\n" "$3"
52 fi
53 FAILED=$((FAILED + 1))
54 }
55
56 skip() {
57 TEST_NUM=$((TEST_NUM + 1))
58 printf "${YELLOW}⊘ SKIP${NC} ${TEST_PREFIX} ${CURRENT_SECTION}.${TEST_NUM}: %s\n" "$1"
59 SKIPPED=$((SKIPPED + 1))
60 }
61
62 section() {
63 # Extract section number from header like "146. BASIC JOB CONTROL"
64 CURRENT_SECTION=$(echo "$1" | grep -oE '^[0-9]+' || echo "0")
65 TEST_NUM=0
66 printf "\n${BLUE}==========================================\n"
67 printf "%s\n" "$1"
68 printf "==========================================${NC}\n"
69 }
70
71 # Test that command succeeds
72 test_succeeds() {
73 test_name="$1"
74 test_cmd="$2"
75
76 if FORTSH_RC_FILE=/dev/null "$RUSH_BIN" -c "$test_cmd" >/dev/null 2>&1; then
77 pass "$test_name"
78 else
79 fail "$test_name" "should succeed" "failed"
80 fi
81 }
82
83 # Test that command produces expected output
84 test_output() {
85 test_name="$1"
86 test_cmd="$2"
87 expected="$3"
88
89 output=$(FORTSH_RC_FILE=/dev/null "$RUSH_BIN" -c "$test_cmd" 2>&1)
90 if [ "$output" = "$expected" ]; then
91 pass "$test_name"
92 else
93 fail "$test_name" "$expected" "$output"
94 fi
95 }
96
97 # Test that output contains pattern
98 test_contains() {
99 test_name="$1"
100 test_cmd="$2"
101 pattern="$3"
102
103 output=$(FORTSH_RC_FILE=/dev/null "$RUSH_BIN" -c "$test_cmd" 2>&1)
104 if echo "$output" | grep -q "$pattern"; then
105 pass "$test_name"
106 else
107 fail "$test_name" "output containing '$pattern'" "$output"
108 fi
109 }
110
111 # =====================================
112 # TESTS START HERE
113 # =====================================
114
115 section "146. BASIC JOB CONTROL"
116
117 # Note: Many job control features require interactive mode
118 # We test what we can in non-interactive mode
119
120 test_succeeds "jobs builtin exists" 'jobs >/dev/null 2>&1 || true'
121 test_succeeds "bg builtin exists" 'bg 2>/dev/null || true'
122 test_succeeds "fg builtin exists" 'fg 2>/dev/null || true'
123
124 section "147. BACKGROUND JOBS"
125
126 test_succeeds "simple background job" 'sleep 0.1 &'
127 test_succeeds "background with wait" 'sleep 0.1 & wait'
128 test_output "background job count" 'sleep 0.2 & sleep 0.2 & jobs | wc -l | tr -d " "' '2'
129 test_succeeds "wait for specific job" 'sleep 0.1 & PID=$!; wait $PID'
130
131 section "148. JOB EXIT STATUS"
132
133 test_output "background true exit" 'true & wait $!; echo $?' '0'
134 test_output "background false exit" 'false & wait $!; echo $?' '1'
135 test_output "wait preserves status" 'sh -c "exit 42" & wait $!; echo $?' '42'
136
137 section "149. JOBS BUILTIN OUTPUT"
138
139 test_succeeds "jobs with no jobs" 'jobs'
140 test_succeeds "jobs after background" 'sleep 0.5 & jobs; wait'
141 # Test that jobs shows running processes
142 test_contains "jobs shows running" 'sleep 0.5 & jobs' 'sleep'
143
144 section "150. BACKGROUND PIPELINES"
145
146 test_succeeds "pipeline in background" 'echo test | cat &'
147 test_succeeds "multi-stage background pipeline" 'echo test | cat | cat & wait'
148 # Pipeline output appears before exit status since it runs in background
149 test_output "background pipeline exit" 'true | cat & wait $!; echo $?' '0'
150
151 section "151. $! LAST BACKGROUND PID"
152
153 test_succeeds "$! is set after background" 'sleep 0.1 & test -n "$!"'
154 # Use test -gt to check numeric - pattern [!0-9] has portability issues
155 test_succeeds "$! is numeric" 'sleep 0.1 & test "$!" -gt 0'
156 test_succeeds "wait for $!" 'sleep 0.1 & wait $!'
157
158 section "152. JOB SPECIFICATIONS (if supported)"
159
160 # These may not work in non-interactive mode, so we're lenient
161 skip "job spec %1 (interactive feature)"
162 skip "job spec %% (interactive feature)"
163 skip "job spec %+ (interactive feature)"
164 skip "job spec %- (interactive feature)"
165
166 section "153. FG/BG WITH SUSPENDED JOBS"
167
168 # Suspending jobs requires interactive terminal (Ctrl-Z)
169 skip "fg with suspended job (requires interactive tty)"
170 skip "bg with suspended job (requires interactive tty)"
171
172 section "154. WAIT EDGE CASES"
173
174 test_output "wait with no args" 'sleep 0.1 & sleep 0.1 & wait; echo done' 'done'
175 test_output "wait nonexistent PID" 'wait 999999 2>&1 | grep -q "not found\|No such" && echo error || echo ok' 'error'
176 test_succeeds "multiple waits" 'sleep 0.1 & P1=$!; sleep 0.1 & P2=$!; wait $P1; wait $P2'
177
178 section "155. SET -m MONITOR MODE"
179
180 test_succeeds "set -m enables" 'set -m'
181 test_succeeds "set +m disables" 'set -m; set +m'
182 test_output "monitor doesn't affect output" 'set -m; echo test; set +m' 'test'
183
184 section "156. JOB CONTROL WITH FUNCTIONS"
185
186 test_succeeds "background function" 'f() { echo test; }; f &'
187 test_output "wait for function" 'f() { echo ok; }; f & wait; echo done' 'ok
188 done'
189
190 section "157. DISOWN (if implemented)"
191
192 # disown is not strictly POSIX but common
193 skip "disown builtin (not required by POSIX)"
194
195 section "158. AMPERSAND SEMANTICS"
196
197 # Background jobs still output to stdout - just test it succeeds
198 test_succeeds "& at end" 'echo test &'
199 test_output "multiple & commands" 'echo a & echo b & wait; echo done' 'a
200 b
201 done'
202
203 section "159. BACKGROUND SUBSHELLS"
204
205 test_succeeds "subshell in background" '(sleep 0.1) &'
206 test_output "background subshell isolation" 'VAR=outer; (VAR=inner) & wait; echo $VAR' 'outer'
207
208 section "160. JOB CONTROL ERROR CASES"
209
210 # Test error messages for invalid job control operations
211 test_contains "fg with no jobs" 'fg 2>&1' 'no.*job\|No current job'
212 test_contains "bg with no jobs" 'bg 2>&1' 'no.*job\|No current job'
213
214 # =====================================
215 # SUMMARY
216 # =====================================
217
218 printf "\n==========================================\n"
219 printf "JOB CONTROL TEST RESULTS ${TEST_PREFIX}\n"
220 printf "==========================================\n"
221 printf "${GREEN}Passed:${NC} %d\n" "$PASSED"
222 printf "${RED}Failed:${NC} %d\n" "$FAILED"
223 printf "${YELLOW}Skipped:${NC} %d\n" "$SKIPPED"
224 printf "Total: %d\n" "$((PASSED + FAILED + SKIPPED))"
225 printf "==========================================\n"
226
227 # Calculate pass rate (excluding skipped)
228 if [ "$((PASSED + FAILED))" -gt 0 ]; then
229 pass_rate=$((PASSED * 100 / (PASSED + FAILED)))
230 printf "Pass rate: %d%% (excluding skipped)\n" "$pass_rate"
231 fi
232
233 if [ "$FAILED" -gt 0 ]; then
234 printf "\n${RED}Failed tests:${NC}\n"
235 printf "%b" "$FAILED_TESTS_LIST"
236 printf "==========================================\n"
237 fi
238
239 if [ "$FAILED" -eq 0 ]; then
240 printf "${GREEN}ALL NON-SKIPPED JOB CONTROL TESTS PASSED!${NC} ✓\n"
241 exit 0
242 else
243 printf "${RED}SOME TESTS FAILED${NC} ✗\n"
244 exit 1
245 fi