OILS / test / ysh-ify.sh View on Github | oilshell.org

649 lines, 223 significant
1#!/usr/bin/env bash
2#
3# Test ysh-prettify transformations
4#
5# Usage:
6# ./ysh-prettify.sh <function name>
7
8set -o nounset
9set -o pipefail
10set -o errexit
11shopt -s strict:all 2>/dev/null || true # dogfood for OSH
12
13source devtools/run-task.sh
14source test/common.sh # $OSH
15
16readonly TEMP_DIR=_tmp
17
18prettify-one() {
19 local file=$1
20
21 set +o errexit
22 $OSH --tool ysh-ify "$file"
23 local status=$?
24 set +o errexit
25
26 if test $status = 0; then
27 echo " (DONE $file)"
28 else
29 echo " FAIL: $file"
30 return 255 # xargs FAILURE
31 fi
32}
33
34smoke-test() {
35 ### Run it against many of our files
36 find build benchmarks -name '*.sh' | xargs -n 1 -- $0 prettify-one
37}
38
39run-all() {
40 ### For both CI and release.
41
42 # Note: might want to split these up.
43
44 run-test-funcs
45
46 smoke-test
47}
48
49soil-run() {
50 run-all
51}
52
53soil-run-cpp() {
54 ### Not used yet, but it works
55
56 local osh=_bin/cxx-asan/osh
57 ninja $osh
58
59 #OSH=$osh run-test-funcs
60 OSH=$osh run-all
61}
62
63run-for-release() {
64 run-other-suite-for-release ysh-ify run-all
65}
66
67
68#
69# Test Harness
70#
71
72check-osh2ysh() {
73 local osh_str=$1
74 local ysh_str=$2 # expected
75 local allow_invalid=${3:-}
76
77 # Make sure they are valid
78
79 bin/osh -n -c "$osh_str"
80 if test -z "$allow_invalid"; then
81 bin/ysh -n -c "$ysh_str"
82 fi
83
84 local tmp=$TEMP_DIR/actual.ysh
85 echo "$osh_str" | bin/osh --tool ysh-ify | tee $tmp
86
87 echo "$ysh_str" | diff -u $tmp -
88 echo 'OK'
89
90 # TODO: Also create a variant that tests equal STDOUT and STATUS!
91 # probably assert no stderr
92 #
93 # For backticks, etc.
94}
95
96#
97# UNCHANGED
98#
99
100test-simple-command() {
101 ### Unchanged
102
103 check-osh2ysh 'echo hi' 'echo hi'
104}
105
106
107test-line-breaks() {
108 ### Unchanged
109
110 check-osh2ysh '
111echo one \
112 two three \
113 four
114' '
115echo one \
116 two three \
117 four
118'
119}
120
121test-and-or() {
122 check-osh2ysh \
123 'ls && echo "$@" || die "foo"' \
124 'ls && echo @ARGV || die "foo"'
125}
126
127#
128# CHANGED WORD LANGUAGE
129#
130
131test-dollar-at() {
132 check-osh2ysh \
133 'echo one "$@" two' \
134 'echo one @ARGV two'
135}
136
137TODO-test-prefix-ops() {
138 check-osh2ysh \
139 'echo ${#s} ${#a[@]}' \
140 'echo $[len(s)] $[len(a)]'
141}
142
143test-unquote-subs-TODO() {
144 check-osh2ysh \
145 'echo "$1" "$foo"' \
146 'echo $1 $foo'
147
148 check-osh2ysh \
149 'echo "$(echo hi)"' \
150 'echo $(echo hi)'
151
152 return
153 # TODO: echo $foo
154 check-osh2ysh \
155 'echo "${foo}"' \
156 'echo $foo'
157}
158
159TODO-test-word-joining() {
160 local osh=$(cat <<EOF
161echo 'foo " bar '"'"
162EOF
163)
164
165 # TODO: Use new YSTR syntax!
166 local ysh=$(cat <<EOF
167echo y"foo \" bar '"
168EOF
169)
170 check-osh2ysh "$osh" "$ysh"
171}
172
173# Unchanged
174test-command-sub() {
175 check-osh2ysh \
176 'echo $(echo hi)' \
177 'echo $(echo hi)'
178
179 check-osh2ysh \
180 'echo "__$(echo hi)__"' \
181 'echo "__$(echo hi)__"'
182}
183
184test-var-sub() {
185 # Unchanged
186 check-osh2ysh \
187 'echo $foo' \
188 'echo $foo'
189
190 # Could just be $bar
191 check-osh2ysh \
192 'echo $foo ${bar} "__${bar}__"' \
193 'echo $foo ${bar} "__${bar}__"'
194
195 return
196
197 # We could make this $[foo ? 'default'], but meh, let's not introduce more
198 # operators
199 #
200 # Better is getvar('foo', 'default')
201
202 check-osh2ysh \
203 'echo ${foo:-default}' \
204 "echo $[getvar('foo', 'default')]"
205}
206
207# Downgraded to one_pass_parse. This means \" will be wrong, but meh.
208# Here the WordParser makes another pass with CommandParser.
209#
210# We could also translate it to:
211# echo $[compat backticks 'echo hi']
212# But that might be overly pedantic. This will work most of the time.
213
214test-backticks-TODO() {
215 check-osh2ysh \
216 'echo `echo hi ${var}`' \
217 'echo $(echo hi ${var})'
218
219 check-osh2ysh \
220 'echo $({ echo hi; })' \
221 'echo $({ echo hi; })'
222
223 # TODO: Fix this
224 check-osh2ysh \
225 'echo `{ echo hi; }`' \
226 'echo $(do { echo hi)' \
227 INVALID
228}
229
230#
231# CHANGED BUILTIN LANGUAGE
232#
233
234test-bracket-builtin() {
235 check-osh2ysh \
236 '[ ! -z "$foo" ] || die' \
237 'test ! -z $foo || die'
238
239 # Don't touch this invalid code?
240 check-osh2ysh \
241 '[ ] || die' \
242 '[ ] || die'
243
244 check-osh2ysh '
245if [ "$foo" -eq 3 ]; then
246 echo yes
247fi' \
248 '
249if test $foo -eq 3 {
250 echo yes
251}'
252}
253
254test-source-builtin() {
255 check-osh2ysh \
256 '. lib.sh' \
257 'source lib.sh'
258
259 check-osh2ysh \
260 '[ -f lib.sh ] && . lib.sh' \
261 'test -f lib.sh && source lib.sh'
262}
263
264TODO-test-set-builtin() {
265 # Not as important now that we have 'setvar'
266 check-osh2ysh \
267 'set -o errexit' \
268 'shopt --set errexit'
269}
270
271#
272# CHANGED COMMAND LANGUAGE
273#
274
275test-here-doc() {
276 check-osh2ysh '
277cat <<EOF
278hi
279EOF
280' '
281cat <<< """
282hi
283"""
284'
285
286 check-osh2ysh "
287cat <<'EOF'
288hi
289EOF
290" "
291cat <<< '''
292hi
293'''
294"
295}
296
297test-bare-assign-TODO() {
298 check-osh2ysh "
299a=
300" "
301setvar a = ''
302"
303
304 check-osh2ysh "
305a=b
306" "
307setvar a = 'b'
308"
309
310 # TODO: Make it quoted
311 if false; then
312 check-osh2ysh '
313a="$x"
314' '
315setvar a = "$x"
316'
317 fi
318
319 check-osh2ysh '
320a=$(hostname)
321' '
322setvar a = $(hostname)
323'
324
325 check-osh2ysh '
326a=${PATH:-}
327' '
328setvar a = ${PATH:-}
329'
330
331 return
332 check-osh2ysh '
333a=$x
334' '
335setvar a = "$x"
336'
337
338}
339
340TODO-test-assign-builtins() {
341 check-osh2ysh "
342local a=
343" "
344var a = ''
345"
346
347 check-osh2ysh "
348local a=b
349" "
350var a = 'b'
351"
352
353 # TODO: more test cases
354
355 check-osh2ysh "
356readonly a=b
357" "
358const a = 'b'
359"
360}
361
362test-while-loop() {
363 check-osh2ysh '
364while read line; do
365 echo $line
366done' \
367 '
368while read line {
369 echo $line
370}'
371
372 check-osh2ysh '
373while read \
374 line; do
375 echo $line
376done' \
377 '
378while read \
379 line {
380 echo $line
381}'
382}
383
384test-if() {
385 check-osh2ysh '
386if true; then
387 echo yes
388fi' \
389 '
390if true {
391 echo yes
392}'
393
394 check-osh2ysh '
395if true; then
396 echo yes
397elif false; then
398 echo elif
399elif spam; then
400 echo elif
401else
402 echo no
403fi' \
404 '
405if true {
406 echo yes
407} elif false {
408 echo elif
409} elif spam {
410 echo elif
411} else {
412 echo no
413}'
414}
415
416TODO-test-then-next-line() {
417 # TODO: Brace must be on same line
418 check-osh2ysh '
419if true
420then
421 echo yes
422fi' \
423 '
424if true {
425 echo yes
426}'
427
428}
429
430test-posix-func() {
431 check-osh2ysh '
432 f() {
433 echo "hi"
434 }' '
435 proc f {
436 echo "hi"
437 }'
438
439 # The brace is moved
440 check-osh2ysh '
441 f()
442 {
443 echo "hi"
444 }' '
445 proc f {
446 echo "hi"
447 }'
448
449 return
450
451 # Nested functinos
452 check-osh2ysh '
453func1() {
454 echo func1
455 func2()
456 {
457 echo func2
458 }
459}' \
460 '
461proc func1 {
462 echo func1
463 proc func2
464 {
465 echo func2
466 }
467}'
468 return
469
470 # Non-brace function bodies
471 # TODO: Bail in this case
472 check-osh2ysh '
473 f() (
474 echo hi
475 )' \
476 '
477 proc f (
478 echo hi
479 )' \
480 INVALID
481}
482
483test-ksh-func() {
484 check-osh2ysh '
485function func1 { # no parens
486 echo func1
487}' '
488proc func1 { # no parens
489 echo func1
490}'
491}
492
493test-for-loop() {
494 check-osh2ysh '
495for x in a b c \
496 d e f; do
497 echo $x
498done
499' '
500for x in a b c \
501 d e f {
502 echo $x
503}
504'
505
506 check-osh2ysh '
507for x in a b c \
508 d e f
509do
510 echo $x
511done
512' '
513for x in a b c \
514 d e f
515{
516 echo $x
517}
518'
519}
520
521test-empty-for-loop() {
522 check-osh2ysh '
523for x in
524do
525 echo $x
526done
527' '
528for x in
529{
530 echo $x
531}
532'
533}
534
535test-args-for-loop() {
536 # Why are we missing a newline here?
537 check-osh2ysh '
538for x; do
539 echo $x
540done
541' 'for x in @ARGV {
542 echo $x
543}
544'
545 # Change brace style
546
547 check-osh2ysh '
548for x
549do
550 echo $x
551done
552' 'for x in @ARGV {
553 echo $x
554}
555'
556}
557
558# TODO: translate to forkwait { proper spaces }
559
560test-subshell() {
561 check-osh2ysh \
562 '(echo hi;)' \
563 'shell {echo hi;}' \
564 INVALID
565
566 check-osh2ysh \
567 '(echo hi)' \
568 'shell {echo hi}' \
569 INVALID
570
571 check-osh2ysh \
572 '(echo hi; echo bye)' \
573 'shell {echo hi; echo bye}' \
574 INVALID
575
576 check-osh2ysh \
577 '( (echo hi; echo bye ) )' \
578 'shell { shell {echo hi; echo bye } }' \
579 INVALID
580}
581
582test-brace-group() {
583 check-osh2ysh \
584 '{ echo hi; }' \
585 'do { echo hi; }' \
586 INVALID
587
588 check-osh2ysh \
589 '{ echo hi; echo bye; }' \
590 'do { echo hi; echo bye; }' \
591 INVALID
592}
593
594# TODO: New case syntax, which looks like
595#
596# case (myvar) {
597# *.cc | *.h { echo 'C++' }
598# }
599
600# case (myvar) {
601# *.cc | *.h {
602# echo 'C++'
603# }
604# }
605
606test-case() {
607 check-osh2ysh '
608case $var in
609 foo|bar)
610 [ -f foo ] && echo file
611 ;;
612 "")
613 echo empty
614 ;;
615 *)
616 echo default
617 ;;
618esac
619' '
620case (var) {
621 foo|bar {
622 test -f foo && echo file
623 }
624 "" {
625 echo empty
626 }
627 * {
628 echo default
629 }
630}
631'
632
633 check-osh2ysh '
634case "$var" in
635 *)
636 echo foo
637 echo bar # no dsemi
638esac
639' '
640case (var) {
641 * {
642 echo foo
643 echo bar # no dsemi
644}
645}
646'
647}
648
649run-task "$@"