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

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