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

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