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

1097 lines, 436 significant
1#!/usr/bin/env bash
2#
3# Usage:
4# test/ysh-runtime-errors.sh <function name>
5
6set -o nounset
7set -o pipefail
8set -o errexit
9
10source test/common.sh
11source test/sh-assert.sh # _assert-sh-status
12
13#
14# Cases
15#
16
17test-no-typed-args() {
18 _ysh-error-X 2 'echo (42)'
19 _ysh-error-X 2 'echo { echo hi }'
20
21 # Hm write could be J8 notation? like json8 write (x)?
22 _ysh-error-X 2 'write (42)'
23
24 _ysh-error-X 2 'true (42)'
25 _ysh-error-X 2 'false { echo hi }'
26
27 _ysh-error-X 2 'test x (42)'
28 _ysh-error-X 2 'test x { echo hi }'
29}
30
31test-undefined-vars() {
32 _ysh-error-1 'echo hi; const y = 2 + x + 3'
33 _ysh-error-1 'if (x) { echo hello }'
34 _ysh-error-1 'if (${x}) { echo hi }'
35
36 # BareDecl and regex
37 _ysh-error-1 'const x = / @undef /; echo hi'
38
39 _ysh-error-1 'var x = undef; echo $x' # VarDecl
40 _ysh-error-1 'setvar a = undef' # PlaceMutation
41}
42
43test-word-eval-with-ysh-data() {
44 _ysh-expr-error 'var d = {}; echo ${d:-}'
45
46 _osh-error-X 3 'var d = {}; echo ${#d}'
47
48 _osh-error-X 3 'var d = {}; echo ${d[0]}'
49
50 _osh-error-X 3 'var d = {}; echo ${d[@]:1:3}'
51
52 _osh-error-X 3 'var d = {}; echo ${!d}'
53
54 _osh-error-X 3 'var d = {}; echo ${!d[@]}'
55
56 _osh-error-X 3 'var d = {}; echo ${d#prefix}'
57
58 _osh-error-X 3 'var d = {}; echo ${d//a/b}'
59
60}
61
62test-ysh-word-eval() {
63 # Wrong sigil
64 _ysh-expr-error 'echo $[maybe("foo")]'
65
66 # Wrong sigil
67 _ysh-expr-error 'source $LIB_YSH/math.ysh; echo $[identity({key: "val"})]'
68
69 # this should be consistent
70 _ysh-expr-error 'source $LIB_YSH/math.ysh; write -- @[identity([{key: "val"}])]'
71
72 _ysh-should-run 'var x = [1, 2]; write @x'
73
74 # errors in items
75 _ysh-expr-error 'var x = [3, {}]; write @x'
76
77 _ysh-expr-error 'var x = [3, {}]; write @[x]'
78
79 # errors at top level
80 _ysh-expr-error 'var x = /d+/; write @x'
81
82 _ysh-expr-error 'var x = /d+/; write @[x]'
83}
84
85# Continuation of above
86test-cannot-stringify-list() {
87 # List can't be stringified
88 _ysh-expr-error 'var mylist = [1,2,3]; write $mylist'
89 _ysh-expr-error 'var mylist = [1,2,3]; write $[mylist]'
90
91 _ysh-should-run '= str(/d+/)'
92
93 _ysh-expr-error '= str([1,2])'
94 _ysh-expr-error '= str({})'
95
96 # Not sure if I like this join() behavior
97 _ysh-should-run '= join([true, null])'
98
99 # Bad error
100 _ysh-expr-error '= join([[1,2], null])'
101}
102
103test-ysh-expr-eval() {
104 _ysh-expr-error 'echo $[42 / 0 ]'
105
106 _ysh-expr-error 'var d = {}; var item = d->nonexistent'
107
108 _ysh-expr-error 'var d = {}; var item = d["nonexistent"]'
109
110 _ysh-expr-error 'var a = []; setvar item = a[1]'
111
112 _ysh-expr-error 'const x = 42 / 0'
113
114 # command sub as part of expression retains its exit code
115 _ysh-error-1 'var x = "z" ++ $(false)'
116 #_ysh-error-1 'var x = "z" ++ $(exit 42)'
117
118 _ysh-expr-error 'case (42 / 0) { * { echo hi } }; echo OK'
119
120 _ysh-expr-error 'var d = {}; for x in $[d->zzz] { echo hi }'
121
122 # Wrong index type
123 _ysh-expr-error 'var d = {}; setvar d[42] = 3'
124 _ysh-expr-error 'var L = []; setvar L["key"] = 3'
125
126 # Index out of bounds
127 _ysh-expr-error 'var L = []; setvar L[99] = 3'
128 _ysh-expr-error 'var L = []; pp (L[-99])'
129}
130
131test-ysh-expr-eval-2() {
132 _ysh-expr-error 'var L = []; var slice = L["foo": "bar"]'
133
134 _ysh-expr-error '= 3 < true'
135 _ysh-expr-error '= "a" < "b"'
136
137 _ysh-expr-error 'var key = 42; var d = {[key]: 3}'
138
139 _ysh-expr-error 'var d = {}; var a = d.a'
140 _ysh-expr-error 'var d = []; var a = d.a'
141
142 _ysh-expr-error '= 3 ** -2'
143 _ysh-expr-error '= 3.2 ** 2'
144
145 _ysh-expr-error '= - "foo"'
146}
147
148test-user-reported() {
149 #_ysh-error-1 'echo'
150
151 # Issue #1118
152 # Some tests became test/parse-errors.sh
153
154
155 # len(INTEGER) causes the same problem
156 _ysh-expr-error '
157 var snippets = [{status: 42}]
158 for snippet in (snippets) {
159 if (len(42)) {
160 echo hi
161 }
162 }
163 '
164
165 # len(INTEGER) causes the same problem
166 _ysh-expr-error '
167 var count = 0
168
169 # The $ causes a weird error
170 while (count < len(count)) {
171 setvar count += 1
172 }
173 '
174}
175
176test-fallback-locations() {
177 # Melvin noticed bug here
178 _ysh-expr-error 'if (len(42)) { echo hi }'
179
180 # Be even more specific
181 _ysh-expr-error 'if (1 + len(42)) { echo hi }'
182
183 # From Aidan's PR -- redefinition
184 _ysh-error-1 'const f = 42; func f() { echo hi }'
185
186 # ForEach shell
187 _ysh-expr-error 'for x in $[2 + len(42)] { echo hi }'
188
189 # ForEach YSH
190 _ysh-expr-error 'for x in (len(42)) { echo hi }'
191
192 _ysh-expr-error 'while (len(42)) { echo hi }'
193
194 _ysh-expr-error 'case (len(42)) { pat { echo argument } }'
195 _ysh-expr-error 'case (42) { (len(42)) { echo arm } }'
196
197 _ysh-expr-error 'case "$[len(42)]" in pat) echo hi ;; esac'
198
199 _ysh-expr-error 'var x = 3 + len(42)'
200 _ysh-expr-error 'const x = 3 + len(42)'
201 _ysh-expr-error 'setvar x = 3 + len(42)'
202
203 _ysh-expr-error 'setvar x = "s" + 5'
204 _ysh-expr-error 'while ("s" + 5) { echo yes } '
205
206 #_ysh-expr-error 'func f(x) { return (x) }; var x = f([1,2])(3); echo $x'
207
208 # Really bad one
209 _ysh-expr-error 'func f(x) { return (x) }; var x = f([1,2])[1](3); echo $x'
210}
211
212test-EvalExpr-calls() {
213 ### Test everywhere expr_ev.EvalExpr() is invoked
214
215 _ysh-expr-error 'json write (len(42))'
216
217 _ysh-expr-error '= len(42)'
218 _ysh-expr-error 'call len(42)'
219
220 _ysh-expr-error 'echo $[len(42)]'
221 _ysh-expr-error 'echo $[len(z = 42)]'
222
223 _ysh-expr-error 'echo @[len(42)]'
224 _ysh-expr-error 'echo @[len(z = 42)]'
225
226 _ysh-expr-error 'const x = len(42)'
227 _ysh-expr-error 'setvar x += len(42)'
228
229 _ysh-expr-error '
230 var d = {}
231 setvar d[len(42)] = "foo"
232 '
233
234 _ysh-error-X 2 '
235 var d = {}
236 setvar len(42).z = "foo"
237 '
238
239 _ysh-expr-error '
240 hay define Package
241 Package foo {
242 x = len(42)
243 }
244 '
245
246 _ysh-expr-error 'if (len(42)) { echo hi }'
247
248 _ysh-expr-error 'while (len(42)) { echo hi }'
249
250 _ysh-expr-error 'for x in (len(42)) { echo $x }'
251}
252
253
254test-hay() {
255 _ysh-error-X 127 '
256hay define package user TASK
257
258hay eval :result {
259 package foo {
260 # commands can be run while evaluating
261 oops
262 }
263
264 bad 2
265}
266'
267}
268
269
270test-hay-osh() {
271 # forgot parse_brace
272 _osh-error-X 2 '
273hay define package TASK
274
275package foo {
276 version = 1
277}
278'
279
280 # forgot parse_equals
281 _osh-error-X 127 '
282shopt --set parse_brace
283
284hay define package TASK
285
286hay eval :result {
287 package foo {
288 version = 1
289 }
290}
291'
292}
293
294test-eggex() {
295 # forgot parse_brace
296 _ysh-should-run ' = / [ \x00 \xff ] /'
297 _ysh-should-run ' = / [ \x00-\xff ] /'
298
299 # Shouldn't be in strings
300
301 cat >_tmp/test-eggex.txt <<'EOF'
302= / [ $'\x00 \xff' ] /
303EOF
304
305 _ysh-error-1 "$(cat _tmp/test-eggex.txt)"
306
307 _ysh-should-run ' = / [ \u{0} ] /'
308 _ysh-should-run ' = / [ \u{0}-\u{1} ] /'
309
310 # Too high
311 _ysh-error-1 'var x =/ [ \u{80} ] /; echo $x'
312 _ysh-error-1 'var x = / [ \u{7f}-\u{80} ] /; echo $x'
313
314 # Now test special characters
315 _ysh-should-run "$(cat <<'EOF'
316= / [ \\ '^-]' 'abc' ] /
317EOF
318)"
319
320 # Special chars in ranges are disallowed for simplicity
321 _ysh-error-1 "var x = / [ a-'^' ] /; echo \$x"
322 _ysh-error-1 "var x = / [ '-'-z ] /; echo \$x"
323 _ysh-error-1 "var x = / [ ']'-z ] /; echo \$x"
324
325 # TODO: Disallow this. It translates to [^], which is a syntax error in
326 # egrep "Unmatched [ or [^"
327 _ysh-should-run "var x = / ['^'] /; echo \$x"
328
329 _ysh-expr-error '
330 var i = 42
331 = / @i / # splice object of wrong type
332 '
333
334 _ysh-expr-error '
335 var i = 42
336 = / [a @i] / # char class splice object of wrong type
337 '
338}
339
340test-eggex-2() {
341 _ysh-should-run "var sq = / 'foo'+ /"
342
343 _ysh-should-run "$(cat <<'EOF'
344 var sq = / ('foo')+ /
345 echo $sq
346
347 var sq2 = / <capture 'foo'>+ /
348 echo $sq2
349EOF
350)"
351
352 _ysh-error-1 '
353 var literal = "foo"
354 var svs = / @literal+ /
355 echo $svs
356 '
357}
358
359test-eggex-api() {
360 _ysh-expr-error '= _group(0)' # No groups
361
362 _ysh-expr-error 'if ("foo" ~ /[a-z]/) { echo $[_group(1)] }'
363 _ysh-expr-error 'if ("foo" ~ /[a-z]/) { echo $[_group("name")] }'
364
365 # ERE
366 _ysh-expr-error 'if ("foo" ~ "[a-z]") { echo $[_group(1)] }'
367 _ysh-expr-error 'if ("foo" ~ "[a-z]") { echo $[_group("name")] }'
368
369 _ysh-expr-error '= _group("foo")' # No such group
370}
371
372test-eggex-convert-func() {
373
374 _ysh-should-run '= / <capture d+ as month: int> /'
375 _ysh-should-run '= / <capture d+: int> /'
376 _ysh-should-run '= / <capture d+> /'
377
378 # bad convert func
379 _ysh-expr-error '= / <capture d+ as month: BAD> /'
380 _ysh-expr-error '= / <capture d+: BAD> /'
381
382 # type error calling convert func (evalExpr)
383 _ysh-expr-error 'var pat = / <capture d+: evalExpr> /; var m = "10" => search(pat) => group(1)'
384}
385
386test-int-convert() {
387 _ysh-expr-error '= int({})'
388 _ysh-expr-error '= int([])'
389 _ysh-expr-error '= int("foo")'
390 _ysh-expr-error '= int(len)'
391 _ysh-expr-error '= int("foo" => startsWith)'
392
393 _ysh-expr-error '= int(NAN)'
394 _ysh-expr-error '= int(-INFINITY)'
395}
396
397test-float-convert() {
398 _ysh-expr-error '= float({})'
399 _ysh-expr-error '= float([])'
400 _ysh-expr-error '= float("foo")'
401 _ysh-expr-error '= float(len)'
402 _ysh-expr-error '= float("foo"->startswith)'
403}
404
405test-str-convert() {
406 _ysh-expr-error '= str({})'
407 _ysh-expr-error '= str([])'
408 _ysh-expr-error '= str(len)'
409 _ysh-expr-error '= str("foo"->startswith)'
410}
411
412test-list-convert() {
413 _ysh-expr-error '= list(1)'
414 _ysh-expr-error '= list(len)'
415 _ysh-expr-error '= list("foo"->startswith)'
416}
417
418test-dict-convert() {
419 _ysh-expr-error '= dict(1)'
420 _ysh-expr-error '= dict("foo")'
421 _ysh-expr-error '= dict(len)'
422 _ysh-expr-error '= dict("foo"->startswith)'
423 _ysh-expr-error '= dict([["too", "many", "parts"]])'
424}
425
426test-proc-error-locs() {
427
428 # positional
429 _ysh-expr-error '
430 var d = [1]
431
432 func f(a=1, x=d[2]) {
433 echo hi
434 }
435 '
436
437 _ysh-expr-error '
438 var d = [1]
439
440 func f(; n=1, m=d[2]) {
441 echo hi
442 }
443 '
444}
445
446test-func-error-locs() {
447 # free funcs
448 _ysh-expr-error '= join(["foo", "bar"], " ", 99)' # too many args
449 _ysh-expr-error '= int()' # not enough args
450 _ysh-expr-error '= str({})' # wrong type
451
452 # bound funcs
453 _ysh-expr-error '= "foo"->startswith("f", "o")' # too many args
454 _ysh-expr-error '= "foo"->startswith()' # not enough args
455 _ysh-expr-error '= "foo"->startswith(1)' # wrong type
456
457 _ysh-expr-error '
458 func f(x) {
459 return (x)
460 }
461 = f()
462 '
463}
464
465test-attr-error-locs() {
466 _ysh-expr-error '= {}.key'
467 _ysh-expr-error '= {}->method'
468
469 _ysh-expr-error 'var obj = Object(null, {}); = obj.attr'
470 _ysh-expr-error 'var obj = Object(null, {}); = obj->method'
471
472}
473
474# TODO:
475test-error-loc-bugs() {
476 _ysh-expr-error '
477func id(x) {
478 return (x)
479}
480
481#pp test_ (id(len(42)))
482
483# This should point at ( in len, not id(
484pp test_ (len(id(42)))
485 '
486
487 _ysh-expr-error '
488var methods = {}
489
490# Should point at methods, not {}
491var o = Object(methods, {})
492 '
493}
494
495test-var-decl() {
496 _ysh-expr-error 'var x, y = 1, 2, 3'
497 _ysh-expr-error 'setvar x, y = 1, 2, 3'
498}
499
500test-const-decl() {
501 _ysh-error-1 'const x = {}; const x = {};'
502 _ysh-error-1 'const x; const x;'
503}
504
505test-proc-defaults() {
506
507 # should be string
508 _ysh-expr-error 'proc p(word=42) { echo }'
509 _ysh-expr-error 'proc p(word=null) { echo }'
510
511 # should be ^() or null
512 _ysh-expr-error 'proc p( ; ; ; block="str") { echo }'
513 _ysh-expr-error 'proc p( ; ; ; block=[]) { echo }'
514
515 _ysh-should-run 'proc p( ; ; ; block=^(echo hi)) { true }'
516 _ysh-should-run 'proc p( ; ; ; block=null) { true }'
517
518 # divide by zero
519 _ysh-expr-error 'proc p(word; t=42/0) { echo }'
520
521 _ysh-error-X 1 'proc p(word; t=f()) { echo }'
522
523 _ysh-error-X 1 'proc p(word; t=42; named=undef) { echo }'
524
525 _ysh-error-X 1 'proc p(word; t=42; named=43; block=ZZ) { echo }'
526
527 _ysh-should-run '
528 proc p(word="yo"; t=42; named=43; block=null) {
529 #echo $word $t $named $block
530 echo $word $t $block
531 }
532 p
533 '
534}
535
536test-proc-passing() {
537 # Too few words
538 _ysh-error-X 3 '
539 proc p(a, b) { echo }
540 p a
541 '
542
543 # Too many words
544 _ysh-error-X 3 '
545 proc p(a, b) { echo }
546 p AA b c DD
547 '
548
549 # Too few typed
550 _ysh-error-X 3 '
551 proc p( ; a, b) { echo }
552 p (42)
553 '
554
555 # Too many words
556 _ysh-error-X 3 '
557 proc p( ; a, b) { echo }
558 p (42, 43, 44, 45)
559 '
560
561 _ysh-expr-error '
562 proc p(; a, b) {
563 echo $a - $b -
564 }
565 p (...[1, 2])
566 p (...3)
567 '
568
569 # positional: rest args and spread
570 _ysh-should-run '
571 proc p(; a, ...b) {
572 echo $a - @b -
573 }
574 p (1, 2, 3)
575
576 var x = [4, 5, 6]
577 p (...x)
578 '
579
580 # named: splat
581 _ysh-should-run '
582 proc myproc (; p ; a, b) {
583 echo "$p ; $a $b"
584 }
585 var kwargs = {a: 42, b: 43}
586 myproc (99; ...kwargs)
587 '
588
589 # named: rest args
590 _ysh-should-run '
591 proc myproc (; p ; a, b, ...named) {
592 = p
593 = a
594 = b
595 = named
596 }
597 var kwargs = {a: 42, b: 43, c:44}
598 myproc (99; ...kwargs)
599 '
600}
601
602# TODO: improve locations for all of these
603test-proc-missing() {
604 # missing word param
605 _ysh-error-X 3 '
606 proc myproc (w) {
607 = w
608 }
609 myproc
610 '
611
612 # missing typed param
613 _ysh-error-X 3 '
614 proc myproc (w; t1, t2) {
615 = w
616 = t
617 }
618 myproc foo (42)
619 '
620
621 # missing named param
622 _ysh-error-X 3 '
623 proc myproc (; p ; a, b) {
624 echo "$p ; $a $b"
625 }
626 myproc (99, b=3)
627 '
628
629 # missing named param with semicolon
630 _ysh-error-X 3 '
631 proc myproc (; p ; a, b) {
632 echo "$p ; $a $b"
633 }
634 myproc (99; b=3)
635 '
636
637 # missing block param
638 _ysh-error-X 3 '
639 proc myproc (w; p ; a, b; block) {
640 = block
641 }
642 myproc foo (99, a=1, b=2)
643 '
644}
645
646test-proc-extra() {
647
648 # extra word
649 _ysh-error-X 3 '
650 proc myproc () {
651 echo hi
652 }
653 myproc foo
654 '
655
656 # extra positional
657 _ysh-error-X 3 '
658 proc myproc (w) {
659 echo hi
660 }
661 myproc foo (42)
662 '
663
664 # extra named
665 _ysh-error-X 3 '
666 proc myproc (w; p) {
667 echo hi
668 }
669 myproc foo (42; named=1)
670 '
671
672 # extra block. TODO: error is about typed args
673 _ysh-error-X 3 '
674 proc myproc (w; p; n) {
675 echo hi
676 }
677 myproc foo (42; n=1) { echo hi }
678 '
679}
680
681
682test-func-defaults() {
683 _ysh-error-X 1 'func f(a=ZZ) { echo }'
684 _ysh-error-X 1 'func f(a; named=YY) { echo }'
685
686 _ysh-expr-error 'func f(a=[]) { echo }'
687 _ysh-expr-error 'func f(; d={a:3}) { echo }'
688}
689
690test-func-missing() {
691 _ysh-expr-error '
692 func f(x, y) {
693 echo "$x $y"
694 }
695 call f(1)
696 '
697
698 _ysh-expr-error '
699 func f(x, y; z) {
700 echo "$x $y"
701 }
702 call f(3, 4)
703 '
704
705}
706
707test-func-extra() {
708 _ysh-expr-error '
709 func f() {
710 echo "$x $y"
711 }
712 call f(42) # extra pos
713 '
714
715 _ysh-expr-error '
716 func f() {
717 echo "$x $y"
718 }
719 call f(; x=32) # extra named
720 '
721}
722
723test-func-passing() {
724 # rest can't have default -- parse error
725 _ysh-error-X 2 '
726 func f(...rest=3) {
727 return (42)
728 }
729 '
730
731 _ysh-expr-error '
732 func f(a, b) {
733 echo "$a -- $b"
734 }
735 = f()
736 '
737
738 _ysh-expr-error '
739 func f(a, b) {
740 echo "$a -- $b"
741 }
742 = f(...[1, 2])
743 = f(...3)
744 '
745
746 # rest args and splat
747 _ysh-should-run '
748 func f(a, ...b) {
749 echo $a - @b -
750 }
751 = f(1, 2, 3)
752
753 var x = [4, 5, 6]
754 = f(...x)
755 '
756
757 # Named splat
758 _ysh-should-run '
759 func f(p ; a, b) {
760 echo "$p ; $a $b"
761 }
762 var kwargs = {a: 42, b: 43, c: 44}
763 = f(99; ...kwargs)
764 '
765}
766
767test-read-builtin() {
768 # no typed args
769 _ysh-error-X 2 'echo hi | read (&x)'
770 _ysh-error-X 2 'echo hi | read --all x y'
771 _ysh-error-X 2 'echo hi | read --line x y'
772}
773
774test-equality() {
775 _ysh-expr-error '
776 = ^[42] === ^[43]
777 '
778
779 _ysh-expr-error '
780 = ^(echo hi) === ^(echo yo)
781 '
782
783 return
784
785 # Hm it's kind of weird you can do this -- it's False
786 _ysh-expr-error '
787 = ^[42] === "hi"
788 '
789}
790
791test-float-equality() {
792 _ysh-expr-error '
793var x = 1
794pp test_ (42.0 === x)'
795
796 _ysh-expr-error 'pp test_ (2.0 === 1.0)'
797}
798
799test-place() {
800 _ysh-expr-error '
801 var a = null
802 var p = &a
803 call p->setValue() # 1 arg
804 '
805
806 _ysh-expr-error '
807 var a = null
808 var p = &a
809 call p->setValue(3, 4)
810 '
811
812 _ysh-error-1 '
813 func f() {
814 var s = "foo"
815 return (&s)
816
817 }
818 var p = f()
819 call p->setValue(3)
820 '
821
822}
823
824test-json() {
825 _ysh-expr-error 'json write'
826 _ysh-expr-error 'json write (42, 43)'
827
828 _ysh-error-X 2 'json read zz'
829 _ysh-error-X 2 'json read yy zz'
830 _ysh-error-X 3 'json read (&x, 43)'
831}
832
833# For decoding errors, see data_lang/j8-errors.sh
834
835test-error-builtin() {
836
837 _ysh-error-X 2 'error '
838 _ysh-error-X 2 'error --'
839
840 # These are OK
841 _ysh-error-X 10 'error -- oops'
842 _ysh-error-X 10 'error oops'
843
844 _ysh-error-X 99 'error oops (code=99)'
845}
846
847test-fat-arrow() {
848 #_ysh-should-run '= "str" -> upper()'
849 _ysh-should-run '= "str" => upper()'
850
851 _ysh-expr-error '= "str" -> bad()'
852
853 # We get 'Undefined variable' error because of the fallback, could make it better
854 _ysh-error-X 1 '= "str" => bad()'
855
856 _ysh-should-run '= ["3", "4"] => join("/")'
857
858 # Good error message for method chaining
859 _ysh-expr-error '= "badstring" => join("/")'
860
861
862 # float has no ExactlyEqual
863 _ysh-error-X 3 "= [1.0, 2.0] => indexOf(3.14)"
864
865 # Invalid type
866 _ysh-expr-error '
867 var myint = 42
868 = "badstring" => myint("/")
869 '
870}
871
872test-method-type-errors() {
873 _ysh-expr-error '= "hi" => search(42)'
874 _ysh-expr-error '= "hi" => leftMatch(42)'
875 _ysh-expr-error "var m = 'hi' => leftMatch(/'hi'/); = m => group(3.14)"
876}
877
878test-str-replace() {
879 # Some ad hoc tests - spec tests cover this
880 if false; then
881 _ysh-should-run '= "hi" => replace("i", "b")'
882 _ysh-should-run '= "hi" => replace(/[a-z]/, "b")'
883 _ysh-should-run '= "hi" => replace(/[a-z]/, "b", count=1)'
884 _ysh-should-run '= "foo42" => replace(/<capture d+>/, ^"hi $1")'
885 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^"hi $num")'
886 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^"hi ${num}")'
887 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^"hi $[num]")'
888 # test out globals - is this desirable?
889 _ysh-should-run '= "foo42" => replace(/<capture d+ as num>/, ^["hi $[num] $PATH"])'
890 # -1 is replace all
891 _ysh-should-run '= "foo" => replace("o", "x", count=-1)'
892 _ysh-should-run '= "foo" => replace("o", "x", count=-2)'
893 fi
894 # Replace empty string? Weird Python behavior
895 _ysh-should-run '= "foo" => replace("", "-")'
896 _ysh-should-run '= "foo" => replace("", "-", count=2)'
897
898 # Use Expr with string
899 _ysh-should-run '= "foo" => replace("o", ^"-")'
900 # $0 is regular $0 here
901 _ysh-should-run '= "foo" => replace("o", ^"-$0")'
902
903 # Hm $0 isn't set?
904 _ysh-should-run '= "foo" => replace(/[o]/, ^"-$0")'
905 # Here $1 is set
906 _ysh-should-run '= "foo" => replace(/<capture [o]>/, ^"-$1")'
907 _ysh-should-run '= "foo" => replace(/<capture [o] as letter>/, ^"-$letter")'
908
909 # Invalid arguments
910 _ysh-expr-error '= "foo" => replace(42, "x")'
911 _ysh-expr-error '= "foo" => replace("x", 42)'
912
913 # Invalid evaluation
914 _ysh-expr-error '= "foo" => replace("x", ^[42])'
915}
916
917test-remainder() {
918 # second number can't be negative
919 _ysh-expr-error '= 5 % -3'
920 _ysh-expr-error 'var x = 5; setvar x %= -3'
921}
922
923test-append-usage-error() {
924 _ysh-should-run 'append x ([])'
925
926 _ysh-expr-error 'append'
927
928 _ysh-expr-error 'append x' # Too few
929
930 _ysh-expr-error 'append x ([], [])' # Too many
931}
932
933# Bad error location
934test-try-usage-error() {
935 _ysh-error-X 2 '
936var s = "README"
937case (s) {
938 README { echo hi }
939}
940echo hi
941
942try myproc
943if (_status !== 0) {
944 echo failed
945}
946'
947}
948
949test-trim-utf8-error() {
950 _ysh-error-here-X 3 << 'EOF'
951 var badUtf = b'\yF9'
952
953 # error is missed
954 call " a$[badUtf]b " => trim()
955 echo status=$_status
956
957 # error is found
958 call "$[badUtf]b " => trim()
959EOF
960}
961
962test-setglobal() {
963 _ysh-should-run '
964var a = [0]
965setglobal a[1-1] = 42
966pp test_ (a)
967 '
968
969 _ysh-expr-error '
970var a = [0]
971setglobal a[a.bad] = 42
972pp test_ (a)
973 '
974
975 _ysh-should-run '
976var d = {e:{f:0}}
977setglobal d.e.f = 42
978pp test_ (d)
979setglobal d.e.f += 1
980pp test_ (d)
981 '
982}
983
984test-assert() {
985 _ysh-expr-error 'assert [0.0]'
986 _ysh-expr-error 'assert [3 > 4]'
987
988 _ysh-expr-error 'assert (0)'
989 _ysh-expr-error 'assert (null === 42)'
990
991 _ysh-expr-error 'assert [null === 42]'
992
993 # One is long
994 _ysh-expr-error 'assert [null === list(1 ..< 50)]'
995
996 # Both are long
997 _ysh-expr-error 'assert [{k: list(3 ..< 40)} === list(1 ..< 50)]'
998}
999
1000test-pp() {
1001 _ysh-expr-error 'pp (42/0)'
1002
1003 # Multiple lines
1004 _ysh-should-run 'pp [42
1005/0]'
1006
1007 _ysh-expr-error 'pp [5, 6]'
1008
1009 _ysh-should-run 'pp (42)'
1010 _ysh-should-run 'var x = 42; pp (x)'
1011 _ysh-should-run '
1012var x = 42;
1013pp [x]'
1014
1015 _ysh-should-run '
1016var x = list(1 ..< 50);
1017pp [x]'
1018}
1019
1020test-module() {
1021 # no args
1022 _ysh-error-X 2 'use spec/testdata/module2/util.ysh; util'
1023
1024 # bad arg
1025 _ysh-error-X 2 'use spec/testdata/module2/util.ysh; util zz'
1026
1027 # proc with bad args
1028 _ysh-error-X 3 'use spec/testdata/module2/util2.ysh; util2 echo-args'
1029
1030 # malformed Obj
1031 _ysh-error-X 3 'use spec/testdata/module2/util2.ysh; util2 badObj otherproc'
1032}
1033
1034test-required-blocks() {
1035
1036 # These are procs, which normally give usage errors
1037 # The usage error prints the builtin name
1038 #
1039 # Funcs give you type errors though? Is that inconsistent?
1040
1041 _ysh-error-X 2 'shvar'
1042 _ysh-error-X 2 'push-registers'
1043
1044 _ysh-error-X 2 'redir'
1045 _ysh-error-X 2 'redir (42)'
1046 _ysh-error-X 2 'hay eval :myvar'
1047 _ysh-error-X 2 'hay eval :myvar (42)'
1048 _ysh-error-X 2 'try'
1049 _ysh-error-X 2 'ctx push ({})'
1050
1051 _ysh-error-X 2 'fork'
1052 _ysh-error-X 2 'forkwait'
1053
1054 # OK this is a type error
1055 _ysh-error-X 3 'forkwait ( ; ; 42)'
1056
1057 _ysh-error-X 2 'haynode Foo'
1058
1059 # Hm this isn't a usage error
1060 _ysh-error-X 3 'haynode Foo (42)'
1061
1062 # This neither
1063 _ysh-error-X 3 'haynode Foo ( ; ; 42)'
1064
1065 _ysh-should-run 'haynode Foo a { echo hi }'
1066}
1067
1068test-obj-methods() {
1069 _ysh-error-X 3 'var o = Object(null, {}); pp test_ (o[1])'
1070 _ysh-error-X 3 'var o = Str; pp test_ (Str[1])'
1071
1072 _ysh-error-X 3 'pp test_ (Bool[Bool])'
1073 _ysh-error-X 3 'pp test_ (Dict[Bool])'
1074 _ysh-error-X 3 'pp test_ (List[Str, Bool])'
1075
1076 # break invariants
1077 _ysh-error-X 3 'call propView(List)->erase("name"); pp test_ (List[Str])'
1078 _ysh-error-X 3 'call propView(Str)->erase("name"); pp test_ (List[Str])'
1079
1080 _ysh-error-X 3 'pp test_ (List[Str, 3])'
1081}
1082
1083soil-run-py() {
1084 run-test-funcs
1085}
1086
1087soil-run-cpp() {
1088 local ysh=_bin/cxx-asan/ysh
1089 ninja $ysh
1090 YSH=$ysh run-test-funcs
1091}
1092
1093run-for-release() {
1094 run-other-suite-for-release ysh-runtime-errors run-test-funcs
1095}
1096
1097"$@"