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

659 lines, 225 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 # Redirect
416 check-osh2ysh '
417if true; then
418 echo yes
419fi > out' \
420 '
421if true {
422 echo yes
423} > out'
424}
425
426TODO-test-then-next-line() {
427 # TODO: Brace must be on same line
428 check-osh2ysh '
429if true
430then
431 echo yes
432fi' \
433 '
434if true {
435 echo yes
436}'
437
438}
439
440test-posix-func() {
441 check-osh2ysh '
442 f() {
443 echo "hi"
444 }' '
445 proc f {
446 echo "hi"
447 }'
448
449 # The brace is moved
450 check-osh2ysh '
451 f()
452 {
453 echo "hi"
454 }' '
455 proc f {
456 echo "hi"
457 }'
458
459 return
460
461 # Nested functinos
462 check-osh2ysh '
463func1() {
464 echo func1
465 func2()
466 {
467 echo func2
468 }
469}' \
470 '
471proc func1 {
472 echo func1
473 proc func2
474 {
475 echo func2
476 }
477}'
478 return
479
480 # Non-brace function bodies
481 # TODO: Bail in this case
482 check-osh2ysh '
483 f() (
484 echo hi
485 )' \
486 '
487 proc f (
488 echo hi
489 )' \
490 INVALID
491}
492
493test-ksh-func() {
494 check-osh2ysh '
495function func1 { # no parens
496 echo func1
497}' '
498proc func1 { # no parens
499 echo func1
500}'
501}
502
503test-for-loop() {
504 check-osh2ysh '
505for x in a b c \
506 d e f; do
507 echo $x
508done
509' '
510for x in a b c \
511 d e f {
512 echo $x
513}
514'
515
516 check-osh2ysh '
517for x in a b c \
518 d e f
519do
520 echo $x
521done
522' '
523for x in a b c \
524 d e f
525{
526 echo $x
527}
528'
529}
530
531test-empty-for-loop() {
532 check-osh2ysh '
533for x in
534do
535 echo $x
536done
537' '
538for x in
539{
540 echo $x
541}
542'
543}
544
545test-args-for-loop() {
546 # Why are we missing a newline here?
547 check-osh2ysh '
548for x; do
549 echo $x
550done
551' 'for x in @ARGV {
552 echo $x
553}
554'
555 # Change brace style
556
557 check-osh2ysh '
558for x
559do
560 echo $x
561done
562' 'for x in @ARGV {
563 echo $x
564}
565'
566}
567
568# TODO: translate to forkwait { proper spaces }
569
570test-subshell() {
571 check-osh2ysh \
572 '(echo hi;)' \
573 'shell {echo hi;}' \
574 INVALID
575
576 check-osh2ysh \
577 '(echo hi)' \
578 'shell {echo hi}' \
579 INVALID
580
581 check-osh2ysh \
582 '(echo hi; echo bye)' \
583 'shell {echo hi; echo bye}' \
584 INVALID
585
586 check-osh2ysh \
587 '( (echo hi; echo bye ) )' \
588 'shell { shell {echo hi; echo bye } }' \
589 INVALID
590}
591
592test-brace-group() {
593 check-osh2ysh \
594 '{ echo hi; }' \
595 'do { echo hi; }' \
596 INVALID
597
598 check-osh2ysh \
599 '{ echo hi; echo bye; }' \
600 'do { echo hi; echo bye; }' \
601 INVALID
602}
603
604# TODO: New case syntax, which looks like
605#
606# case (myvar) {
607# *.cc | *.h { echo 'C++' }
608# }
609
610# case (myvar) {
611# *.cc | *.h {
612# echo 'C++'
613# }
614# }
615
616test-case() {
617 check-osh2ysh '
618case $var in
619 foo|bar)
620 [ -f foo ] && echo file
621 ;;
622 "")
623 echo empty
624 ;;
625 *)
626 echo default
627 ;;
628esac
629' '
630case (var) {
631 foo|bar {
632 test -f foo && echo file
633 }
634 "" {
635 echo empty
636 }
637 * {
638 echo default
639 }
640}
641'
642
643 check-osh2ysh '
644case "$var" in
645 *)
646 echo foo
647 echo bar # no dsemi
648esac
649' '
650case (var) {
651 * {
652 echo foo
653 echo bar # no dsemi
654}
655}
656'
657}
658
659run-task "$@"