diff options
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/Makefile | 41 | ||||
| -rw-r--r-- | tests/fib/fib.fwd | 24 | ||||
| -rw-r--r-- | tests/fib/test.mk | 1 | ||||
| -rw-r--r-- | tests/guard/guard.fwd | 21 | ||||
| -rw-r--r-- | tests/guard/test.mk | 1 | ||||
| -rw-r--r-- | tests/opt_group/opt_group.fwd | 14 | ||||
| -rw-r--r-- | tests/opt_group/test.mk | 1 | ||||
| -rw-r--r-- | tests/ptrs/ptrs.fwd | 8 | ||||
| -rw-r--r-- | tests/ptrs/test.mk | 1 | ||||
| -rwxr-xr-x | tests/scripts/gen-xfail | 23 | ||||
| -rwxr-xr-x | tests/scripts/gen-xpass | 12 | ||||
| -rw-r--r-- | tests/scripts/makefile | 27 | ||||
| -rwxr-xr-x | tests/scripts/run-xfail | 58 | ||||
| -rwxr-xr-x | tests/scripts/run-xpass | 53 | ||||
| -rw-r--r-- | tests/sum/sum.fwd | 28 | ||||
| -rw-r--r-- | tests/sum/test.mk | 1 | ||||
| -rw-r--r-- | tests/type_mismatch/test.mk | 1 | ||||
| -rw-r--r-- | tests/type_mismatch/type_mismatch.fwd | 5 | ||||
| -rw-r--r-- | tests/vec/test.mk | 1 | ||||
| -rw-r--r-- | tests/vec/vec.fwd | 153 |
20 files changed, 474 insertions, 0 deletions
diff --git a/tests/Makefile b/tests/Makefile new file mode 100644 index 0000000..81cc205 --- /dev/null +++ b/tests/Makefile @@ -0,0 +1,41 @@ +export VALGRIND ?= 1 + +check: + $(MAKE) -f scripts/makefile check + +.DEFAULT: + $(MAKE) -f scripts/makefile $< + +TESTS != find . -name test.mk +DO != echo -n > tests.mk && rm -rf reports + +# collect tests that are expected to pass into XPASS, each testcase is assumed +# to be in a separate directory, let's call it $NAME, and have a $NAME/test.mk +# file which just appends the $NAME of to XPASS if the test should pass, so +# +# XPASS += $NAME +# +# or to XFAIL along, with the expected error message with the format +# +# XFAIL += $NAME,'error message' +# +# Note that it's very important to use single quotes here, otherwise Make might +# start splitting the error message along spaces into arguments. +# +# Each test is assumed to have a fwd file called $NAME/$NAME.fwd, which should +# exercise some part of the compiler. The test framework links all libraries in +# `../mod` into the binary automatically. Test logs, along with some other +# useful stuff, is written out to `reports/$NAME`. +XPASS := +XFAIL := + +include $(TESTS) + +DO != ./scripts/gen-xpass $(XPASS) +DO != ./scripts/gen-xfail $(XFAIL) + +RM ?= rm + +.PHONY: clean +clean: + $(RM) -rf reports diff --git a/tests/fib/fib.fwd b/tests/fib/fib.fwd new file mode 100644 index 0000000..e07d8af --- /dev/null +++ b/tests/fib/fib.fwd @@ -0,0 +1,24 @@ +import "../../mod/libfwdio.so" + +fib(i64 n, (i64) res) +{ + if n < 2 { + res(1); + } else { + fib(n - 1) => i64 f1; + fib(n - 2) => i64 f2; + res(f1 + f2); + } +} + +main() +{ + fib(20) => i64 n; + if n == 10946 { + fwdprint_str("OK\n"); + } else { + fwdprint_str("expected 10946, got "); + fwdprint_i64(n); + fwdprint_nl(); + } +} diff --git a/tests/fib/test.mk b/tests/fib/test.mk new file mode 100644 index 0000000..3ace25e --- /dev/null +++ b/tests/fib/test.mk @@ -0,0 +1 @@ +XPASS += fib diff --git a/tests/guard/guard.fwd b/tests/guard/guard.fwd new file mode 100644 index 0000000..dbde0f3 --- /dev/null +++ b/tests/guard/guard.fwd @@ -0,0 +1,21 @@ +import "../../mod/libfwdio.so" + +guard(bool cond, () err | () ok) +{ + if cond { + err(); + } else { + ok(); + } +} + +main() +{ + guard(1 > 2) => { + fwdprint_str("guard or comparison broken"); + fwdprint_nl(); + } => ; + + fwdprint_str("OK"); + fwdprint_nl(); +} diff --git a/tests/guard/test.mk b/tests/guard/test.mk new file mode 100644 index 0000000..3214e3c --- /dev/null +++ b/tests/guard/test.mk @@ -0,0 +1 @@ +XPASS += guard diff --git a/tests/opt_group/opt_group.fwd b/tests/opt_group/opt_group.fwd new file mode 100644 index 0000000..e520c70 --- /dev/null +++ b/tests/opt_group/opt_group.fwd @@ -0,0 +1,14 @@ +do_something(() a | () b) +{ + a(); + + /* should fail, since either a or b should be called, but not both */ + b(); +} + +main() +{ + do_something() + => {} + => {} +} diff --git a/tests/opt_group/test.mk b/tests/opt_group/test.mk new file mode 100644 index 0000000..c51cdec --- /dev/null +++ b/tests/opt_group/test.mk @@ -0,0 +1 @@ +XFAIL += opt_group,'opt_group.fwd:6:2: error: using moved value' diff --git a/tests/ptrs/ptrs.fwd b/tests/ptrs/ptrs.fwd new file mode 100644 index 0000000..fbb9cbb --- /dev/null +++ b/tests/ptrs/ptrs.fwd @@ -0,0 +1,8 @@ +import "../../mod/libfwdmem.so" + +main() +{ + fwdmalloc(sizeof(i64)) => *i64 i; + /* deref not allowed on raw pointers */ + i* + 20 => int something; +} diff --git a/tests/ptrs/test.mk b/tests/ptrs/test.mk new file mode 100644 index 0000000..3003a8c --- /dev/null +++ b/tests/ptrs/test.mk @@ -0,0 +1 @@ +XFAIL += ptrs,'deref of raw ptr not allowed' diff --git a/tests/scripts/gen-xfail b/tests/scripts/gen-xfail new file mode 100755 index 0000000..a04f8d9 --- /dev/null +++ b/tests/scripts/gen-xfail @@ -0,0 +1,23 @@ +#!/bin/sh + +mkdir -p $(for d in "${@}"; do echo "$d"; done \ + | sed "s|,.*||" | uniq | sed "s|^|reports/|") + +for s in "${@}" +do + NAME=${s%%,*} + EMSG=${s#${NAME},} + echo ".PHONY: $NAME" >> tests.mk + echo "$NAME:" >> tests.mk + echo " ./scripts/run-xfail $NAME '$EMSG'" >> tests.mk +done + +echo -n "TESTS +=" >> tests.mk +for s in "${@}" +do + NAME=${s%%,*} + echo -n " $NAME" >> tests.mk +done + +# append newline +echo "" >> tests.mk diff --git a/tests/scripts/gen-xpass b/tests/scripts/gen-xpass new file mode 100755 index 0000000..8ec383f --- /dev/null +++ b/tests/scripts/gen-xpass @@ -0,0 +1,12 @@ +#!/bin/sh + +mkdir -p $(for d in "${@}"; do echo "$d"; done | uniq | sed "s|^|reports/|") + +for s in "${@}" +do + echo ".PHONY: $s" >> tests.mk + echo "$s:" >> tests.mk + echo " ./scripts/run-xpass $s" >> tests.mk +done + +echo "TESTS += " "${@}" >> tests.mk diff --git a/tests/scripts/makefile b/tests/scripts/makefile new file mode 100644 index 0000000..e6ea394 --- /dev/null +++ b/tests/scripts/makefile @@ -0,0 +1,27 @@ +.PHONY: all +all: check + +COMPILE_TEST := $(CC) -L../mod -I../include -I../lib -Wl,-rpath=../mod -O2 +TEST_LIBS := -lfwdio -lfwdmem -lfwdutil + +export COMPILE_TEST +export TEST_LIBS + +TESTS := +include tests.mk + +.PHONY: check +check: $(TESTS) + @cd reports; OK=1; for d in * ; do \ + if [ ! -f "$$d/OK" ]; then \ + echo "BROKEN: $$d" ; \ + elif [ "$$(tail -n1 $$d/OK)" != "OK" ]; then \ + OK=0 ; \ + echo "FAIL: $$d" ; \ + fi \ + done; \ + if [ "$$OK" != 1 ]; then \ + echo "There were failing tests"; \ + exit 1; \ + fi + @echo "Done." diff --git a/tests/scripts/run-xfail b/tests/scripts/run-xfail new file mode 100755 index 0000000..35bcc75 --- /dev/null +++ b/tests/scripts/run-xfail @@ -0,0 +1,58 @@ +#!/bin/sh +NAME="$1" +EMSG="$2" + +try_vg() { + if [ "x$VALGRIND" != "x0" ] && which valgrind > /dev/null >&1; then + eval "valgrind -q --leak-check=full --error-exitcode=169 $1" + else + eval "$1" + fi + + RETVAL=$? + return "$RETVAL" +} + +I=0 +while :; do + if try_vg "../fwd $NAME/$NAME.fwd" \ + 1> "reports/$NAME/gen.c" \ + 2> "reports/$NAME/run-$I" + then + # success was not expected + echo "SUCC" > reports/$NAME/OK + exit 0 + fi + + if [ "$RETVAL" = 169 ]; then + echo "VALGRIND" > reports/$NAME/OK + + # don't return on an error so that makefile continues running + # other tests that are maybe failing, and reports the status + # after all tests have been run + exit 0 + fi + + # correctly handled coverage exception, continue to next run + # here we check for the special 'internal error' which should be + # reported if the error was handled gracefully, otherwise since the + # error message is not the one we expected and not a covsrv error, we + # don't know what it is and could end up in an infinite loop if we don't + # stop here. + if grep 'COVSRV: EXIT' "reports/$NAME/gen.c" > /dev/null \ + && grep 'internal error:' "reports/$NAME/run-$I" > /dev/null; then + I=$((I+1)) + continue + fi + + if grep "$EMSG" "reports/$NAME/run-$I" > /dev/null; then + # we exited with the expected error message and without leaking + # memory, presume covsrv is happy as well + echo "OK" > reports/$NAME/OK + exit 0 + fi + + # incorrectly handled coverage exception, stop run + echo "MEMERR" > reports/$NAME/OK + exit 0 +done diff --git a/tests/scripts/run-xpass b/tests/scripts/run-xpass new file mode 100755 index 0000000..93540c9 --- /dev/null +++ b/tests/scripts/run-xpass @@ -0,0 +1,53 @@ +#!/bin/sh +NAME="$1" + +if [ -z "${COMPILE_TEST+x}" -o -z "${TEST_LIBS+x}" ]; then + echo "Test runner script must have COMPILE_TEST and TEST_LIBS defined." + exit 1 +fi + +try_vg() { + if [ "x$VALGRIND" != "x0" ] && which valgrind > /dev/null >&1; then + eval "valgrind -q --leak-check=full --error-exitcode=169 $1" + else + eval "$1" + fi + + RETVAL=$? + return "$RETVAL" +} + +I=0 +while :; do + if try_vg "../fwd $NAME/$NAME.fwd" \ + 1> "reports/$NAME/gen.c" \ + 2> "reports/$NAME/run-$I" + then + # fwd succeeded, compile and run program to check result + # remove possible COVSRV lines in output + sed -i "s/COVSRV:.*$//" "reports/$NAME/gen.c" + eval "$COMPILE_TEST reports/$NAME/gen.c -o reports/$NAME/$NAME $TEST_LIBS" + ./reports/$NAME/$NAME > reports/$NAME/OK 2>&1 + exit 0 + fi + + if [ "$RETVAL" = 169 ]; then + echo "VALGRIND" > reports/$NAME/OK + + # don't return on an error so that makefile continues running + # other tests that are maybe failing, and reports the status + # after all tests have been run + exit 0 + fi + + # correctly handled coverage exception, continue to next run + if grep 'COVSRV: EXIT' "reports/$NAME/gen.c" > /dev/null \ + && grep 'internal error:' "reports/$NAME/run-$I" > /dev/null; then + I=$((I+1)) + continue + fi + + # incorrectly handled coverage exception, stop run + echo "MEMERR" > reports/$NAME/OK + exit 0 +done diff --git a/tests/sum/sum.fwd b/tests/sum/sum.fwd new file mode 100644 index 0000000..05a4632 --- /dev/null +++ b/tests/sum/sum.fwd @@ -0,0 +1,28 @@ +import "../../mod/libfwdio.so" + +sum_inner(i32 s, i32 n, (i32) res) +{ + if n <= 0 as i32 { + res(s); + } else { + sum_inner(s + n, n - 1 as i32, res); + } +} + +sum(i32 n, (i32) res) +{ + sum_inner(0 as i32, n, res); +} + +main() +{ + sum(1000000 as i32) => i32 s; + if s != 1784293664 as i32 { + fwdprint_str("expected 1784293664, got "); + fwdprint_i64(s as i64); + fwdprint_nl(); + } else { + fwdprint_str("OK"); + fwdprint_nl(); + } +} diff --git a/tests/sum/test.mk b/tests/sum/test.mk new file mode 100644 index 0000000..9836826 --- /dev/null +++ b/tests/sum/test.mk @@ -0,0 +1 @@ +XPASS += sum diff --git a/tests/type_mismatch/test.mk b/tests/type_mismatch/test.mk new file mode 100644 index 0000000..3f589d9 --- /dev/null +++ b/tests/type_mismatch/test.mk @@ -0,0 +1 @@ +XFAIL += type_mismatch,'type_mismatch.fwd:4:12: error: type mismatch: i32 vs i64' diff --git a/tests/type_mismatch/type_mismatch.fwd b/tests/type_mismatch/type_mismatch.fwd new file mode 100644 index 0000000..e837740 --- /dev/null +++ b/tests/type_mismatch/type_mismatch.fwd @@ -0,0 +1,5 @@ +takes_i32(i32 n) {} + +main() { + takes_i32(20); +} diff --git a/tests/vec/test.mk b/tests/vec/test.mk new file mode 100644 index 0000000..4120ce5 --- /dev/null +++ b/tests/vec/test.mk @@ -0,0 +1 @@ +XPASS += vec diff --git a/tests/vec/vec.fwd b/tests/vec/vec.fwd new file mode 100644 index 0000000..b88d226 --- /dev/null +++ b/tests/vec/vec.fwd @@ -0,0 +1,153 @@ +import "../../mod/libfwdio.so" +import "../../mod/libfwdmem.so" +import "../../mod/libfwdutil.so" + +vec { + u64 n; + u64 s; + *i64 buf; +} + +init_vec(u64 n, (vec) ok) +{ + ok([n => n, 0 as u64 => s, * => buf] vec); +} + +next_s_vec(u64 s, (u64) ok) +{ + if s == 0 as u64 { + ok(1 as u64); + } else { + ok(2 as u64 * s); + } +} + +reserve_vec(vec v, u64 c, (vec) ok) +{ + v => [n => n, s => s, buf => buf]; + + /* expand if we run out of space */ + if c > s { + next_s_vec(s) => s; + + /* note that fwdrealloc creates a new closure, meaning that it + * takes ownership of v.buf, so we can't back out and do a + * single ok(v) at the end of the function */ + fwdrealloc(buf, s * sizeof(i64)) => nbuf; + ok([c => n, s => s, nbuf => buf] vec); + } else { + /* if this were a generic vector we'd have to worry about + * destroying the elements, but since we're just dealing with + * integers this is fine */ + ok([c => n, s => s, buf => buf] vec); + } +} + +set_vec(vec v, i64 i, i64 e, (vec) ok) +{ + v => [n => n, s => s, buf => buf]; + /* oh wait, this is dumb, of course nbuf will never be null */ + buf + i => nbuf; + nil nbuf { + nil nbuf; + fwdfree(buf); + fwdpanic("set_vec, should never happen\n"); + } => dst; + + /* perform actual store */ + e => dst*; + + nil nbuf; + ok([n => n, s => s, buf => buf] vec); +} + +n_vec(vec v, (vec, u64) ok) +{ + v => [n => n, s => s, buf => buf]; + ok([n => n, s => s, buf => buf] vec, n); +} + +append_vec(vec v, i64 e, (vec) ok) +{ + n_vec(v) => v, n; + reserve_vec(v, n + 1 as u64) => v; + set_vec(v, n as i64, e) => v; + ok(v); +} + +at_vec(vec v, u64 i, (vec > &i64) ok) +{ + v => [n => n, s => s, buf => buf]; + guard(i < n) => { + fwdfree(buf); + fwdpanic("at_vec, bounds error\n"); + } => ; + + buf + i => *i64 nbuf; + nil nbuf { + nil nbuf; + fwdfree(buf); + fwdpanic("at_vec, should never happen\n"); + } => &i64 bufr; + + nil nbuf; + ok([n => n, s => s, buf => buf] vec, bufr); +} + +destroy_vec(vec v) +{ + v => [n => n, s => s, buf => buf]; + fwdfree(buf); + nil n; + nil s; +} + +populate_vec(i64 i, i64 n, vec v, (vec) ok) +{ + if i < n { + append_vec(v, i) => vec v; + populate_vec(i + 1, n, v, ok); + } else { + ok(v); + } +} + +guard(bool c, () err | () ok) +{ + if c { + ok(); + } else { + err(); + } +} + +check_vec(i64 i, i64 n, vec v, (vec) ok) +{ + if i < n { + at_vec(v, i as u64) => v, elem; + + /* oh, this doesn't really work with the memory analysis, since + * I can destroy `v` but `elem` is still usable. I guess I could + * transform this such that `elem` is only alive within a + * closure? Not sure. */ + guard(elem* == i) => { + destroy_vec(v); + fwdpanic("check_vec, vec built wrong\n"); + } => ; + + check_vec(i + 1, n, v, ok); + } else { + ok(v); + } +} + +main() +{ + init_vec(0 as u64) => vec v; + populate_vec(0, 1000000, v) => vec v; + check_vec(0, 1000000, v) => vec v; + destroy_vec(v); + + fwdprint_str("OK"); + fwdprint_nl(); +} |
