aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/Makefile41
-rw-r--r--tests/fib/fib.fwd24
-rw-r--r--tests/fib/test.mk1
-rw-r--r--tests/guard/guard.fwd21
-rw-r--r--tests/guard/test.mk1
-rw-r--r--tests/opt_group/opt_group.fwd14
-rw-r--r--tests/opt_group/test.mk1
-rw-r--r--tests/ptrs/ptrs.fwd8
-rw-r--r--tests/ptrs/test.mk1
-rwxr-xr-xtests/scripts/gen-xfail23
-rwxr-xr-xtests/scripts/gen-xpass12
-rw-r--r--tests/scripts/makefile27
-rwxr-xr-xtests/scripts/run-xfail58
-rwxr-xr-xtests/scripts/run-xpass53
-rw-r--r--tests/sum/sum.fwd28
-rw-r--r--tests/sum/test.mk1
-rw-r--r--tests/type_mismatch/test.mk1
-rw-r--r--tests/type_mismatch/type_mismatch.fwd5
-rw-r--r--tests/vec/test.mk1
-rw-r--r--tests/vec/vec.fwd153
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();
+}