1 | #!/usr/bin/env bash
|
2 | #
|
3 | # Test ysh-prettify transformations
|
4 | #
|
5 | # Usage:
|
6 | # ./ysh-prettify.sh <function name>
|
7 |
|
8 | set -o nounset
|
9 | set -o pipefail
|
10 | set -o errexit
|
11 | shopt -s strict:all 2>/dev/null || true # dogfood for OSH
|
12 |
|
13 | source devtools/run-task.sh
|
14 | source test/common.sh
|
15 |
|
16 | readonly TEMP_DIR=_tmp
|
17 |
|
18 | prettify-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 |
|
34 | smoke-test() {
|
35 | ### Run it against many of our files
|
36 | find build benchmarks -name '*.sh' | xargs -n 1 -- $0 prettify-one
|
37 | }
|
38 |
|
39 | run-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 |
|
49 | soil-run() {
|
50 | run-all
|
51 | }
|
52 |
|
53 | run-for-release() {
|
54 | run-other-suite-for-release ysh-ify run-all
|
55 | }
|
56 |
|
57 |
|
58 | #
|
59 | # Test Harness
|
60 | #
|
61 |
|
62 | check-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 |
|
90 | test-simple-command() {
|
91 | ### Unchanged
|
92 |
|
93 | check-osh2ysh 'echo hi' 'echo hi'
|
94 | }
|
95 |
|
96 |
|
97 | test-line-breaks() {
|
98 | ### Unchanged
|
99 |
|
100 | check-osh2ysh '
|
101 | echo one \
|
102 | two three \
|
103 | four
|
104 | ' '
|
105 | echo one \
|
106 | two three \
|
107 | four
|
108 | '
|
109 | }
|
110 |
|
111 | test-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 |
|
121 | test-dollar-at() {
|
122 | check-osh2ysh \
|
123 | 'echo one "$@" two' \
|
124 | 'echo one @ARGV two'
|
125 | }
|
126 |
|
127 | TODO-test-prefix-ops() {
|
128 | check-osh2ysh \
|
129 | 'echo ${#s} ${#a[@]}' \
|
130 | 'echo $[len(s)] $[len(a)]'
|
131 | }
|
132 |
|
133 | test-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 |
|
149 | TODO-test-word-joining() {
|
150 | local osh=$(cat <<EOF
|
151 | echo 'foo " bar '"'"
|
152 | EOF
|
153 | )
|
154 |
|
155 | # TODO: Use new YSTR syntax!
|
156 | local ysh=$(cat <<EOF
|
157 | echo y"foo \" bar '"
|
158 | EOF
|
159 | )
|
160 | check-osh2ysh "$osh" "$ysh"
|
161 | }
|
162 |
|
163 | # Unchanged
|
164 | test-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 |
|
174 | test-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 |
|
204 | test-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 |
|
224 | test-bracket-builtin() {
|
225 | check-osh2ysh \
|
226 | '[ ! -z "$foo" ] || die' \
|
227 | 'test ! -z $foo || die'
|
228 |
|
229 | check-osh2ysh '
|
230 | if [ "$foo" -eq 3 ]; then
|
231 | echo yes
|
232 | fi' \
|
233 | '
|
234 | if test $foo -eq 3 {
|
235 | echo yes
|
236 | }'
|
237 | }
|
238 |
|
239 | test-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 |
|
249 | TODO-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 |
|
260 | test-here-doc() {
|
261 | check-osh2ysh '
|
262 | cat <<EOF
|
263 | hi
|
264 | EOF
|
265 | ' '
|
266 | cat <<< """
|
267 | hi
|
268 | """
|
269 | '
|
270 |
|
271 | check-osh2ysh "
|
272 | cat <<'EOF'
|
273 | hi
|
274 | EOF
|
275 | " "
|
276 | cat <<< '''
|
277 | hi
|
278 | '''
|
279 | "
|
280 | }
|
281 |
|
282 | test-bare-assign-TODO() {
|
283 | check-osh2ysh "
|
284 | a=
|
285 | " "
|
286 | setvar a = ''
|
287 | "
|
288 |
|
289 | check-osh2ysh "
|
290 | a=b
|
291 | " "
|
292 | setvar a = 'b'
|
293 | "
|
294 |
|
295 | # TODO: Make it quoted
|
296 | if false; then
|
297 | check-osh2ysh '
|
298 | a="$x"
|
299 | ' '
|
300 | setvar a = "$x"
|
301 | '
|
302 | fi
|
303 |
|
304 | check-osh2ysh '
|
305 | a=$(hostname)
|
306 | ' '
|
307 | setvar a = $(hostname)
|
308 | '
|
309 |
|
310 | check-osh2ysh '
|
311 | a=${PATH:-}
|
312 | ' '
|
313 | setvar a = ${PATH:-}
|
314 | '
|
315 |
|
316 | return
|
317 | check-osh2ysh '
|
318 | a=$x
|
319 | ' '
|
320 | setvar a = "$x"
|
321 | '
|
322 |
|
323 | }
|
324 |
|
325 | TODO-test-assign-builtins() {
|
326 | check-osh2ysh "
|
327 | local a=
|
328 | " "
|
329 | var a = ''
|
330 | "
|
331 |
|
332 | check-osh2ysh "
|
333 | local a=b
|
334 | " "
|
335 | var a = 'b'
|
336 | "
|
337 |
|
338 | # TODO: more test cases
|
339 |
|
340 | check-osh2ysh "
|
341 | readonly a=b
|
342 | " "
|
343 | const a = 'b'
|
344 | "
|
345 | }
|
346 |
|
347 | test-while-loop() {
|
348 | check-osh2ysh '
|
349 | while read line; do
|
350 | echo $line
|
351 | done' \
|
352 | '
|
353 | while read line {
|
354 | echo $line
|
355 | }'
|
356 |
|
357 | check-osh2ysh '
|
358 | while read \
|
359 | line; do
|
360 | echo $line
|
361 | done' \
|
362 | '
|
363 | while read \
|
364 | line {
|
365 | echo $line
|
366 | }'
|
367 | }
|
368 |
|
369 | test-if() {
|
370 | check-osh2ysh '
|
371 | if true; then
|
372 | echo yes
|
373 | fi' \
|
374 | '
|
375 | if true {
|
376 | echo yes
|
377 | }'
|
378 |
|
379 | check-osh2ysh '
|
380 | if true; then
|
381 | echo yes
|
382 | elif false; then
|
383 | echo elif
|
384 | elif spam; then
|
385 | echo elif
|
386 | else
|
387 | echo no
|
388 | fi' \
|
389 | '
|
390 | if true {
|
391 | echo yes
|
392 | } elif false {
|
393 | echo elif
|
394 | } elif spam {
|
395 | echo elif
|
396 | } else {
|
397 | echo no
|
398 | }'
|
399 | }
|
400 |
|
401 | TODO-test-then-next-line() {
|
402 | # TODO: Brace must be on same line
|
403 | check-osh2ysh '
|
404 | if true
|
405 | then
|
406 | echo yes
|
407 | fi' \
|
408 | '
|
409 | if true {
|
410 | echo yes
|
411 | }'
|
412 |
|
413 | }
|
414 |
|
415 | test-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 '
|
438 | func1() {
|
439 | echo func1
|
440 | func2()
|
441 | {
|
442 | echo func2
|
443 | }
|
444 | }' \
|
445 | '
|
446 | proc 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 |
|
468 | test-ksh-func() {
|
469 | check-osh2ysh '
|
470 | function func1 { # no parens
|
471 | echo func1
|
472 | }' '
|
473 | proc func1 { # no parens
|
474 | echo func1
|
475 | }'
|
476 | }
|
477 |
|
478 | test-for-loop() {
|
479 | check-osh2ysh '
|
480 | for x in a b c \
|
481 | d e f; do
|
482 | echo $x
|
483 | done
|
484 | ' '
|
485 | for x in a b c \
|
486 | d e f {
|
487 | echo $x
|
488 | }
|
489 | '
|
490 |
|
491 | check-osh2ysh '
|
492 | for x in a b c \
|
493 | d e f
|
494 | do
|
495 | echo $x
|
496 | done
|
497 | ' '
|
498 | for x in a b c \
|
499 | d e f
|
500 | {
|
501 | echo $x
|
502 | }
|
503 | '
|
504 | }
|
505 |
|
506 | test-empty-for-loop() {
|
507 | check-osh2ysh '
|
508 | for x in
|
509 | do
|
510 | echo $x
|
511 | done
|
512 | ' '
|
513 | for x in
|
514 | {
|
515 | echo $x
|
516 | }
|
517 | '
|
518 | }
|
519 |
|
520 | test-args-for-loop() {
|
521 | # Why are we missing a newline here?
|
522 | check-osh2ysh '
|
523 | for x; do
|
524 | echo $x
|
525 | done
|
526 | ' 'for x in @ARGV {
|
527 | echo $x
|
528 | }
|
529 | '
|
530 | # Change brace style
|
531 |
|
532 | check-osh2ysh '
|
533 | for x
|
534 | do
|
535 | echo $x
|
536 | done
|
537 | ' 'for x in @ARGV {
|
538 | echo $x
|
539 | }
|
540 | '
|
541 | }
|
542 |
|
543 | # TODO: translate to forkwait { proper spaces }
|
544 |
|
545 | test-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 |
|
567 | test-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 |
|
591 | test-case() {
|
592 | check-osh2ysh '
|
593 | case $var in
|
594 | foo|bar)
|
595 | [ -f foo ] && echo file
|
596 | ;;
|
597 | "")
|
598 | echo empty
|
599 | ;;
|
600 | *)
|
601 | echo default
|
602 | ;;
|
603 | esac
|
604 | ' '
|
605 | case (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 '
|
619 | case "$var" in
|
620 | *)
|
621 | echo foo
|
622 | echo bar # no dsemi
|
623 | esac
|
624 | ' '
|
625 | case (var) {
|
626 | * {
|
627 | echo foo
|
628 | echo bar # no dsemi
|
629 | }
|
630 | }
|
631 | '
|
632 | }
|
633 |
|
634 | run-task "$@"
|