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

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