#!/bin/bash # full check: bootstraps the compiler from zc0, verifies the fixpoint, then runs # the examples and tests on the portable interpreter (host), the microcore, the # arm64 jit under qemu-user, and the llvm backend. needs a c compiler with c23 # #embed (src/tools/findcc.sh picks one; CC overrides it). the jit half needs # aarch64-linux-gnu-gcc and qemu-aarch64 (linux-user, not qemu-system). # # the suite reads nothing from build/: it lays out the default map, bootstraps # the compiler and builds every core into its own scratch directory, so it runs # the same from a clean checkout as after `make`. # the llvm half needs llvm-config (LLVM_CONFIG names another) and libLLVM. src/baremetal/run.sh boots the # microcore on qemu-system-riscv64 and qemu-system-aarch64 when those cross # compilers and system emulators are installed. each of these is skipped # when its tools are missing. # # -j N runs N sections at a time, where N is how many cores to use. the # sections are independent and each gets its own scratch directory, so they # can overlap; the output still reads in the same order either way. -j 1 is # the default, and is what make test does unless JOBS says otherwise. # # -q (make check) is the minimal suite for iterating: the bootstrap and the # fixpoint, the interpreter section and the language server's own checks. # it skips the other backends, the node protocol and corpus tests, and the # js and make sections. run the whole thing (no -q) when the work is done. cd "$(dirname "$0")/.."; T=$(mktemp -d); trap 'rm -rf $T' EXIT ok=0; bad=0 chk(){ if [ "$1" = "$2" ]; then echo "ok $3"; ok=$((ok+1)); else echo "FAIL $3: got [$1]"; bad=$((bad+1)); fi; } # the compiler is the files listed in src/compiler/parts, in that order ccparts(){ tr -d '\r' < src/compiler/parts | sed 's|^|src/compiler/|'; } src(){ cat src/runtime/rt.zc src/core/core.zc $(ccparts); } # -j N runs N sections at a time: j is how many cores to use. each section # writes its own log, so the output still reads in the order it always did. JOBS=1; QUICK=0; SELECT="" while [ $# -gt 0 ]; do case "$1" in -j) JOBS=$2; shift 2;; -j*) JOBS=${1#-j}; shift;; -q) QUICK=1; shift;; --section) SELECT=$2; shift 2;; *) echo "usage: $0 [-q] [-j N] [--section NAME]"; exit 2;; esac done case "$JOBS" in ''|*[!0-9]*|0) JOBS=1;; esac # each section gets its own scratch directory, so none of them can tread on # another's files when they overlap SECS="interp micro small runonly bare jit llvm lsp js make learn" [ $QUICK = 1 ] && SECS="interp lsp learn" if [ -n "$SELECT" ]; then case " $SECS " in *" $SELECT "*) SECS=$SELECT;; *) echo "unknown test section: $SELECT" >&2; exit 2;; esac fi ROOT=$T CC=${CC:-$(sh src/tools/findcc.sh)} || exit 1 export CC # the language server runs on the interpreter unless the host has a jit (arm64, or x86-64 # linux), and a check of a large document then takes many seconds: stretch the drivers' # deadlines. the x86-64 jit gets a little room if [ -z "${ZC_TEST_TIMEOUT_SCALE:-}" ]; then case "$(uname -s)-$(uname -m)" in *-arm64|*-aarch64) ZC_TEST_TIMEOUT_SCALE=1;; Linux-x86_64) ZC_TEST_TIMEOUT_SCALE=2;; *) ZC_TEST_TIMEOUT_SCALE=8;; esac; fi export ZC_TEST_TIMEOUT_SCALE # the default map, generated as make does it, and the tools built against it $CC -O2 -w -fwrapv src/tools/genmap.c -o $T/genmap || exit 1 $T/genmap --preset default --check -o $T/map >$T/genmap.log 2>&1 || { cat $T/genmap.log; echo "FAIL the default map does not match the checked-in sources"; exit 1; } MAP=$T/map $CC -O1 -w -fwrapv src/tools/zc0.c -Isrc/core -I$MAP -o $T/zc0 && $CC -O1 -w -fwrapv src/tools/mkgenome.c -I$MAP -o $T/mkg || exit 1 # the c helper for tests/programs/structval.zc: structs by value, loaded with cload mkdir -p build && $CC -shared -fPIC -w tests/data/sbv.c -o build/sbv.so || exit 1 selfc(){ (cat src/tools/build.zc; printf '`'; src) | $1 > $2; } # bootstrap: zc0 (c) -> g0, g0 compiles the compiler -> g1, g1 compiles it -> g2. # each stage's core embeds the image in its own directory mkdir $T/s0 $T/s1 src | $T/zc0 > $T/s0/g0.bin && $T/mkg < $T/s0/g0.bin > $T/s0/genome.bin || exit 1 $CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -I$MAP --embed-dir=$T/s0 -o $T/zy0 || exit 1 selfc $T/zy0 $T/g1.bin; $T/mkg < $T/g1.bin > $T/s1/genome.bin || exit 1 $CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c -Isrc/core -I$MAP --embed-dir=$T/s1 -o $T/zy1 || exit 1 selfc $T/zy1 $T/g2.bin cmp -s $T/g1.bin $T/g2.bin && chk 1 1 "fixpoint: the compiler compiles itself to the same $(($(wc -c < $T/g1.bin) / 8)) words" || chk 0 1 "fixpoint" # every core below carries the fixpoint genome: GI is the include and embed path for it GI="-Isrc/core -I$MAP --embed-dir=$T/s1" # built here rather than inside a section: interp runs the program list on # it, and the run-only and bare-metal sections read it as the reference. $CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c $GI -o $T/zy || exit 1 chk "$($T/zc0 < tests/programs/zc0typed.zc | md5sum)" "$($T/zc0 < tests/programs/zc0plain.zc | md5sum)" "zc0 reads types and throws them away: typed and plain sources give one image" case "$($T/zc0 < tests/programs/zc0embed_write.zc 2>&1 >/dev/null)" in *"line 6: a local embed array is part of the code"*) chk 1 1 "zc0 refuses a store into a local embed array";; *) chk 0 1 "zc0 refuses a store into a local embed array";; esac for n in $SECS; do mkdir -p $ROOT/$n cp $ROOT/g1.bin $ROOT/$n/ # the bootstrap's image, read by micro, jit and llvm cp $ROOT/zy $ROOT/$n/zy # the interpreter, built once above cp $ROOT/genmap $ROOT/$n/genmap done run(){ # $1 = vm command, $2 = label local vm="$1" l="$2" chk "$($vm < examples/hello.zc)" "hello, world!" "$l hello" chk "$($vm < examples/fact.zc | tr '\n' '/')" "1101110101111100000000/3628800/" "$l factorial" chk "$( (cat examples/echo.zc; printf '`abc\n') | $vm)" "abc" "$l echo after the backtick" chk "$( (cat tests/programs/backtick.zc; printf '`xyz') | $vm)" "3 3 96 39 xyz" "$l a backtick in a comment, string or char literal does not end the source" chk "$($vm < examples/selfpatch.zc)" "ABC" "$l self-patching code" chk "$($vm < examples/lists.zc | tr '\n' '/')" "[0 1 4 9 16 25]/live: 7/after the block: 0/" "$l ownership frees everything" chk "$( (cat examples/repl.zc; printf '`fn sq(x) { return x * x; }`rt.put_n(sq(7)); rt.put_c(10);`rt.put_s("hi\\n");') | $vm | tr '\n' '/')" "49/hi/" "$l repl on the resident compiler" chk "$($vm < examples/typed.zc | tr '\n' '/')" "last: 4/point is 2 words: x int, y int/vec is 3 words: len int, cap int, items point**/push is bool(vec*, T.d)/the heap says v is a vec, and v.items[0] is a point/" "$l typed example" chk "$($vm < examples/wiring.zc)" "16 1234 25 4/9" "$l enhancement example" chk "$($vm < tests/programs/allocfast.zc)" "0 0 499500 0" "$l constant-size allocation fast path" chk "$($vm < tests/programs/basics.zc | tr '\n' '/')" "285 610 42 512/hey 65 -3 -2 1024 -8/12456/757/9 100-101/5/ok/" "$l language basics" chk "$($vm < tests/programs/umd.zc)" "100 6 0 15 99 5 5" "$l u m d lifetimes" chk "$($vm < tests/programs/names.zc)" "12 1 8" "$l u, m and d are ordinary names" chk "$($vm < tests/programs/zc0umd.zc | tr '\n' '/')" "6 6 28 24 37 10/42 41 336 59/21 6 72 120 105 21/" "$l u, m and d as names, products, recasts and tags (the zc0 subset)" chk "$($vm < tests/programs/gc.zc)" "1000 1 900 1 199900" "$l mark-sweep collector" chk "$($vm < tests/programs/meta.zc | tr '\n' '/')" "1024 19/unless ok/rrr/15 42/7/42/7/144/" "$l parse words, quote, eval, load, operator tables" chk "$($vm < tests/programs/misc.zc | tr '\n' '/')" "45/54321/ok/" "$l compile-time eval, patched mnemonics, unchecked" chk "$($vm < tests/programs/defer.zc | tr '\n' '/')" "1 2 10 11 20 30 /7 /2 2 /9 18 /-1 /500 1 /501 500 2 /2 1 5 9 /4 3 5 9 /1 101 102 3 103 104 777 0 200 300 201 2 202 401 402 403 900 /60 50 0 51 1 62 52 2 2000 1000 /51 1 50 /50 51 2 3 /4 3 5 2 6 1 7 /10 11 99 0 /21 99 0 /31 0 /2 1 42 /9 12 /0 2 /0 /10 11 20 0 /2 3 4 5 /9 0 2 3 /" "$l defer: block end, return, break, continue, ownership" chk "$($vm < tests/programs/types.zc | tr '\n' '/')" "12 5 2 15 56 42 7 14 3/rect rect* list* list*.u(int) T(list*) fn(word.d)/a:point@0 b:point@2 tag:int@4 /point 1 1/" "$l types, generics, reflection" chk "$($vm < tests/programs/typedef.zc | tr '\n' '/')" "7 1 42 5 30/13 4 19 4/4 3 3 12/5 11/hihihihellook/8/" "$l typedef, global and local, in every place a type goes, and string" chk "$($vm < tests/programs/typeok.zc)" "14" "$l a program the typechecker accepts" chk "$($vm < tests/programs/typeoff.zc)" "6" "$l the typechecker is off by default" chk "$($vm < tests/programs/enhance.zc)" "100 0 343 3,2 5 -2 0" "$l virtual enhancements" chk "$($vm < tests/programs/precedence.zc)" "1 1 1 1 1 1" "$l && binds tighter than ||, as in c" chk "$($vm < tests/programs/const_local.zc)" "6 42 zc" "$l an untyped local const takes its initializer's type" chk "$($vm < tests/programs/joins.zc)" "0 0 5 80 8 3 9 0" "$l joins free what one path gave away; switch, ?:, no prototypes" chk "$($vm < tests/programs/buddy.zc)" "131072 262144 1 0" "$l buddy allocator merges freed blocks" chk "$($vm < tests/programs/growheap.zc)" "144 1 7 1" "$l the heap grows past its initial size and a bigger block fits after the frees" chk "$($vm < tests/programs/sized.zc | md5sum)" "$(md5sum < tests/programs/sized.expected)" "$l sized integers wrap, and arrays and pointers of them are packed" chk "$($vm < tests/programs/bitops.zc | md5sum)" "$(md5sum < tests/programs/bitops.expected)" "$l ctz, popcnt, bswap, rotates, mulh and float32 conversion micro-ops" chk "$($vm < tests/programs/f32.zc | md5sum)" "$(md5sum < tests/programs/f32.expected)" "$l f32: single precision scalars and packed arrays" chk "$($vm < tests/programs/f32o3.zc)" "10718.750000 1.600000 0" "$l f32 inside an o3 region" chk "$($vm < tests/programs/sizedvec.zc | md5sum)" "$(md5sum < tests/programs/sizedvec.expected)" "$l growable arrays of the sized types and f32" chk "$($vm < tests/programs/packedfields.zc | md5sum)" "$(md5sum < tests/programs/packedfields.expected)" "$l struct fields of the sized types pack into words" chk "$($vm < tests/programs/generics.zc | md5sum)" "$(md5sum < tests/programs/generics.expected)" "$l generic functions are compiled per type arguments" chk "$($vm < tests/programs/genmeth.zc | md5sum)" "$(md5sum < tests/programs/genmeth.expected)" "$l methods of generic structs are compiled per type arguments" chk "$($vm < tests/programs/static.zc)" "14 5 9" "$l static functions of a struct are called as Struct.name(args)" chk "$($vm < tests/programs/struct_const.zc)" "24 49 24 23 19 55 12 99" "$l constants declared in a struct are reached as Struct.NAME, and by bare name in its methods" chk "$($vm < tests/programs/struct_static_call.zc)" "3 100 11 8 1010 7" "$l a static function of a struct is called by its bare name in its methods, and wins over a global of the same name" chk "$($vm < tests/programs/methodown.zc)" "7 9" "$l struct methods and functions return owned objects (.u)" chk "$($vm < tests/programs/typedmethods.zc)" "14 5 13" "$l struct methods declare a return type and typed parameters" chk "$($vm < tests/programs/enumscope.zc)" "0 5 6 blue" "$l enum members are reached as Enum.MEMBER, also in a case" chk "$($vm < tests/programs/enumtype.zc)" "6 5 1" "$l typed enums: members take their enum, an enum becomes an int" chk "$($vm < tests/programs/tagenum.zc)" "27 20 0 0 1 2" "$l tagged enums: members carry fields, constructors build them, a case binds the fields" chk "$($vm < tests/programs/tagenum_warn.zc)" "$(printf 'zc: line 8: warning: this switch doesn'"'"'t cover rect\n0')" "$l a switch on a tagged enum that misses a member warns, on the switch's line" chk "$($vm < tests/programs/tagenum_two.zc)" "3 8 12" "$l two tagged enums with the same member name, and a case in an o3 loop" chk "$($vm < tests/programs/o3push.zc | md5sum)" "$(md5sum < tests/programs/o3push.expected)" "$l push and pop of a growable array are inline in o3" chk "$($vm < tests/programs/o3copy.zc)" "520507 1005" "$l o3 copies of a loop variable read the source instead" chk "$($vm < tests/programs/borrowmove.zc)" "6" "$l storing into a borrowed object moves it" chk "$($vm < tests/programs/embed.zc | tr '\n' '/')" "hello/world 11 42 5 255 8 -1 0 119 15 5 255 3 98 321 63/" "$l embed declarations: strings, numbers and arrays from files, global and local" chk "$($vm < tests/programs/ownmove.zc)" "7 50 0 1" "$l an owned local moves into a released owner, and a global borrows a local until it is restored" chk "$($vm < tests/programs/alias.zc)" "11 22 4 8" "$l aliases of a live owner: arithmetic, field, index" chk "$($vm < tests/programs/quarantine.zc)" "0 1234" "$l quarantine: stale alias reads intact data, second free is a no-op" chk "$($vm < tests/programs/recycle.zc)" "1 0 1" "$l recycle returns a dropped block to its size class" chk "$($vm < tests/programs/roots.zc)" "1 0" "$l the collector marks the live stack, and scrub clears the rest" chk "$($vm < tests/programs/arena.zc)" "4 7 0 1 1" "$l bump arena allocates, rewinds, and refuses a bump past the end" chk "$($vm < tests/programs/csyn.zc)" "13 0 5 6 0" "$l do/while, enum, and a C cast" chk "$($vm < tests/programs/use_std.zc)" "42 -7 1" "$l include and the string helpers" chk "$($vm < tests/programs/stdbuf.zc)" "4 1 266 8" "$l stdlib arena and packed byte buffer" chk "$($vm < tests/programs/stdmore.zc)" "$(printf '5 1 0 0 7 copy me 12 -1 0 1 0 1\n9 mixed 123 9 MIXED 123\n11 [padded line]\n42 3 9 10 12 81 9 10 -1 0\n6 1 2 -1 1 0')" "$l stdlib text, byte-string and num helpers" chk "$(ZC_FILE=tests/data/hi.txt $vm < examples/cli.zc)" "6 h" "$l reading a file named by the environment" chk "$(ZC_ARG_TEST=ok $vm < tests/programs/args.zc)" "1 1 1" "$l argv and envp from the host, argv[0] is on stdin" chk "$(ZC_ARG_TEST=ok $vm < tests/programs/capture.zc)" "0 1 15 hello world!42 1 1 0" "$l capture.start() collects output and end() hands back the string" chk "$($vm < tests/programs/capture_long.zc)" "7 256 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/file_drop.zc)" "5 1 1 hello! 1" "$l std.File: its drop closes the descriptor at block end" chk "$($vm < tests/programs/memleak.zc)" $'leak: line 8 1 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\n at :10 in main' "$l memdebug: use after free" chk "$($vm < tests/programs/uaf_in.zc)" $'zy: use after free from line 7\n at :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 :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" chk "$($vm < tests/programs/float.zc)" "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" "$l floating point" chk "$($vm < tests/programs/ftruth.zc)" "00111000110107" "$l negative zero is false" chk "$($vm < tests/programs/ftype.zc)" "3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4" "$l float type through calls, globals, fields" chk "$($vm < tests/programs/fo3.zc)" "5997 5997 4.000000 nan 0 3.250000 1 0 0" "$l o3 lifts float ops" chk "$($vm < tests/programs/ftext.zc)" "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" "$l float text: shortest, fixed, scientific, 17 digits" chk "$($vm < tests/programs/bytes.zc)" "17 34 43981 16909060 1 205 171 -3674937295934316015 1108152156331 0 0 30768 ABC" "$l byte addressing" chk "$($vm < tests/programs/o3bytes.zc)" "190 4660 7 2055 16909060 0 0 ABC" "$l o3 lifts byte loads and stores" chk "$($vm < tests/programs/o3zero.zc)" "1" "$l untyped new inside o3 zeroes its words" chk "$($vm < tests/programs/strings.zc)" "hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab" "$l byte strings" chk "$($vm < tests/programs/share.zc)" "1 2 1 4 1 1 1 1 42 1 1 1 6 0" "$l shared cons cells" chk "$($vm < tests/programs/share_dbg.zc)" "7 1" "$l memdebug accepts a shared cell" chk "$($vm < tests/programs/memdebug_interior.zc | md5sum)" "$(md5sum < tests/programs/memdebug_interior.expected)" "$l memdebug finds the block of a deep interior pointer" chk "$($vm < tests/programs/global_transfer.zc)" "5 9" "$l an m or u owner stored in a global is transferred" chk "$($vm < tests/programs/closure_result.zc)" "8 1.500000" "$l a closure with a declared result type" chk "$($vm < examples/strings.zc)" "hello, zc 9 42" "$l byte string example" chk "$(ZC_BSTR=zybyte $vm < tests/programs/bstr_io.zc)" "byte-io 7 7 1 6 122 101" "$l byte-string file and getenv" 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 | 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" chk "$($vm < examples/tree.zc 2>&1 | md5sum | cut -c1-32)" "b86a2e4e9e31acc294a4ddf86be863f2" "$l example: owning tree frees itself" chk "$($vm < examples/lang.zc 2>&1 | md5sum | cut -c1-32)" "$(printf '22\none isn'"'"'t bigger than two\n***\nassertion failed on line 50\n' | md5sum | cut -c1-32)" "$l example: pipe operator, unless, assert, times" chk "$($vm < examples/staged.zc 2>&1 | md5sum | cut -c1-32)" "6f790a125c48b0cc7a897cee73ee5627" "$l example: staged code compiled while running" chk "$($vm < examples/quine.zc 2>&1 | md5sum | cut -c1-32)" "$(md5sum < examples/quine.zc | cut -c1-32)" "$l example: quine" chk "$($vm < examples/primes.zc 2>&1 | md5sum | cut -c1-32)" "814faffc70dad0ae520c0671c33e73d4" "$l example: o3 prime sieve" chk "$($vm < examples/life.zc 2>&1 | md5sum | cut -c1-32)" "aefb036b6f9ad1dadfc3e6bb47a6885d" "$l example: game of life" chk "$($vm < tests/programs/arrays.zc | tr '\n' '/')" "100 128 9801 9801 99 3 4 12 19 8 19 28/0/" "$l growable and fixed arrays" chk "$($vm < tests/programs/o3.zc)" "3142 42 43 9 57 98781 520625 10192 -18782 1152" "$l o3 regions: loops, calls, recursion, switch, arrays, more vars than registers" chk "$($vm < tests/programs/o3scopes.zc)" "116" "$l o3 registers shared by scopes, cached fixed arrays" chk "$($vm < tests/programs/borrow_rules.zc)" "$(cat tests/programs/borrow_rules.expected)" "$l inferred transfers, projections and shared closure cells" chk "$($vm < tests/programs/replace_owner.zc)" "$(cat tests/programs/replace_owner.expected)" "$l replacing a released owner moves ownership and cleans up once" chk "$($vm < tests/programs/temporary_borrows.zc)" "$(cat tests/programs/temporary_borrows.expected)" "$l temporary borrows preserve aliases and release allocations" chk "$($vm < tests/programs/array_values.zc)" "$(cat tests/programs/array_values.expected)" "$l array initializers, designators, zero filling, nested and compound literals" chk "$($vm < tests/programs/o3fat.zc)" "node node 3 box node int[0]" "$l o3 data gets fattened on the way out" chk "$($vm < tests/programs/o3call.zc)" "0 0 4 23 55 node" "$l o3 calls o3 directly, and new stamps the header" chk "$($vm < tests/programs/o3direct.zc | md5sum)" "$(md5sum < tests/programs/o3direct.expected)" "$l o3 calls: arguments, results, recursion, floats, pointers, the ways out of a fast call" 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; echo "rc=$?")" "rc=0" "$l o3 implies unbounded: no check on a T[?] index" 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 | 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; echo "rc=$?")" "rc=0" "$l o3 implies unbounded: an index that is not the induction variable is not checked" chk "$($vm < tests/programs/boundsrules.zc 2>&1 | head -3)" $'5\n7\nzy: index 5 out of range (length 4)' "$l unbounded as prefix, block and statement, o3 implies it; checked code reports" chk "$($vm < tests/programs/bounds_fixed.zc 2>&1 | head -3)" $'55\n29\nzy: index 6 out of range (length 6)' "$l a fixed array with a variable index is checked against its length" chk "$($vm < tests/programs/bounds_heap.zc 2>&1 | head -4)" $'3\n0\nx\nzy: index 11 out of range (length 11)' "$l a pointer to a heap block is checked against its payload" chk "$($vm < tests/programs/enh_ops.zc | md5sum)" "$(md5sum < tests/programs/enh_ops.expected)" "$l enhancement micro-ops on edge values, with variables and with literals" chk "$($vm < tests/programs/bounds_proof.zc)" $'0\n1\n1' "$l a loop test against a constant drops the fixed-array check; a variable bound keeps it" chk "$($vm < tests/programs/bounds_hoist.zc 2>&1 | head -5)" $'28\n3\n27\n0 1 2 3 4 5 6 7 8 zy: index 8 out of range (length 8)\n at :17 in main' "$l a check against an invariant bound is hoisted to the loop entry and still faults in place" chk "$($vm < tests/programs/bounds_body.zc 2>&1 | head -1)" "zy: index -2999 out of range (length 4)" "$l a body that moves the loop index keeps the check" chk "$($vm < tests/programs/bounds_ptr.zc 2>&1 | head -3 | cut -c1-9)" $'120\n240\nzy: index' "$l a pointer from rt.new(n): proved, hoisted, or kept when the body calls" chk "$($vm --explain-checks < tests/programs/explain_checks.zc | md5sum)" "$(md5sum < tests/programs/explain_checks.expected)" "$l --explain-checks lists the kept checks (format v1)" chk "$($vm --explain-checks a/b.zc < tests/programs/bounds.zc | head -1)" '{"version":1}' "$l --explain-checks starts with the version line" chk "$($vm < tests/programs/zc0unbounded.zc)" "42" "$l unbounded in the unit, a prefix and a statement" 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) chk "$r" "rejected: $(ls tests/typeerrors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l typechecker rejects every bad program" r=$(bash tests/runerr.sh "$vm" tests/langerrors | tail -1) chk "$r" "rejected: $(ls tests/langerrors/*.zc | wc -l | tr -d ' ') passed, 0 failed" "$l o3 and array misuse is rejected" bash tests/exitstatus.sh "$vm" > /tmp/zy-exitstatus.$$ 2>&1; r=$?; grep -v '^ok' /tmp/zy-exitstatus.$$; rm -f /tmp/zy-exitstatus.$$ chk $r 0 "$l a compile error exits 1, a good program and a runtime trap exit 0" } s_interp() { T=$ROOT/interp $CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c $GI -o $T/zy run $T/zy "interp" # docs/SPEC.md conformance programs; failures listed in tests/conformance/known.txt do not count. # not in the quick path (-q, make check): it takes about half a minute if [ $QUICK = 0 ]; then bash tests/conformance/run.sh --zy $T/zy -w $T/conformance -q > $T/conformance.out 2>&1; r=$? grep '^FAIL' $T/conformance.out chk $r 0 "interp conformance suite: $(grep '^ total' $T/conformance.out | sed 's/ */ /g')" fi python3 tests/drivers/array_methods_test.py $T/zy > $T/arrays.out 2>&1; r=$? grep -v '^ok' $T/arrays.out chk $r 0 "native array receiver, algorithm and ownership contracts" # actors are a library: nothing the compiler compiles or reads by default mentions them chk "$(grep -il actor $(ccparts) src/runtime/rt.zc src/core/core.zc src/lib/std.zc src/lib/icon.zc src/tools/build.zc | wc -l)" "0" "no actor code in the compiler or anything it reads by default" python3 tests/drivers/actor_test.py $T/zy > $T/actor.out 2>&1; r=$? grep -v '^ok' $T/actor.out chk $r 0 "native isolated actor acceptance, rejection and regression fixtures" chk "$(ZC_ARG_TEST=ok $T/zy tests/programs/args.zc alpha beta < tests/programs/args.zc)" "4 beta 1 1" "interp the command line reaches argv, after " chk "$($T/zy < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "interp native calls and callbacks (extern, cfunc)" chk "$($T/zy < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "interp native calls from o3 code, more arguments than registers" chk "$($T/zy < tests/programs/cabi_sized.zc | md5sum)" "$(md5sum < tests/programs/cabi_sized.expected)" "interp native calls with f32 and the sized integers" chk "$($T/zy < tests/programs/structval.zc)" "34 3520 18 325 810 340" "interp structs by value to a c function (registers, sse, stack)" chk "$($T/zy < tests/programs/structret.zc)" "3 4 3.500000 4.000000 24 8 1.000000 3.500000" "interp structs returned from a c function (registers, sse, memory)" chk "$(ZYTRACE=1 $T/zy < tests/programs/o3.zc 2>&1 | grep -c 'micro-ops$')" "8" "every o3 region lifts" chk "$($T/zy < tests/programs/infer_return.zc)" "3" "an owned return with no .u is inferred from the body" chk "$($T/zy < tests/programs/discard_m.zc)" "7" "m* discards an unnamed u result" chk "$($T/zy < tests/programs/closure.zc | tr '\n' ' ')" "31 35 42 2 7 7 109 30 7 16 33 20 101 " "closures: calls through values, captures, nesting, returns" chk "$($T/zy < tests/programs/decl_undefined.zc 2>&1 | head -1)" "zc: line 3: helper is used but never defined" "a declared function nothing defines fails a build" if [ "$(uname -s)" = Linux ]; then chk "$($T/zy < tests/programs/rawsys.zc)" "-38" "Host.raw: an unknown syscall number gives -ENOSYS"; else chk "$($T/zy < tests/programs/rawsys.zc)" "-1" "Host.raw: unavailable on this host"; fi if [ "$(uname -m)" = x86_64 ]; then chk "$($T/zy < tests/programs/rawpid.zc)" "1" "Host.raw: getpid (39 on x86-64) answers"; fi } s_micro() { T=$ROOT/micro $CC -pthread -DZY_THREADS -O2 -w -fwrapv src/micro/micro.c src/micro/port_linux.c $GI -Isrc/micro -o $T/zym || exit 1 run $T/zym "micro" python3 tests/drivers/array_methods_test.py $T/zym > $T/arrays.out 2>&1; r=$? grep -v '^ok' $T/arrays.out chk $r 0 "micro array receiver, algorithm and ownership contracts" python3 tests/drivers/actor_test.py $T/zym > $T/actor.out 2>&1; r=$? grep -v '^ok' $T/actor.out chk $r 0 "micro isolated actor acceptance, rejection and regression fixtures" if command -v node >/dev/null && command -v tsc >/dev/null && node src/js/mklayout.js --map $MAP/memmap.h --out $T/jsvm >/dev/null \ && cp $ROOT/s1/genome.bin $T/jsvm/ && tsc src/js/tinyvm.ts --target es2020 --lib es2020,dom --module commonjs --outDir $T/jsvm; then python3 tests/drivers/fuzz_compiler.py $ROOT/zy $T/zym --js $T/jsvm/tinyvm.js > $T/fuzz.out 2>&1; r=$? else python3 tests/drivers/fuzz_compiler.py $ROOT/zy $T/zym > $T/fuzz.out 2>&1; r=$? fi grep -v '^ok' $T/fuzz.out chk $r 0 "compiler/runtime/stdlib differential fuzz across available hosts" python3 tests/drivers/fuzz_malformed.py $ROOT/zy $T/zym > $T/fuzzmalformed.out 2>&1; r=$? grep -v '^ok' $T/fuzzmalformed.out chk $r 0 "malformed compiler input fuzz and host agreement" selfc $T/zym $T/gm.bin cmp -s $T/g1.bin $T/gm.bin && chk 1 1 "microcore self-compile matches" || chk 0 1 "microcore self-compile matches" $CC -ffreestanding -w -c src/micro/micro.c $GI -Isrc/micro -o $T/micro.o && $CC -ffreestanding -w -c src/micro/port_stub.c $GI -Isrc/micro -o $T/stub.o && chk 1 1 "microcore and the stub port compile freestanding" || chk 0 1 "microcore and the stub port compile freestanding" $CC -pthread -O2 -w -fwrapv -DJTYPE=int -DZY_SOFTFLOAT -DZYINTERP src/core/core.c src/core/io_linux.c $GI -o $T/zysoft || exit 1 chk "$($T/zysoft < tests/programs/float.zc)" "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" "soft-float fallback matches the hardware line" chk "$($T/zysoft < tests/programs/ftruth.zc)" "00111000110107" "soft-float negative zero is false" chk "$($T/zysoft < tests/programs/fo3.zc)" "5997 5997 4.000000 nan 0 3.250000 1 0 0" "soft-float o3 float ops match" chk "$($T/zysoft < tests/programs/ftext.zc)" "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" "soft-float text matches" } s_small() { T=$ROOT/small # smaller machines. addresses are literals, so each size is its own genome. $CC -O2 -w -fwrapv src/tools/genmap.c $GI -o $T/genmap || exit 1 $T/genmap --preset default --check >/dev/null && chk 1 1 "default map matches the checked-in sources" || chk 0 1 "default map matches the checked-in sources" $T/genmap --preset small -o $T/small || exit 1 $T/genmap --mode compiler --name small --heap-lo 8192 --heap 20480 --types 8192 \ --genome 393216 --frame 2048 --return 512 --mark 512 --data 3072 \ --snap-cap 1000 --quote-cap 300 -o $T/small-flags || exit 1 cmp -s $T/small/memmap.h $T/small-flags/memmap.h && cmp -s $T/small/rt.zc $T/small-flags/rt.zc \ && cmp -s $T/small/compiler.zc $T/small-flags/compiler.zc && cmp -s $T/small/map.txt $T/small-flags/map.txt \ && chk 1 1 "compiler flags reproduce the small map" || chk 0 1 "compiler flags reproduce the small map" cp src/tools/zc0.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $T/small/ $CC -O1 -w -fwrapv $T/small/zc0.c -o $T/zc0s || exit 1 cat $T/small/rt.zc src/core/core.zc $T/small/compiler.zc | $T/zc0s > $T/small/genome.bin || exit 1 cp src/micro/micro.c src/micro/micro_port.h src/micro/port_linux.c src/micro/port_stub.c src/core/core.c src/core/io_linux.c src/core/fops.h src/core/bops.h src/core/stack.h src/core/genome.h $T/small/ $CC -pthread -DZY_THREADS -O2 -w -fwrapv $T/small/micro.c $T/small/port_linux.c -o $T/zyms || exit 1 $CC -pthread -O2 -w -fwrapv -DJTYPE=int $T/small/core.c $T/small/io_linux.c -o $T/zys || exit 1 $CC -ffreestanding -w -c $T/small/micro.c -o $T/micro_s.o && $CC -ffreestanding -w -c $T/small/port_stub.c -o $T/stub_s.o && chk 1 1 "small microcore compiles freestanding" || chk 0 1 "small microcore compiles freestanding" sameout() { # $1 = other vm, $2 = label, $3 = source file local a b a=$($T/zy < "$3" 2>&1 | tr '\n' '/') b=$($1 < "$3" 2>&1 | tr '\n' '/') chk "$b" "$a" "$2" } for f in examples/hello.zc examples/fact.zc examples/lists.zc examples/typed.zc examples/wiring.zc examples/life.zc \ tests/programs/basics.zc tests/programs/wide.zc tests/programs/types.zc tests/programs/alias.zc tests/programs/meta.zc tests/programs/arrays.zc \ tests/programs/joins.zc tests/programs/enhance.zc tests/programs/o3.zc tests/programs/csyn.zc tests/programs/umd.zc tests/programs/quarantine.zc tests/programs/roots.zc \ tests/programs/float.zc tests/programs/ftruth.zc tests/programs/ftype.zc tests/programs/fo3.zc tests/programs/ftext.zc tests/programs/bytes.zc \ tests/programs/strings.zc examples/strings.zc tests/programs/share.zc tests/programs/share_dbg.zc; do sameout $T/zyms "small $(basename "$f" .zc)" "$f" sameout $T/zys "small core $(basename "$f" .zc)" "$f" done # code literals can look like pointers on a compact map. test collection and # the retained root without assuming the default map's exact reclaim count. for vm in $T/zyms $T/zys; do out=$(sed -E 's/f >= [0-9]+/f > 0/' tests/programs/gc.zc | $vm | tr '\n' '/') chk "$out" "1000 1 900 1 199900/" "small collector makes progress and preserves its root" done $T/genmap --preset run -o $T/run || exit 1 $T/genmap --mode run --words 20480 -o $T/run-flags || exit 1 cmp -s $T/run/memmap.h $T/run-flags/memmap.h && cmp -s $T/run/rt.zc $T/run-flags/rt.zc \ && cmp -s $T/run/map.txt $T/run-flags/map.txt \ && chk 1 1 "run flags reproduce the growing run-only map" || chk 0 1 "run flags reproduce the growing run-only map" } s_runonly() { T=$ROOT/runonly python3 tests/drivers/runtime_image_test.py --host $T/zy --mkgenome $ROOT/mkg > $T/images.out 2>&1; r=$? grep -v '^ok' $T/images.out chk $r 0 "self-hosted run-only images install enhancements and preserve generic array methods" # its own run map: the run-only image is built against this one, and the # small-machine section lays out its own rather than sharing this $T/genmap --preset run -o $T/run || exit 1 cp src/tools/zc0.c 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 $T/run/ $CC -O1 -w -fwrapv $T/run/zc0.c -o $T/zc0r || exit 1 runonly() { # $1 label, $2 source. the program is already in the image, so it reads no source. # the map is sized to the program: src/tools/automap.sh lays it out for the image's own length local a b d=$T/run-$1 input=$2 if [ "$1" = gc ]; then input=$T/collector-progress.zc; sed -E 's/f >= [0-9]+/f > 0/' "$2" > "$input"; fi 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 | sed "s##$input#g" | tr '\n' '/') b=$($T/zymr &1 | tr '\n' '/') chk "$b" "$a" "run $1" } runonly hello examples/hello.zc runonly fact examples/fact.zc runonly wide tests/programs/wide.zc runonly selfpatch examples/selfpatch.zc runonly gc tests/programs/gc.zc runonly zc0unbounded tests/programs/zc0unbounded.zc # zc0 accepts unbounded in all three places and ignores it 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 runonly bytes tests/programs/bytes.zc runonly strings tests/programs/strings.zc runonly strings-ex examples/strings.zc runonly share tests/programs/share.zc runonly embed tests/programs/embed_top.zc runonly zc0bstr tests/programs/zc0bstr.zc runonly zc0umd tests/programs/zc0umd.zc runonly wiring examples/wiring.zc d=$T/run-echo; GENMAP=$T/genmap bash src/tools/automap.sh run $d src/runtime/run.zc examples/echo.zc >/dev/null && 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 "$(printf 'abc\n' | $T/zymr | tr '\n' '/')" "$( (cat examples/echo.zc; printf '`abc\n') | $T/zy | tr '\n' '/')" "run echo" \ || chk 0 1 "run echo" # zcc: a program becomes a static binary that runs without stdin and with a held-open pipe if src/tools/zcc -o $T/zcc-hello examples/hello.zc >/dev/null 2>&1; then chk "$($T/zcc-hello < /dev/null)" "hello, world!" "zcc: hello, static" chk "$(sleep 2 | timeout 5 $T/zcc-hello; echo $?)" "hello, world! 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 &1 | sed "s#$PWD/##g" | md5sum)" "$($T/zy < tests/programs/trace_run.zc 2>&1 | sed 's##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 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" $CC -ffreestanding -w -c $T/run-echo/micro.c -o $T/micro_r.o && $CC -ffreestanding -w -c $T/run-echo/port_stub.c -o $T/stub_r.o && chk 1 1 "run-only microcore compiles freestanding" || chk 0 1 "run-only microcore compiles freestanding" } s_bare() { T=$ROOT/bare bare() { # $1 target, $2 run|compile, $3 source, $4 label. compare UART stdout to the hosted interpreter. local a b a=$($T/zy < "$3" 2>&1 | tr '\n' '/') b=$(bash src/baremetal/run.sh "$1" "$2" "$3" 2>/dev/null | tr '\n' '/') chk "$b" "$a" "$4" } bareio() { # clock, RAM file, random, BOARD, MW, a long echo, and exit status local mw mw=$(GENMAP=$T/genmap bash src/tools/automap.sh run $T/run-bareio src/runtime/run.zc tests/programs/bareio.zc | sed -n 's/.*MW \([0-9]*\) .*/\1/p') local t="$1" pay a b out st pay=abcdefghijklmnopqrstuvwxyz0123456789 a=$( (cat examples/echo.zc; printf '`%s' "$pay") | $T/zy | tr -d '\n') b=$(printf '%s' "$pay" | bash src/baremetal/run.sh "$t" io examples/echo.zc 2>/dev/null | tr -d '\n') chk "$b" "$a" "bare $t echo until eof" b=$(bash src/baremetal/run.sh "$t" run tests/programs/bareio.zc 2>/dev/null | tr -d '\n') chk "$b" "tick 2 zy rand board $mw" "bare $t clock, files, random, memory" out=$(bash src/baremetal/run.sh "$t" run tests/programs/bareexit.zc 2>/dev/null) st=$? chk "$(printf '%s' "$out" | tr -d '\n')" "bye" "bare $t exit status text" chk "$st" "3" "bare $t exit status code" } if bash src/baremetal/run.sh ready riscv; then bare riscv run examples/hello.zc "bare riscv hello" bare riscv run examples/fact.zc "bare riscv fact" bare riscv run tests/programs/wide.zc "bare riscv wide" bare riscv run tests/programs/gc.zc "bare riscv gc" bare riscv run tests/programs/float.zc "bare riscv float" bare riscv run tests/programs/ftruth.zc "bare riscv negative zero" bare riscv run tests/programs/ftext.zc "bare riscv float text" bare riscv run tests/programs/bytes.zc "bare riscv bytes" bare riscv run tests/programs/strings.zc "bare riscv strings" bare riscv run tests/programs/share.zc "bare riscv share" bare riscv compile tests/programs/float.zc "bare riscv compile float" bare riscv compile tests/programs/ftruth.zc "bare riscv compile negative zero" bare riscv compile tests/programs/ftype.zc "bare riscv compile float type" bare riscv compile tests/programs/fo3.zc "bare riscv compile o3 floats" bare riscv compile tests/programs/ftext.zc "bare riscv compile float text" bare riscv compile tests/programs/bytes.zc "bare riscv compile bytes" bare riscv compile tests/programs/strings.zc "bare riscv compile strings" bare riscv compile tests/programs/share.zc "bare riscv compile share" bare riscv compile examples/hello.zc "bare riscv compile hello" bare riscv compile examples/fact.zc "bare riscv compile fact" bare riscv compile tests/programs/wide.zc "bare riscv compile wide" bareio riscv else echo "skip baremetal riscv (no riscv64-linux-gnu-gcc or qemu-system-riscv64)"; fi if bash src/baremetal/run.sh ready aarch64; then bare aarch64 run examples/hello.zc "bare aarch64 hello" bare aarch64 run examples/fact.zc "bare aarch64 fact" bare aarch64 run tests/programs/wide.zc "bare aarch64 wide" bare aarch64 run tests/programs/gc.zc "bare aarch64 gc" bare aarch64 run tests/programs/float.zc "bare aarch64 float" bare aarch64 run tests/programs/ftruth.zc "bare aarch64 negative zero" bare aarch64 run tests/programs/ftext.zc "bare aarch64 float text" bare aarch64 run tests/programs/bytes.zc "bare aarch64 bytes" bare aarch64 run tests/programs/strings.zc "bare aarch64 strings" bare aarch64 run tests/programs/share.zc "bare aarch64 share" bare aarch64 compile tests/programs/float.zc "bare aarch64 compile float" bare aarch64 compile tests/programs/ftruth.zc "bare aarch64 compile negative zero" bare aarch64 compile tests/programs/ftype.zc "bare aarch64 compile float type" bare aarch64 compile tests/programs/fo3.zc "bare aarch64 compile o3 floats" bare aarch64 compile tests/programs/ftext.zc "bare aarch64 compile float text" bare aarch64 compile tests/programs/bytes.zc "bare aarch64 compile bytes" bare aarch64 compile tests/programs/strings.zc "bare aarch64 compile strings" bare aarch64 compile tests/programs/share.zc "bare aarch64 compile share" bare aarch64 compile examples/hello.zc "bare aarch64 compile hello" bare aarch64 compile examples/fact.zc "bare aarch64 compile fact" bare aarch64 compile tests/programs/wide.zc "bare aarch64 compile wide" bareio aarch64 else echo "skip baremetal aarch64 (no aarch64-linux-gnu-gcc or qemu-system-aarch64)"; fi } s_jit() { T=$ROOT/jit if command -v aarch64-linux-gnu-gcc >/dev/null && command -v qemu-aarch64 >/dev/null; then aarch64-linux-gnu-gcc -pthread -O2 -w -fwrapv -DJTYPE=int -static src/core/core.c src/core/io_linux.c $GI -o $T/zyj || exit 1 printf '#!/bin/sh\nexec qemu-aarch64 %s "$@"\n' $T/zyj > $T/jit; chmod +x $T/jit run $T/jit "jit" chk "$(ZYTRACE=1 $T/jit < tests/programs/o3.zc 2>&1 | grep -c 'bytes of arm64')" "8" "jit compiles every o3 region to arm64" selfc $T/jit $T/gj.bin; cmp -s $T/g1.bin $T/gj.bin && chk 1 1 "jit self-compile matches" || chk 0 1 "jit self-compile matches" # native calls need a dynamic binary (a static one has no dlsym), run against the aarch64 libc if [ -d /usr/aarch64-linux-gnu/lib ] && aarch64-linux-gnu-gcc -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c $GI -o $T/zyjd -ldl 2>/dev/null; then chk "$(qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "jit native calls and callbacks (extern, cfunc)" chk "$(qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "jit native calls from o3 code, stack arguments" chk "$(qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < tests/programs/cabi_sized.zc | md5sum)" "$(md5sum < tests/programs/cabi_sized.expected)" "jit native calls with f32 and the sized integers" # structs by value: the helper library has to be arm64, and cload reads build/sbv.so from the working directory R=$(pwd); aarch64-linux-gnu-gcc -shared -fPIC -w tests/data/sbv.c -o $T/sbv_arm.so && mkdir -p $T/build && cp $T/sbv_arm.so $T/build/sbv.so chk "$(cd $T && qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < $R/tests/programs/structval.zc)" "34 3520 18 325 810 340" "jit structs by value to a c function (aapcs64: registers, hfa, reference, stack)" chk "$(cd $T && qemu-aarch64 -L /usr/aarch64-linux-gnu $T/zyjd < $R/tests/programs/structret.zc)" "3 4 3.500000 4.000000 24 8 1.000000 3.500000" "jit structs returned from a c function (aapcs64 results)" fi else echo "skip jit (no aarch64 toolchain or qemu)"; fi # the x86-64 jit against the interpreter, every program (about a minute; not in the quick path) if [ "$(uname -s)-$(uname -m)" = Linux-x86_64 ] && [ $QUICK = 0 ]; then $CC -pthread -O2 -w -fwrapv -DJTYPE=int -DZYINTERP src/core/core.c src/core/io_linux.c $GI -o $T/zyi || exit 1 $CC -pthread -O2 -w -fwrapv -DJTYPE=int src/core/core.c src/core/io_linux.c $GI -o $T/zyx || exit 1 bash tests/drivers/jit_x64_test.sh $T/zyi $T/zyx > $T/jit_x64.out 2>&1; r=$? grep '^DIFF' $T/jit_x64.out chk $r 0 "x86-64 jit agrees with the interpreter: $(tail -n 1 $T/jit_x64.out)" chk "$(ZYNOENH=1 $T/zyx < tests/programs/enh_ops.zc | md5sum)" "$(md5sum < tests/programs/enh_ops.expected)" "x86-64 jit with enhancements called through xrun (ZYNOENH=1)" chk "$(ZYTRACE=1 $T/zyx < tests/programs/o3.zc 2>&1 | grep -c 'bytes of x86-64')" "7" "x86-64 jit compiles the o3 regions it can (one has too many live values)" for p in o3 o3copy o3licm o3push o3bytes fo3 o3call o3deep; do chk "$($T/zyx < tests/programs/$p.zc 2>&1)" "$(ZYO3INTERP=1 $T/zyx < tests/programs/$p.zc 2>&1)" "x86-64 o3 code matches the region interpreter (ZYO3INTERP=1): $p" done fi } s_llvm() { T=$ROOT/llvm # llvm-config can exist without the headers and static libs (a runtime-only install), so the gate is a real build llvm_ok=0 # LLVM_CONFIG picks the llvm build when the one on PATH has no headers (e.g. /usr/lib/llvm-20/bin/llvm-config) local lc=${LLVM_CONFIG:-llvm-config} if command -v $lc >/dev/null && $CC -pthread -O2 -w -fwrapv -DJTYPE=int -DZYLLVM $($lc --cflags) src/core/core.c src/core/io_linux.c $GI -o $T/zyl $($lc --ldflags --libs) 2>/dev/null; then llvm_ok=1; fi if [ $llvm_ok = 1 ]; then run $T/zyl "llvm" chk "$(ZYTRACE=1 $T/zyl < tests/programs/o3.zc 2>&1 | grep -c 'compiled by llvm')" "8" "llvm compiles every o3 region" printf '#!/bin/sh\nZYHOT=1 exec %s "$@"\n' $T/zyl > $T/llall; chmod +x $T/llall chk "$($T/zyl < tests/programs/structval.zc)" "34 3520 18 325 810 340" "llvm structs by value (the call goes through the interpreter's native call)" chk "$($T/zyl < tests/programs/structret.zc)" "3 4 3.500000 4.000000 24 8 1.000000 3.500000" "llvm structs returned from a c function" chk "$($T/llall < tests/programs/types.zc | md5sum)" "$($T/zy < tests/programs/types.zc | md5sum)" "llvm compiling every block agrees" chk "$($T/llall < tests/programs/float.zc)" "4.000000 4.500000 3.000000 2.500000 2.500000 0.001500 0.100000 -0.125000 inf -inf nan 1 1 1 0 0 3 -3 3.000000 -2.000000 2" "llvm compiling every block agrees on floats" chk "$($T/llall < tests/programs/ftruth.zc)" "00111000110107" "llvm compiling every block agrees on negative zero" chk "$($T/llall < tests/programs/ftype.zc)" "3.500000 10.500000 3.500000 4.250000 5.250000 4.250000 6.500000 2 4 4.250000 8.500000 1 4" "llvm compiling every block agrees on the float type" chk "$($T/llall < tests/programs/fo3.zc)" "5997 5997 4.000000 nan 0 3.250000 1 0 0" "llvm compiling every block agrees on o3 floats" chk "$($T/llall < tests/programs/ftext.zc)" "1e+308 1e-10 -0 inf -inf nan 0.1 1.50 1.250e+0 100000000000000000000.000000 1e+40 1111111111" "llvm compiling every block agrees on float text" chk "$($T/llall < tests/programs/bytes.zc)" "17 34 43981 16909060 1 205 171 -3674937295934316015 1108152156331 0 0 30768 ABC" "llvm compiling every block agrees on bytes" chk "$($T/llall < tests/programs/strings.zc)" "hi! 3 3 104 i! 1 1 1 -42 42 -42 11 1 -9223372036854775808 0 1 3 10 1 1 1 W ab" "llvm compiling every block agrees on strings" chk "$($T/llall < tests/programs/share.zc)" "1 2 1 4 1 1 1 1 42 1 1 1 6 0" "llvm compiling every block agrees on shared cells" chk "$($T/zyl < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "llvm native calls and callbacks (extern, cfunc)" chk "$($T/llall < tests/programs/cabi.zc | md5sum)" "$(md5sum < tests/programs/cabi.expected)" "llvm compiling every block agrees on native calls" chk "$($T/zyl < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "llvm calls natives from o3 regions directly" chk "$(ZYTRACE=1 $T/zyl < tests/programs/cabi_o3.zc 2>&1 | grep -c 'compiled by llvm')" "2" "llvm compiles the o3 regions that call natives" chk "$($T/llall < tests/programs/cabi_o3.zc | md5sum)" "$(md5sum < tests/programs/cabi_o3.expected)" "llvm compiling every block agrees on o3 native calls" chk "$($T/zyl < tests/programs/cabi_sized.zc | md5sum)" "$(md5sum < tests/programs/cabi_sized.expected)" "llvm native calls with f32 and the sized integers" chk "$($T/llall < tests/programs/sized.zc | md5sum)" "$(md5sum < tests/programs/sized.expected)" "llvm compiling every block agrees on sized integers" chk "$($T/llall < tests/programs/bitops.zc | md5sum)" "$(md5sum < tests/programs/bitops.expected)" "llvm compiling every block agrees on the new micro-ops" chk "$($T/llall < tests/programs/f32.zc | md5sum)" "$(md5sum < tests/programs/f32.expected)" "llvm compiling every block agrees on f32" chk "$($T/llall < tests/programs/sizedvec.zc | md5sum)" "$(md5sum < tests/programs/sizedvec.expected)" "llvm compiling every block agrees on growable sized arrays" chk "$($T/llall < tests/programs/packedfields.zc | md5sum)" "$(md5sum < tests/programs/packedfields.expected)" "llvm compiling every block agrees on packed fields" chk "$($T/llall < tests/programs/generics.zc | md5sum)" "$(md5sum < tests/programs/generics.expected)" "llvm compiling every block agrees on generic instances" chk "$($T/llall < tests/programs/genmeth.zc | md5sum)" "$(md5sum < tests/programs/genmeth.expected)" "llvm compiling every block agrees on generic struct methods" chk "$($T/llall < tests/programs/o3push.zc | md5sum)" "$(md5sum < tests/programs/o3push.expected)" "llvm compiling every block agrees on inline push and pop" selfc $T/zyl $T/gl.bin; cmp -s $T/g1.bin $T/gl.bin && chk 1 1 "llvm self-compile matches" || chk 0 1 "llvm self-compile matches" else echo "skip llvm (no llvm-config, or its headers and libraries are missing)"; fi } s_lsp() { T=$ROOT/lsp # the language server: the compiler itself in check mode, so these are the real compiler's findings if bash src/tools/mklsp.sh $T/lsp >/dev/null 2>&1; then lsp=$T/lsp/zclsp chk "$($lsp < examples/hello.zc)" "no errors" "lsp: a clean program" chk "$($lsp < tests/errors/back_to_m.zc | head -1 | cut -d' ' -f2-)" "error: $(grep -m1 '^// expect: ' tests/errors/back_to_m.zc | sed 's#^// expect: ##')" "lsp: the compiler's own message" h=$($lsp < examples/opt_test.zc) for w in "is never read" "is never reassigned; make it a const" "is a shift; write << 3" "unannotated numeric literal 1234" "new inside a loop" "stack buffer"; do case "$h" in *"$w"*) chk 1 1 "lsp hint: $w";; *) chk 0 1 "lsp hint: $w";; esac done chk "$($lsp < examples/struct_closure_opt.zc | grep -c error)" "0" "lsp: methods and closures check clean" case "$($lsp < tests/data/drop_hint.zc)" in *"value with drop is never bound; it drops at the end of this statement"*) chk 1 1 "lsp hint: a value with drop that is never bound";; *) chk 0 1 "lsp hint: a value with drop that is never bound";; esac case "$($lsp < tests/data/reassign_hint.zc)" in *"p: reassigning frees the old value"*) chk 1 1 "lsp hint: reassigning an owner frees the old value";; *) chk 0 1 "lsp hint: reassigning an owner frees the old value";; esac case "$($lsp < tests/data/spawn_hint.zc)" in *"r: has drop and is live at spawn; the worker's copy drops it too"*) chk 1 1 "lsp hint: a value with drop live at spawn";; *) chk 0 1 "lsp hint: a value with drop live at spawn";; esac # a file of a larger build checks on its own: its bodyless declarations are not errors in check mode for f in src/runtime/rt.zc src/runtime/run.zc src/lsp/server.zc; do chk "$($lsp < $f | grep -c ': error:')" "0" "lsp: $f checks alone"; done # the compiler is several files that only make sense together: check them as one document chk "$(cat $(ccparts) | $lsp | grep -c ': error:')" "0" "lsp: the compiler (src/compiler/parts) checks alone" chk "$($lsp < tests/programs/decl_undefined.zc)" "no errors" "lsp: a bodyless declaration is not an error in check mode" chk "$($lsp < tests/programs/undefined_call.zc | grep -c ': error:')" "1" "lsp: a call to a name nothing defines is still an error" chk "$($lsp --annotate < tests/programs/annotate.zc | md5sum)" "$(md5sum < tests/programs/annotate.expected)" "lsp --annotate writes the types the compiler can see into a program" chk "$($lsp --annotate < tests/programs/annotate.zc | $T/zy)" "$($T/zy < tests/programs/annotate.zc)" "an annotated program means what the original did" chk "$($lsp --types < tests/programs/annotate.zc | head -3 | tr '\n' '/')" "9:1 3 int/10:1 3 char*/11:1 3 int/" "lsp --types lists the edits without making them" chk "$($lsp --annotate < tests/programs/annotate.expected | md5sum)" "$(md5sum < tests/programs/annotate.expected)" "annotating an annotated program changes nothing more" printf 'Content-Length: 40\r\n\r\n{"jsonrpc": [2.0", "method": "x"}' | timeout 10 $lsp > /dev/null 2>&1; chk $? 0 "lsp: a stray brace in an array ends the message, and the server exits" if [ $QUICK = 1 ]; then echo "skip lsp protocol and corpus tests (quick)" elif command -v node >/dev/null; then node tests/drivers/fuzz_lsp.js $lsp > $T/fuzzlsp.out 2>&1; r=$? grep -v '^ok' $T/fuzzlsp.out chk $r 0 "lsp fuzz: malformed recovery, long-session churn and cache invalidation" node tests/drivers/storage_analysis_test.js $lsp > $T/storage.out 2>&1; r=$? grep -v '^ok' $T/storage.out chk $r 0 "lsp: definite storage warnings and reference lifetime hovers" node tests/drivers/hints_test.js $lsp > $T/hints.out 2>&1; r=$? grep -v '^ok' $T/hints.out chk $r 0 "lsp: hints are information diagnostics and obey hints on, small and off" node tests/drivers/stream_test.js $lsp > $T/stream.out 2>&1; r=$? grep -v '^ok' $T/stream.out chk $r 0 "lsp: large checks stream and a stale check is dropped" node tests/drivers/format_test.js $lsp > $T/format.out 2>&1; r=$? grep -v '^ok' $T/format.out chk $r 0 "lsp: the formatter keeps tokens, is stable and follows its options" node tests/drivers/include_test.js $lsp > $T/include.out 2>&1; r=$? grep -v '^ok' $T/include.out chk $r 0 "lsp: relative includes, string diagnostics and explicit std" for group in quickfix modulehover namespacecompletion navigation inference qualifiers; do CONTRACT_GROUP=$group node tests/drivers/toolchain_lsp_contract_test.js $lsp > $T/contracts-$group.out 2>&1; r=$? grep -v '^ok' $T/contracts-$group.out chk $r 0 "lsp: $group contracts" done node tests/drivers/hint_fixes_test.js $lsp > $T/hintfixes.out 2>&1; r=$? grep -v '^ok' $T/hintfixes.out chk $r 0 "lsp: hint and import quick fixes keep the program's meaning" node tests/drivers/codeactions_client_test.js > $T/codeactions-client.out 2>&1; r=$? grep -v '^ok' $T/codeactions-client.out chk $r 0 "lsp: versioned import quick-fix edits" node tests/drivers/completion_test.js $lsp > $T/completion.out 2>&1; r=$? grep -v '^ok' $T/completion.out chk $r 0 "lsp: fuzzy completions, signatures, scope and expired lifetimes" node tests/drivers/analysis_cache_test.js $lsp > $T/analysiscache.out 2>&1; r=$? grep -v '^ok' $T/analysiscache.out chk $r 0 "lsp: analysis cache reuse and invalidation" node tests/drivers/hover_surface_test.js $lsp > $T/hoversurface.out 2>&1; r=$? grep -v '^ok' $T/hoversurface.out chk $r 0 "lsp: embedded declarations, constant values, asm and std hovers" node tests/drivers/nav_test.js $lsp > $T/nav.out 2>&1; r=$? grep -v '^ok' $T/nav.out chk $r 0 "lsp: definition and references across includes" node tests/drivers/lsp_test.js $lsp > $T/lsp.out 2>&1; r=$? grep -v '^ok' $T/lsp.out chk $r 0 "lsp: protocol and corpus tests ($(grep -c '^ok' $T/lsp.out) checks)" node tests/drivers/lsp_live.js $lsp > $T/lsplive.out 2>&1; r=$? grep -v '^ok' $T/lsplive.out chk $r 0 "lsp: the native server answers while the client keeps the pipe open" node tests/drivers/lsp_sandbox.js $lsp > $T/lspsb.out 2>&1; r=$? grep -v '^ok' $T/lspsb.out chk $r 0 "lsp: compile-time code in a checked file cannot write files" bash src/tools/xref_selftest.sh $lsp > $T/xref.out 2>&1; r=$? grep -v '^ok' $T/xref.out chk $r 0 "xref: --check passes on tests/xref.baseline and fails on a missing writer" # the diagnostics cache: its key rules, then the extension's client end to end on the built-in server node tests/drivers/diagcache_test.js > $T/dcache.out 2>&1; r=$? grep -v '^ok' $T/dcache.out chk $r 0 "lsp: the diagnostics cache keys and store ($(grep -c '^ok' $T/dcache.out) checks)" # the same server as WebAssembly, through the extension's host (needs clang) if sh src/tools/findcc.sh --wasm >/dev/null 2>&1 && LSP_BUILD_DIR=$T/lsp LSP_REUSE_BUILD=1 bash src/tools/mklspwasm.sh $T/lsp/lsp.wasm >/dev/null 2>&1; then node tests/drivers/hover_surface_test.js --wasm $T/lsp/lsp.wasm > $T/hoversurfacewasm.out 2>&1; r=$? grep -v '^ok' $T/hoversurfacewasm.out chk $r 0 "lsp: embedded and asm hovers through the wasm host" node tests/drivers/completion_test.js --wasm $T/lsp/lsp.wasm > $T/completionwasm.out 2>&1; r=$? grep -v '^ok' $T/completionwasm.out chk $r 0 "lsp: completions through the wasm host" node tests/drivers/lsp_wasm.js $T/lsp/lsp.wasm > $T/lspw.out 2>&1; r=$? grep -v '^ok' $T/lspw.out chk $r 0 "lsp: wasm server through the extension's host ($(grep -c '^ok' $T/lspw.out) checks)" else echo "skip lsp wasm tests (no clang with wasm32)"; fi # the same server run by the javascript vm, the extension's fallback when wasm is not there. # the vm starts at the image's heap and grows on the heap's request, so a long document is the test node src/js/extract_lsp.js --lsp $T/lsp --out $T/vmserver >/dev/null && node src/js/mklayout.js --map $MAP/memmap.h --out $T/vmserver >/dev/null \ && tsc src/js/tinyvm.ts --target es2020 --lib es2020,dom --module commonjs --outDir $T/vmserver node tests/drivers/hover_surface_test.js --vm $T/vmserver > $T/hoversurfacevm.out 2>&1; r=$? grep -v '^ok' $T/hoversurfacevm.out chk $r 0 "lsp: embedded and asm hovers through the javascript host" node tests/drivers/completion_test.js --vm $T/vmserver > $T/completionvm.out 2>&1; r=$? grep -v '^ok' $T/completionvm.out chk $r 0 "lsp: completions through the javascript host" node tests/drivers/lsp_vm.js $T/vmserver > $T/lspvm.out 2>&1; r=$? grep -v '^ok' $T/lspvm.out chk $r 0 "lsp: the server in the javascript vm, through the extension's host ($(grep -c '^ok' $T/lspvm.out) checks)" if [ -f $T/lsp/lsp.wasm ]; then node tests/drivers/stream_host_test.js $T/lsp/lsp.wasm $T/vmserver > $T/streamhost.out 2>&1; r=$? grep -v '^ok' $T/streamhost.out chk $r 0 "lsp: the wasm and vm servers stream and drop stale checks" fi # the extension's client end to end, with the server files the package would carry mkdir -p $T/extserver && cp $T/vmserver/tinyvm.js $T/vmserver/layout.js $T/vmserver/lsp.json $T/vmserver/lsp.bin $T/extserver/ [ -f $T/lsp/lsp.wasm ] && cp $T/lsp/lsp.wasm $T/extserver/ node tests/drivers/ext_cache_test.js $T/extserver > $T/ecache.out 2>&1; r=$? grep -v '^ok' $T/ecache.out chk $r 0 "lsp: the extension's cache, end to end ($(grep -c '^ok' $T/ecache.out) checks)" else echo "skip lsp protocol tests (no node)"; fi else chk 0 1 "lsp server builds"; fi } s_js() { T=$ROOT/js # the javascript vm, and the microcore as webassembly. both run the same genome if command -v node >/dev/null && command -v tsc >/dev/null; then # the vm as make js builds it, with the fixpoint genome beside it node src/js/mklayout.js --map $MAP/memmap.h --out $T/jsvm >/dev/null && cp $ROOT/s1/genome.bin $T/jsvm/genome.bin \ && tsc src/js/tinyvm.ts --target es2020 --lib es2020,dom --module commonjs --outDir $T/jsvm || chk 0 1 "js vm builds" chk "$(node $T/jsvm/tinyvm.js examples/hello.zc)" "hello, world!" "js vm: hello" 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 GENOME_DIR=$ROOT/s1 MAP_DIR=$MAP bash src/web/build.sh $T/zc.wasm >/dev/null && node tests/drivers/wasm_test.js $T/zc.wasm > $T/wasm.out 2>&1; r=$? grep -v '^ok' $T/wasm.out chk $r 0 "wasm: the microcore in webassembly ($(grep -c '^ok' $T/wasm.out) checks)" node tests/drivers/fuzz_wasm.js $T/zc.wasm > $T/fuzzw.out 2>&1; r=$? grep -v '^ok' $T/fuzzw.out chk $r 0 "wasm: compiler/runtime/stdlib differential fuzz" else echo "skip wasm (no clang with the wasm32 target and #embed, or no wasm-ld)"; fi else echo "skip js and wasm (no node or tsc)"; fi } s_make() { T=$ROOT/make # the makefile builds the programs and the run-only image tool if command -v make >/dev/null; then # from an empty build directory, as a fresh checkout would be make -s B=$T/mbuild all >/dev/null 2>&1 && chk "$($T/mbuild/bin/zy < examples/hello.zc)" "hello, world!" "make: build/bin/zy runs a program" || chk 0 1 "make: all builds" python3 tests/drivers/cli_help_test.py --group binaries --bin-dir $T/mbuild/bin > $T/help.out 2>&1; r=$? grep -v '^ok' $T/help.out chk $r 0 "command-line help, invalid options and existing lsp source modes" python3 tests/drivers/cli_help_test.py --group scripts > $T/scripthelp.out 2>&1; r=$? grep -v '^ok' $T/scripthelp.out chk $r 0 "build-script help exits without reading input or starting a build" make -s B=$T/mbuild run PROG=examples/hello.zc >/dev/null 2>&1 && chk "$($T/mbuild/run/zymr > $T/log.$n pids=("${pids[@]:1}"); names=("${names[@]:1}") } sec(){ # $1 = the name the section reports under, $2 = the function to run local n=$1 "$2" > $T/log.$n 2>&1 & pids+=($!); names+=("$n") [ ${#pids[@]} -ge $JOBS ] && reap return 0 } # the bootstrap's own checks stay counted; the sections are tallied from their # logs below, so a section that dies half way still reports what it got through. b_ok=$ok; b_bad=$bad; ok=0; bad=0 for n in $SECS; do sec $n s_$n; done while [ ${#pids[@]} -gt 0 ]; do reap; done for n in $SECS; do cat $T/log.$n ok=$((ok + $(grep -c '^ok ' $T/log.$n))) bad=$((bad + $(grep -c '^FAIL' $T/log.$n))) done ok=$((ok + b_ok)); bad=$((bad + b_bad)) echo "$ok passed, $bad failed"; [ $bad = 0 ]