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

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