diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/drivers/runtime_image_test.py | 1 | ||||
| -rw-r--r-- | tests/programs/gc.zc | 2 | ||||
| -rw-r--r-- | tests/programs/trace.expected | 9 | ||||
| -rw-r--r-- | tests/programs/trace.zc | 36 | ||||
| -rw-r--r-- | tests/programs/trace_jit.expected | 6 | ||||
| -rw-r--r-- | tests/programs/trace_jit.zc | 24 | ||||
| -rw-r--r-- | tests/programs/trace_more.expected | 38 | ||||
| -rw-r--r-- | tests/programs/trace_more.zc | 40 | ||||
| -rw-r--r-- | tests/programs/trace_run.zc | 36 | ||||
| -rwxr-xr-x | tests/test.sh | 24 |
10 files changed, 207 insertions, 9 deletions
diff --git a/tests/drivers/runtime_image_test.py b/tests/drivers/runtime_image_test.py index 6c44349..64aa353 100644 --- a/tests/drivers/runtime_image_test.py +++ b/tests/drivers/runtime_image_test.py @@ -17,6 +17,7 @@ args = parser.parse_args() cases = [ ('enhancements', 'include rt; fn main() { rt.put_n(17 % 5); rt.put_c(32); rt.put_n(17 / 5); }', b'2 3'), ('array algorithms', Path('examples/learn/31-array-algorithms.zc').read_text(), Path('examples/learn/31-array-algorithms.out').read_bytes()), + ('line tables', '\n//:lines trace.zc\n' + Path('tests/programs/trace.zc').read_text(), Path('tests/programs/trace.expected').read_bytes().replace(b'<stdin>', b'trace.zc')), ('owned pop', 'include rt; int*.u take() { int*[?] a = rt.new(int*[?]); int* p = rt.new(int); *p = 8; a.push(p); return a.pop(); } fn main() { let p = take(); rt.put_n(rt.isobj(p)); rt.put_c(32); rt.put_n(*p); }', b'1 8'), # main may be void: run.zc's prototype of main must not fix its type (docs/SPEC.md 5.2.5) ('void main', Path('tests/programs/void_main.zc').read_text(), b'void main\n'), diff --git a/tests/programs/gc.zc b/tests/programs/gc.zc index 7d2441b..44fde5d 100644 --- a/tests/programs/gc.zc +++ b/tests/programs/gc.zc @@ -12,7 +12,7 @@ fn main() { churn(1000); rt.put_n(rt.live_objects() - base); rt.put_c(' '); f = rt.gc(); - rt.put_n(f >= 990); rt.put_c(' '); // conservative: a stale word or two may keep one alive + rt.put_n(f >= 950); rt.put_c(' '); // conservative: stale words, and genome code words that happen to look like heap addresses, keep a few alive rt.put_n(keep[0]); rt.put_c(' '); // the one a global still points at survives churn(200000); // 3.2m words through a 393k word heap: needs the automatic collector rt.put_n(rt.gc_runs > 1); rt.put_c(' '); diff --git a/tests/programs/trace.expected b/tests/programs/trace.expected new file mode 100644 index 0000000..2c7dce3 --- /dev/null +++ b/tests/programs/trace.expected @@ -0,0 +1,9 @@ + at <stdin>:18 in where + at <stdin>:23 in outer + at <stdin>:30 in main +1 +10 +zy: index 4 out of range (length 4) + at <stdin>:5 in get + at <stdin>:11 in sum + at <stdin>:34 in main diff --git a/tests/programs/trace.zc b/tests/programs/trace.zc new file mode 100644 index 0000000..c283f75 --- /dev/null +++ b/tests/programs/trace.zc @@ -0,0 +1,36 @@ +namespace; +include rt; + +int get(int[?] t, int i) { + return t[i]; +} + +int sum(int[?] t, int n) { + int s = 0, i = 0; + while (i < n) { + s = s + get(t, i); + i++; + } + return s; +} + +int where() { + rt.trace(); + return 1; +} + +int outer() { + int r = where(); + return r; +} + +int main() { + int[?] t = rt.new(int[?]); + t.push(1); t.push(2); t.push(3); t.push(4); + rt.put_n(outer()); + rt.put_c(10); + rt.put_n(sum(t, 4)); + rt.put_c(10); + rt.put_n(sum(t, 5)); + return 0; +} diff --git a/tests/programs/trace_jit.expected b/tests/programs/trace_jit.expected new file mode 100644 index 0000000..68e2eb4 --- /dev/null +++ b/tests/programs/trace_jit.expected @@ -0,0 +1,6 @@ + at <stdin>:9 in down + at <stdin>:12 in down + at <stdin>:12 in down + at <stdin>:12 in down + at <stdin>:18 in main +20000 diff --git a/tests/programs/trace_jit.zc b/tests/programs/trace_jit.zc new file mode 100644 index 0000000..e864c50 --- /dev/null +++ b/tests/programs/trace_jit.zc @@ -0,0 +1,24 @@ +namespace; +include rt; + +// a hot path: the jits have translated down() and the loop in main long before +// the last call, so the return stack pointer comes from translated code (op 56 +// reads it at the gate store, cb_r on both jits) +int down(int n, int last) { + if (n == 0) { + if (last) rt.trace(); + return 1; + } + return down(n - 1, last) + 1; +} + +int main() { + int i = 0, s = 0; + while (i < 5000) { + s = s + down(3, i == 4999); + i++; + } + rt.put_n(s); + rt.put_c(10); + return 0; +} diff --git a/tests/programs/trace_more.expected b/tests/programs/trace_more.expected new file mode 100644 index 0000000..b35d42d --- /dev/null +++ b/tests/programs/trace_more.expected @@ -0,0 +1,38 @@ + at <stdin>:6 in pt.get + at <stdin>:31 in main +3 + at <stdin>:34 in <closure> + at <stdin>:11 in apply + at <stdin>:32 in main +9 + at <stdin>:14 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:15 in rec + ... 35 more frames + at <stdin>:15 in rec + at <stdin>:15 in rec + at <stdin>:37 in main + at ADDR in quiet + at <stdin>:24 in loud + at <stdin>:38 in main diff --git a/tests/programs/trace_more.zc b/tests/programs/trace_more.zc new file mode 100644 index 0000000..512f93f --- /dev/null +++ b/tests/programs/trace_more.zc @@ -0,0 +1,40 @@ +namespace; +include rt; +struct pt { + int x; + int get() { + rt.trace(); + return this.x; + } +}; +int apply(fn(int) f, int v) { + return f(v); +} +int rec(int n) { + if (n == 0) { rt.trace(); return 0; } + return rec(n - 1) + 1; +} +lines off; +int quiet(int n) { + rt.trace(); + return n; +} +lines on; +int loud(int n) { + return quiet(n) + + 1; +} +int main() { + pt* p = rt.new(pt); + p.x = 3; + int k = 5; + rt.put_n(p.get()); rt.put_c(10); + rt.put_n(apply(fn(int a) { + int b = a + k; + rt.trace(); + return b; + }, 4)); rt.put_c(10); + rec(60); + loud(2); + return 0; +} diff --git a/tests/programs/trace_run.zc b/tests/programs/trace_run.zc new file mode 100644 index 0000000..1d42e26 --- /dev/null +++ b/tests/programs/trace_run.zc @@ -0,0 +1,36 @@ +namespace; +include rt; + +int get(word t, int i) { + if (i >= 4) rt.oob(i, 4); return t[i]; +} + +int sum(word t, int n) { + int s = 0, i = 0; + while (i < n) { + s = s + get(t, i); + i++; + } + return s; +} + +int where() { + rt.trace(); + return 1; +} + +int outer() { + int r = where(); + return r; +} + +int main() { + word t = rt.new(4); + t[0] = 1; t[1] = 2; t[2] = 3; t[3] = 4; + rt.put_n(outer()); + rt.put_c(10); + rt.put_n(sum(t, 4)); + rt.put_c(10); + rt.put_n(sum(t, 5)); + return 0; +} diff --git a/tests/test.sh b/tests/test.sh index 22fdf30..ec51838 100755 --- a/tests/test.sh +++ b/tests/test.sh @@ -166,9 +166,12 @@ hello world!42 v" "$l a full capture is still terminated inside its buffer" chk "$($vm < tests/programs/sys.zc)" "2 zy time" "$l syscalls: write, read, time" chk "$($vm < tests/programs/memleak.zc)" $'leak: line 8 3 words\n1' "$l memdebug names a live allocation" - chk "$($vm < tests/programs/uaf.zc)" $'zc: line 10: warning: use after free: this storage\'s lifetime ended on line 9\nzy: use after free from line 7' "$l memdebug: use after free" - chk "$($vm < tests/programs/uaf_in.zc)" "zy: use after free from line 7" "$l memdebug: use after free through an interior pointer" - chk "$($vm < tests/programs/doublefree.zc)" $'zy: double free from line 7\nok' "$l memdebug: double free is reported and does nothing" + chk "$($vm < tests/programs/uaf.zc)" $'zc: line 10: warning: use after free: this storage\'s lifetime ended on line 9\nzy: use after free from line 7\n at <stdin>:10 in main' "$l memdebug: use after free" + chk "$($vm < tests/programs/uaf_in.zc)" $'zy: use after free from line 7\n at <stdin>:11 in main' "$l memdebug: use after free through an interior pointer" + chk "$($vm < tests/programs/doublefree.zc)" $'zy: double free from line 7\n at <stdin>:9 in main\nok' "$l memdebug: double free is reported and does nothing" + chk "$($vm < tests/programs/trace.zc 2>&1 | md5sum)" "$(md5sum < tests/programs/trace.expected)" "$l rt.trace() and a fault print the call stack with file, line and function" + chk "$($vm < tests/programs/trace_more.zc 2>&1 | sed 's/0x[0-9a-f]*/ADDR/' | md5sum)" "$(md5sum < tests/programs/trace_more.expected)" "$l backtraces: methods, closures, lines off, a deep stack is cut to its ends" + chk "$($vm < tests/programs/trace_jit.zc 2>&1 | md5sum)" "$(md5sum < tests/programs/trace_jit.expected)" "$l rt.trace() from code the jit has translated: op 56 reads the return stack pointer there too" chk "$($vm < tests/programs/scrub.zc)" $'zc: line 9: warning: use after free: this storage\'s lifetime ended on line 8\n57005' "$l memdebug scrub poisons a freed payload" chk "$($vm < tests/programs/interior.zc)" "0" "$l an interior pointer keeps a quarantined block" chk "$($vm < tests/programs/wide.zc)" "1099511627776 1 1 1 -1" "$l 64-bit shifts and equality" @@ -193,7 +196,7 @@ ABC" "$l o3 lifts byte loads and stores" chk "$($vm < tests/programs/manyerrors.zc | tr '\n' '/')" "zc: line 6: expected an expression, found ';'/zc: line 7: x: owned object is never used/zc: line 8: type error: initializer: expected char*, got int/zc: line 9: expected ';', found '}'/zc: 4 errors/" "$l several errors in one run" chk "$($vm < tests/programs/overflow_return.zc 2>&1)" "zy: return stack overflow" "$l return stack overflow is caught" chk "$($vm < tests/programs/overflow_data.zc 2>&1)" "zy: data stack overflow" "$l data stack overflow is caught" - chk "$($vm < tests/programs/overflow_frame.zc 2>&1)" "zc: out of heap memory" "$l frame growth stops at a configured memory limit" + chk "$($vm < tests/programs/overflow_frame.zc 2>&1 | head -1)" "zc: out of heap memory" "$l frame growth stops at a configured memory limit" chk "$($vm < examples/mandel.zc 2>&1 | md5sum | cut -c1-32)" "f41aecbf2c844168050413cfa832db0e" "$l example: mandelbrot with a fixed-point enhancement" chk "$($vm < examples/bf.zc 2>&1 | md5sum | cut -c1-32)" "223570983752b17f7ece32e93d5e19e4" "$l example: brainfuck compiled at runtime" chk "$($vm < examples/dump.zc 2>&1 | md5sum | cut -c1-32)" "757774b00f6b3cd0bfbcba66448a8619" "$l example: struct printer through reflection" @@ -216,10 +219,10 @@ ABC" "$l o3 lifts byte loads and stores" chk "$($vm < tests/programs/o3licm.zc)" "3184200 7950 315 50 260 -56" "$l o3 hoists loop invariants and shares values between blocks" chk "$($vm < tests/programs/o3deep.zc)" "30000" "$l o3 recursion thirty thousand deep" chk "$($vm < tests/programs/o3deep_over.zc 2>&1)" "100000" "$l o3 recursion grows past the original stack" - chk "$($vm < tests/programs/o3_bounds.zc 2>&1)" "zy: index 7 out of range (length 5)" "$l o3 bounds check" + chk "$($vm < tests/programs/o3_bounds.zc 2>&1 | head -1)" "zy: index 7 out of range (length 5)" "$l o3 bounds check" chk "$($vm < tests/programs/bounds.zc)" "55 55 8" "$l a proved +1 loop skips the bounds check" - chk "$($vm < tests/programs/bounds_lo.zc 2>&1)" "zy: index -1 out of range (length 4)" "$l a negative loop index is still checked" - chk "$($vm < tests/programs/bounds_hi.zc 2>&1)" "zy: index 4 out of range (length 4)" "$l an index that is not the induction variable is still checked" + chk "$($vm < tests/programs/bounds_lo.zc 2>&1 | head -1)" "zy: index -1 out of range (length 4)" "$l a negative loop index is still checked" + chk "$($vm < tests/programs/bounds_hi.zc 2>&1 | head -1)" "zy: index 4 out of range (length 4)" "$l an index that is not the induction variable is still checked" local r; r=$(bash tests/runerr.sh "$vm" tests/errors | tail -1) chk "$r" "rejected: $(ls tests/errors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l analyzer rejects every bad program" r=$(bash tests/runerr.sh "$vm" tests/typeerrors | tail -1) @@ -350,7 +353,7 @@ s_runonly() { GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc "$input" >/dev/null || { chk 0 1 "run $1 builds"; return; } cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $d/ $CC -pthread -DZY_THREADS -O2 -w -fwrapv $d/micro.c $d/port_linux.c -o $T/zymr || { chk 0 1 "run $1 links"; return; } - a=$($T/zy < "$input" 2>&1 | tr '\n' '/') + a=$($T/zy < "$input" 2>&1 | sed "s#<stdin>#$input#g" | tr '\n' '/') b=$($T/zymr </dev/null 2>&1 | tr '\n' '/') chk "$b" "$a" "run $1" } @@ -360,6 +363,7 @@ s_runonly() { runonly selfpatch examples/selfpatch.zc runonly gc tests/programs/gc.zc runonly voidmain tests/programs/void_main.zc + runonly trace tests/programs/trace_run.zc # the image carries line tables: a fault and rt.trace() print the same backtrace as the resident compiler's runonly float tests/programs/float.zc runonly ftruth tests/programs/ftruth.zc runonly ftext tests/programs/ftext.zc @@ -382,6 +386,9 @@ s_runonly() { 0" "zcc: a program that never reads stdin does not wait for it" if [ "$(uname -s)" = Linux ]; then chk "$(file -b $T/zcc-hello | grep -c 'statically linked')" "1" "zcc: the binary is statically linked"; else echo "skip zcc static linkage (unavailable on this host)"; fi else chk 0 1 "zcc builds examples/hello.zc"; fi + if src/tools/zcc -o $T/zcc-trace tests/programs/trace_run.zc >/dev/null 2>&1; then + chk "$($T/zcc-trace </dev/null 2>&1 | sed "s#$PWD/##g" | md5sum)" "$($T/zy < tests/programs/trace_run.zc 2>&1 | sed 's#<stdin>#tests/programs/trace_run.zc#g' | md5sum)" "zcc: a fault in a compiled program prints its backtrace" + else chk 0 1 "zcc builds tests/programs/trace_run.zc"; fi printf 'fn main() {\n\tput_n(1\n\treturn 0;\n}\n' > $T/zcc-bad.zc src/tools/zcc -o $T/zcc-bad $T/zcc-bad.zc </dev/null >/dev/null 2>&1; r=$? chk "$r $( [ -e $T/zcc-bad ] && echo yes || echo no)" "1 no" "zcc: a bad program exits 1 and writes no binary" @@ -653,6 +660,7 @@ s_js() { chk "$(node $T/jsvm/tinyvm.js examples/fact.zc | tail -1)" "3628800" "js vm: factorial" chk "$(node $T/jsvm/tinyvm.js examples/primes.zc | tail -1)" "widest gap: 72, after 31397" "js vm: primes" chk "$(node $T/jsvm/tinyvm.js tests/programs/growone.zc)" "12 1" "js vm: a block past the initial heap grows the machine" + chk "$(node $T/jsvm/tinyvm.js tests/programs/trace.zc 2>&1 | md5sum)" "$(md5sum < tests/programs/trace.expected)" "js vm: backtraces read the return stack through gate op 56" chk "$(node $T/jsvm/tinyvm.js tests/programs/replace_owner.zc)" "$(cat tests/programs/replace_owner.expected)" "js vm: replacement ownership and cleanup" chk "$(node $T/jsvm/tinyvm.js tests/programs/embed.zc | tr '\n' '/')" "hello/world 11 42 5 255 8 -1 0 119 15 5 255 3 98 321 63/" "js vm: embed declarations read their files through node" if sh src/tools/findcc.sh --wasm >/dev/null 2>&1 && command -v wasm-ld >/dev/null; then |
