OILS / test / spec.sh View on Github | oilshell.org

965 lines, 648 significant
1#!/usr/bin/env bash
2#
3# Usage:
4# test/spec.sh <function name>
5
6set -o nounset
7set -o pipefail
8set -o errexit
9shopt -s strict:all 2>/dev/null || true # dogfood for OSH
10
11REPO_ROOT=$(cd "$(dirname $0)/.."; pwd)
12
13source test/common.sh
14source test/spec-common.sh
15source devtools/run-task.sh
16
17if test -z "${IN_NIX_SHELL:-}"; then
18 source build/dev-shell.sh # to run 'dash', etc.
19fi
20
21# TODO: Just use 'dash bash' and $PATH
22readonly DASH=dash
23readonly BASH=bash
24readonly MKSH=mksh
25readonly ZSH=zsh
26readonly BUSYBOX_ASH=ash
27
28# ash and dash are similar, so not including ash by default. zsh is not quite
29# POSIX.
30readonly REF_SHELLS=($DASH $BASH $MKSH)
31
32check-survey-shells() {
33 ### Make sure bash, zsh, OSH, etc. exist
34
35 # Note: yash isn't here, but it is used in a couple tests
36
37 test/spec-runner.sh shell-sanity-check "${REF_SHELLS[@]}" $ZSH $BUSYBOX_ASH $OSH_LIST
38}
39
40# TODO: remove this stub after we hollow out this file
41
42run-file() { test/spec-py.sh run-file "$@"; }
43
44#
45# Misc
46#
47
48# Really what I want is enter(func) and exit(func), and filter by regex?
49trace-var-sub() {
50 local out=_tmp/coverage
51 mkdir -p $out
52
53 # This creates *.cover files, with line counts.
54 #python -m trace --count -C $out \
55
56 # This prints trace with line numbers to stdout.
57 #python -m trace --trace -C $out \
58 PYTHONPATH=. python -m trace --trackcalls -C $out \
59 test/sh_spec.py spec/var-sub.test.sh $DASH $BASH "$@"
60
61 ls -l $out
62 head $out/*.cover
63}
64
65#
66# Individual tests.
67#
68# We configure the shells they run on and the number of allowed failures (to
69# prevent regressions.)
70#
71
72interactive-parse() {
73 run-file interactive-parse "$@"
74}
75
76smoke() {
77 run-file smoke "$@"
78}
79
80interactive() {
81 run-file interactive "$@"
82}
83
84prompt() {
85 run-file prompt "$@"
86}
87
88bugs() {
89 run-file bugs "$@"
90}
91
92TODO-deprecate() {
93 run-file TODO-deprecate "$@"
94}
95
96blog1() {
97 sh-spec spec/blog1.test.sh \
98 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
99}
100
101blog2() {
102 run-file blog2 "$@"
103}
104
105blog-other1() {
106 sh-spec spec/blog-other1.test.sh \
107 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
108}
109
110alias() {
111 run-file alias "$@"
112}
113
114comments() {
115 sh-spec spec/comments.test.sh ${REF_SHELLS[@]} $OSH_LIST "$@"
116}
117
118word-split() {
119 run-file word-split "$@"
120}
121
122word-eval() {
123 sh-spec spec/word-eval.test.sh \
124 ${REF_SHELLS[@]} $OSH_LIST "$@"
125}
126
127# These cases apply to many shells.
128assign() {
129 run-file assign "$@"
130}
131
132# These cases apply to a few shells.
133assign-extended() {
134 run-file assign-extended "$@"
135}
136
137# Corner cases that OSH doesn't handle
138assign-deferred() {
139 sh-spec spec/assign-deferred.test.sh \
140 $BASH $MKSH "$@"
141}
142
143# These test associative arrays
144assign-dialects() {
145 run-file assign-dialects "$@"
146}
147
148background() {
149 run-file background "$@"
150}
151
152subshell() {
153 sh-spec spec/subshell.test.sh \
154 ${REF_SHELLS[@]} $OSH_LIST "$@"
155}
156
157quote() {
158 sh-spec spec/quote.test.sh \
159 ${REF_SHELLS[@]} $BUSYBOX_ASH $OSH_LIST "$@"
160}
161
162unicode() {
163 run-file unicode "$@"
164}
165
166loop() {
167 run-file loop "$@"
168}
169
170case_() {
171 run-file case_ "$@"
172}
173
174if_() {
175 sh-spec spec/if_.test.sh \
176 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
177}
178
179builtin-misc() {
180 run-file builtin-misc "$@"
181}
182
183builtin-process() {
184 run-file builtin-process "$@"
185}
186
187builtin-cd() {
188 run-file builtin-cd "$@"
189}
190
191builtin-eval-source() {
192 run-file builtin-eval-source "$@"
193}
194
195builtin-echo() {
196 run-file builtin-echo "$@"
197}
198
199builtin-read() {
200 run-file builtin-read "$@"
201}
202
203nul-bytes() {
204 run-file nul-bytes "$@"
205}
206
207whitespace() {
208 run-file whitespace "$@"
209}
210
211# Special bash printf things like -v and %q. Portable stuff goes in builtin-io.
212builtin-printf() {
213 run-file builtin-printf "$@"
214}
215
216builtin-meta() {
217 run-file builtin-meta "$@"
218}
219
220builtin-history() {
221 run-file builtin-history "$@"
222}
223
224# dash and mksh don't implement 'dirs'
225builtin-dirs() {
226 sh-spec spec/builtin-dirs.test.sh \
227 $BASH $ZSH $OSH_LIST "$@"
228}
229
230builtin-vars() {
231 run-file builtin-vars "$@"
232}
233
234builtin-getopts() {
235 run-file builtin-getopts "$@"
236}
237
238builtin-bracket() {
239 run-file builtin-bracket "$@"
240}
241
242builtin-trap() {
243 sh-spec spec/builtin-trap.test.sh \
244 ${REF_SHELLS[@]} $OSH_LIST "$@"
245}
246
247builtin-trap-bash() {
248 run-file builtin-trap-bash "$@"
249}
250
251# Bash implements type -t, but no other shell does. For Nix.
252# zsh/mksh/dash don't have the 'help' builtin.
253builtin-bash() {
254 run-file builtin-bash "$@"
255}
256
257builtin-type() {
258 run-file builtin-type "$@"
259}
260
261builtin-type-bash() {
262 run-file builtin-type-bash "$@"
263}
264
265vars-bash() {
266 run-file vars-bash "$@"
267}
268
269vars-special() {
270 run-file vars-special "$@"
271}
272
273builtin-completion() {
274 run-file builtin-completion "$@"
275}
276
277builtin-special() {
278 run-file builtin-special "$@"
279}
280
281builtin-times() {
282 sh-spec spec/builtin-times.test.sh $BASH $ZSH $OSH_LIST "$@"
283}
284
285command-parsing() {
286 sh-spec spec/command-parsing.test.sh ${REF_SHELLS[@]} $OSH_LIST "$@"
287}
288
289func-parsing() {
290 sh-spec spec/func-parsing.test.sh ${REF_SHELLS[@]} $OSH_LIST "$@"
291}
292
293sh-func() {
294 run-file sh-func "$@"
295}
296
297glob() {
298 run-file glob "$@"
299}
300
301globignore() {
302 run-file globignore "$@"
303}
304
305arith() {
306 run-file arith "$@"
307}
308
309command-sub() {
310 sh-spec spec/command-sub.test.sh \
311 ${REF_SHELLS[@]} $OSH_LIST "$@"
312}
313
314command_() {
315 sh-spec spec/command_.test.sh \
316 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
317}
318
319pipeline() {
320 sh-spec spec/pipeline.test.sh \
321 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
322}
323
324explore-parsing() {
325 sh-spec spec/explore-parsing.test.sh \
326 ${REF_SHELLS[@]} $OSH_LIST "$@"
327}
328
329parse-errors() {
330 run-file parse-errors "$@"
331}
332
333here-doc() {
334 # NOTE: The last two tests, 31 and 32, have different behavior on my Ubuntu
335 # and Debian machines.
336 # - On Ubuntu, read_from_fd.py fails with Errno 9 -- bad file descriptor.
337 # - On Debian, the whole process hangs.
338 # Is this due to Python 3.2 vs 3.4? Either way osh doesn't implement the
339 # functionality, so it's probably best to just implement it.
340 sh-spec spec/here-doc.test.sh --range 0-31 \
341 ${REF_SHELLS[@]} $OSH_LIST "$@"
342}
343
344redirect() {
345 run-file redirect "$@"
346}
347
348redirect-command() {
349 run-file redirect-command "$@"
350}
351
352redirect-multi() {
353 run-file redirect-multi "$@"
354}
355
356posix() {
357 sh-spec spec/posix.test.sh \
358 ${REF_SHELLS[@]} $OSH_LIST "$@"
359}
360
361introspect() {
362 run-file introspect "$@"
363}
364
365tilde() {
366 run-file tilde "$@"
367}
368
369var-op-test() {
370 run-file var-op-test "$@"
371}
372
373var-op-len() {
374 sh-spec spec/var-op-len.test.sh \
375 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
376}
377
378var-op-patsub() {
379 # 1 unicode failure, and [^]] which is a parsing divergence
380 run-file var-op-patsub "$@"
381}
382
383var-op-slice() {
384 run-file var-op-slice "$@"
385}
386
387var-op-bash() {
388 run-file var-op-bash "$@"
389}
390
391var-op-strip() {
392 sh-spec spec/var-op-strip.test.sh \
393 ${REF_SHELLS[@]} $ZSH $BUSYBOX_ASH $OSH_LIST "$@"
394}
395
396var-sub() {
397 # NOTE: ZSH has interesting behavior, like echo hi > "$@" can write to TWO
398 # FILES! But ultimately we don't really care, so I disabled it.
399 sh-spec spec/var-sub.test.sh \
400 ${REF_SHELLS[@]} $OSH_LIST "$@"
401}
402
403var-num() {
404 run-file var-num "$@"
405}
406
407var-sub-quote() {
408 sh-spec spec/var-sub-quote.test.sh \
409 ${REF_SHELLS[@]} $OSH_LIST "$@"
410}
411
412sh-usage() {
413 run-file sh-usage "$@"
414}
415
416sh-options() {
417 run-file sh-options "$@"
418}
419
420xtrace() {
421 run-file xtrace "$@"
422}
423
424strict-options() {
425 run-file strict-options "$@"
426}
427
428exit-status() {
429 run-file exit-status "$@"
430}
431
432errexit() {
433 run-file errexit "$@"
434}
435
436errexit-osh() {
437 run-file errexit-osh "$@"
438}
439
440fatal-errors() {
441 sh-spec spec/fatal-errors.test.sh \
442 ${REF_SHELLS[@]} $ZSH $OSH_LIST "$@"
443}
444
445#
446# Non-POSIX extensions: arrays, brace expansion, [[, ((, etc.
447#
448
449# There as many non-POSIX arithmetic contexts.
450arith-context() {
451 sh-spec spec/arith-context.test.sh \
452 $BASH $MKSH $ZSH $OSH_LIST "$@"
453}
454
455array() {
456 run-file array "$@"
457}
458
459array-compat() {
460 run-file array-compat "$@"
461}
462
463type-compat() {
464 run-file type-compat "$@"
465}
466
467# += is not POSIX and not in dash.
468append() {
469 run-file append "$@"
470}
471
472# associative array -- mksh and zsh implement different associative arrays.
473assoc() {
474 run-file assoc "$@"
475}
476
477# ZSH also has associative arrays
478assoc-zsh() {
479 sh-spec spec/assoc-zsh.test.sh $ZSH "$@"
480}
481
482dbracket() {
483 run-file dbracket "$@"
484}
485
486dparen() {
487 run-file dparen "$@"
488}
489
490brace-expansion() {
491 run-file brace-expansion "$@"
492}
493
494regex() {
495 run-file regex "$@"
496}
497
498process-sub() {
499 run-file process-sub "$@"
500}
501
502# This does file system globbing
503extglob-files() {
504 run-file extglob-files "$@"
505}
506
507# This does string matching.
508extglob-match() {
509 sh-spec spec/extglob-match.test.sh \
510 $BASH $MKSH $OSH_LIST "$@"
511}
512
513nocasematch-match() {
514 run-file nocasematch-match "$@"
515}
516
517# ${!var} syntax -- oil should replace this with associative arrays.
518# mksh has completely different behavior for this syntax. Not worth testing.
519var-ref() {
520 run-file var-ref "$@"
521}
522
523nameref() {
524 ### declare -n / local -n
525 run-file nameref "$@"
526}
527
528let() {
529 sh-spec spec/let.test.sh $BASH $MKSH $ZSH "$@"
530}
531
532for-expr() {
533 run-file for-expr "$@"
534}
535
536empty-bodies() {
537 sh-spec spec/empty-bodies.test.sh "${REF_SHELLS[@]}" $ZSH $OSH_LIST "$@"
538}
539
540# TODO: This is for the ANTLR grammars, in the oil-sketch repo.
541# osh has infinite loop?
542shell-grammar() {
543 sh-spec spec/shell-grammar.test.sh $BASH $MKSH $ZSH "$@"
544}
545
546serialize() {
547 run-file serialize "$@"
548}
549
550#
551# Smoosh
552#
553
554readonly SMOOSH_REPO=~/git/languages/smoosh
555
556sh-spec-smoosh-env() {
557 local test_file=$1
558 shift
559
560 # - smoosh tests use $TEST_SHELL instead of $SH
561 # - cd $TMP to avoid littering repo
562 # - pass -o posix
563 # - timeout of 1 second
564 # - Some tests in smoosh use $HOME and $LOGNAME
565
566 sh-spec $test_file \
567 --sh-env-var-name TEST_SHELL \
568 --posix \
569 --env-pair "TEST_UTIL=$SMOOSH_REPO/tests/util" \
570 --env-pair "LOGNAME=$LOGNAME" \
571 --env-pair "HOME=$HOME" \
572 --timeout 1 \
573 --oils-bin-dir $REPO_ROOT/bin \
574 --compare-shells \
575 "$@"
576}
577
578# For speed, only run with one copy of OSH.
579readonly smoosh_osh_list=$OSH_CPYTHON
580
581smoosh() {
582 ### Run case smoosh from the console
583
584 # TODO: Use --oils-bin-dir
585 # our_shells, etc.
586
587 sh-spec-smoosh-env _tmp/smoosh.test.sh \
588 ${REF_SHELLS[@]} $smoosh_osh_list \
589 "$@"
590}
591
592smoosh-hang() {
593 ### Run case smoosh-hang from the console
594
595 # Need the smoosh timeout tool to run correctly.
596 sh-spec-smoosh-env _tmp/smoosh-hang.test.sh \
597 --timeout-bin "$SMOOSH_REPO/tests/util/timeout" \
598 --timeout 1 \
599 "$@"
600}
601
602_one-html() {
603 local spec_name=$1
604 shift
605
606 local out_dir=_tmp/spec/smoosh
607 local tmp_dir=_tmp/src-smoosh
608 mkdir -p $out_dir $out_dir
609
610 doctools/src_tree.py smoosh-file \
611 _tmp/$spec_name.test.sh \
612 $out_dir/$spec_name.test.html
613
614 local out=$out_dir/${spec_name}.html
615 set +o errexit
616 # Shell function is smoosh or smoosh-hang
617 time $spec_name --format html "$@" > $out
618 set -o errexit
619
620 echo
621 echo "Wrote $out"
622
623 # NOTE: This IGNORES the exit status.
624}
625
626# TODO:
627# - Put these tests in the CI
628# - Import smoosh spec tests into the repo, with 'test/smoosh.sh'
629
630smoosh-html() {
631 ### Run by devtools/release.sh
632 _one-html smoosh "$@"
633}
634
635smoosh-hang-html() {
636 ### Run by devtools/release.sh
637 _one-html smoosh-hang "$@"
638}
639
640html-demo() {
641 ### Test for --format html
642
643 local out=_tmp/spec/demo.html
644 builtin-special --format html "$@" > $out
645
646 echo
647 echo "Wrote $out"
648}
649
650#
651# Hay is part of the YSH suite
652#
653
654hay() {
655 run-file hay "$@"
656}
657
658hay-isolation() {
659 run-file hay-isolation "$@"
660}
661
662hay-meta() {
663 run-file hay-meta "$@"
664}
665
666#
667# YSH
668#
669
670ysh-convert() {
671 run-file ysh-convert "$@"
672}
673
674ysh-completion() {
675 run-file ysh-completion "$@"
676}
677
678ysh-stdlib() {
679 run-file ysh-stdlib "$@"
680}
681
682ysh-stdlib-2() {
683 run-file ysh-stdlib-2 "$@"
684}
685
686ysh-stdlib-args() {
687 run-file ysh-stdlib-args "$@"
688}
689
690ysh-stdlib-testing() {
691 run-file ysh-stdlib-testing "$@"
692}
693
694ysh-stdlib-synch() {
695 run-file ysh-stdlib-synch "$@"
696}
697
698ysh-source() {
699 run-file ysh-source "$@"
700}
701
702ysh-usage() {
703 run-file ysh-usage "$@"
704}
705
706ysh-unicode() {
707 run-file ysh-unicode "$@"
708}
709
710ysh-bin() {
711 run-file ysh-bin "$@"
712}
713
714ysh-dict() {
715 run-file ysh-dict "$@"
716}
717
718ysh-list() {
719 run-file ysh-list "$@"
720}
721
722ysh-place() {
723 run-file ysh-place "$@"
724}
725
726ysh-prompt() {
727 run-file ysh-prompt "$@"
728}
729
730ysh-assign() {
731 run-file ysh-assign "$@"
732}
733
734ysh-augmented() {
735 run-file ysh-augmented "$@"
736}
737
738ysh-blocks() {
739 run-file ysh-blocks "$@"
740}
741
742ysh-bugs() {
743 run-file ysh-bugs "$@"
744}
745
746ysh-builtins() {
747 run-file ysh-builtins "$@"
748}
749
750ysh-builtin-module() {
751 run-file ysh-builtin-module "$@"
752}
753
754ysh-builtin-eval() {
755 run-file ysh-builtin-eval "$@"
756}
757
758# Related to errexit-oil
759ysh-builtin-error() {
760 run-file ysh-builtin-error "$@"
761}
762
763ysh-builtin-meta() {
764 run-file ysh-builtin-meta "$@"
765}
766
767ysh-builtin-process() {
768 run-file ysh-builtin-process "$@"
769}
770
771ysh-builtin-shopt() {
772 run-file ysh-builtin-shopt "$@"
773}
774
775ysh-case() {
776 run-file ysh-case "$@"
777}
778
779ysh-command-sub() {
780 run-file ysh-command-sub "$@"
781}
782
783ysh-demo() {
784 run-file ysh-demo "$@"
785}
786
787ysh-expr() {
788 run-file ysh-expr "$@"
789}
790
791ysh-int-float() {
792 run-file ysh-int-float "$@"
793}
794
795ysh-expr-bool() {
796 run-file ysh-expr-bool "$@"
797}
798
799ysh-expr-arith() {
800 run-file ysh-expr-arith "$@"
801}
802
803ysh-expr-compare() {
804 run-file ysh-expr-compare "$@"
805}
806
807ysh-expr-sub() {
808 run-file ysh-expr-sub "$@"
809}
810
811ysh-cmd-lang() {
812 run-file ysh-cmd-lang "$@"
813}
814
815ysh-for() {
816 run-file ysh-for "$@"
817}
818
819ysh-methods() {
820 run-file ysh-methods "$@"
821}
822
823ysh-func() {
824 run-file ysh-func "$@"
825}
826
827ysh-func-builtin() {
828 run-file ysh-func-builtin "$@"
829}
830
831ysh-funcs-external() {
832 run-file ysh-funcs-external "$@"
833}
834
835ysh-interactive() {
836 run-file ysh-interactive "$@"
837}
838
839ysh-json() {
840 run-file ysh-json "$@"
841}
842
843ysh-keywords() {
844 run-file ysh-keywords "$@"
845}
846
847ysh-multiline() {
848 run-file ysh-multiline "$@"
849}
850
851ysh-options() {
852 run-file ysh-options "$@"
853}
854
855ysh-options-assign() {
856 run-file ysh-options-assign "$@"
857}
858
859ysh-proc() {
860 run-file ysh-proc "$@"
861}
862
863ysh-regex() {
864 run-file ysh-regex "$@"
865}
866
867ysh-regex-api() {
868 run-file ysh-regex-api "$@"
869}
870
871ysh-reserved() {
872 run-file ysh-reserved "$@"
873}
874
875ysh-scope() {
876 run-file ysh-scope "$@"
877}
878
879ysh-slice-range() {
880 run-file ysh-slice-range "$@"
881}
882
883ysh-string() {
884 run-file ysh-string "$@"
885}
886
887ysh-special-vars() {
888 run-file ysh-special-vars "$@"
889}
890
891ysh-tuple() {
892 run-file ysh-tuple "$@"
893}
894
895ysh-var-sub() {
896 run-file ysh-var-sub "$@"
897}
898
899ysh-with-sh() {
900 run-file ysh-with-sh "$@"
901}
902
903ysh-word-eval() {
904 run-file ysh-word-eval "$@"
905}
906
907ysh-xtrace() {
908 run-file ysh-xtrace "$@"
909}
910
911ysh-user-feedback() {
912 run-file ysh-user-feedback "$@"
913}
914
915ysh-builtin-ctx() {
916 run-file ysh-builtin-ctx "$@"
917}
918
919ysh-builtin-error() {
920 run-file ysh-builtin-error "$@"
921}
922
923ysh-builtin-help() {
924 run-file ysh-builtin-help "$@"
925}
926
927ysh-dev() {
928 run-file ysh-dev "$@"
929}
930
931ysh-printing() {
932 run-file ysh-printing "$@"
933}
934
935
936#
937# More OSH
938#
939
940nix-idioms() {
941 run-file nix-idioms "$@"
942}
943
944zsh-idioms() {
945 run-file zsh-idioms "$@"
946}
947
948ble-idioms() {
949 sh-spec spec/ble-idioms.test.sh \
950 $BASH $ZSH $MKSH $BUSYBOX_ASH $OSH_LIST "$@"
951}
952
953ble-features() {
954 run-file ble-features "$@"
955}
956
957toysh() {
958 run-file toysh "$@"
959}
960
961toysh-posix() {
962 run-file toysh-posix "$@"
963}
964
965run-task "$@"