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

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