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

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