aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-08-30 21:21:53 +0200
committerhachem <im@hachem.wtf>2026-08-30 21:21:53 +0200
commit294777178f9203283364f3e672569becb4dd1e7c (patch)
treeb97447843d06f8f63a01505c85e6e9f9e804b797
parent13704a1df5c15af7163cc7764b0aeebc81ad2bdd (diff)
test: add automated docker testing for the whole test suite
-rw-r--r--README.md10
-rw-r--r--examples/arithmetic.hdass13
-rw-r--r--examples/branch.hdass17
-rw-r--r--examples/call.hdass13
-rw-r--r--examples/exit_code.hdass9
-rw-r--r--examples/greet.hdass20
-rw-r--r--examples/loop_sum.hdass18
-rwxr-xr-xscripts/docker_test.sh18
-rwxr-xr-xscripts/test_examples.sh76
9 files changed, 194 insertions, 0 deletions
diff --git a/README.md b/README.md
index e624e5b..4a69904 100644
--- a/README.md
+++ b/README.md
@@ -64,6 +64,16 @@ ld hello.o -o hello
./hello
```
+To transpile, assemble, link and run every program in [examples/](examples/) and check its output, use the end-to-end test script (also from inside the container):
+```bash
+make config=debug && ./scripts/test_examples.sh
+```
+
+Or, from the host, run the whole suite (unit tests plus the example tests) in one shot, bringing the container up if needed:
+```bash
+./scripts/docker_test.sh
+```
+
When you're finished, stop and remove the container:
```bash
docker compose down
diff --git a/examples/arithmetic.hdass b/examples/arithmetic.hdass
new file mode 100644
index 0000000..359653e
--- /dev/null
+++ b/examples/arithmetic.hdass
@@ -0,0 +1,13 @@
+// Computes (5 + 3) * 2 - 1 = 15 and returns it as the exit status.
+const SYS_EXIT = 60
+
+proc main
+{
+ rdi = 5
+ rdi += 3
+ rdi *= 2
+ rdi -= 1
+
+ rax = SYS_EXIT
+ syscall
+}
diff --git a/examples/branch.hdass b/examples/branch.hdass
new file mode 100644
index 0000000..1218773
--- /dev/null
+++ b/examples/branch.hdass
@@ -0,0 +1,17 @@
+// Returns the larger of two values (max(3, 8) = 8) as the exit status.
+const SYS_EXIT = 60
+
+proc main
+{
+ rbx = 3
+ rcx = 8
+
+ rdi = rbx
+ if rcx <= rbx
+ goto done
+ rdi = rcx
+
+done:
+ rax = SYS_EXIT
+ syscall
+}
diff --git a/examples/call.hdass b/examples/call.hdass
new file mode 100644
index 0000000..239a26b
--- /dev/null
+++ b/examples/call.hdass
@@ -0,0 +1,13 @@
+// Passes an argument through a proc parameter, which exits with that value.
+const SYS_EXIT = 60
+
+proc exit_with(code: rdi)
+{
+ rax = SYS_EXIT
+ syscall
+}
+
+proc main
+{
+ exit_with(21)
+}
diff --git a/examples/exit_code.hdass b/examples/exit_code.hdass
new file mode 100644
index 0000000..7c44df9
--- /dev/null
+++ b/examples/exit_code.hdass
@@ -0,0 +1,9 @@
+// Exits with status 42 and no output.
+const SYS_EXIT = 60
+
+proc main
+{
+ rax = SYS_EXIT
+ rdi = 42
+ syscall
+}
diff --git a/examples/greet.hdass b/examples/greet.hdass
new file mode 100644
index 0000000..a253b5e
--- /dev/null
+++ b/examples/greet.hdass
@@ -0,0 +1,20 @@
+// Writes a fixed string to stdout, then exits cleanly.
+const SYS_WRITE = 1
+const SYS_EXIT = 60
+
+const STDOUT = 1
+
+data greeting = "hdass works!\n"
+
+proc main
+{
+ rax = SYS_WRITE
+ rdi = STDOUT
+ rsi = greeting
+ rdx = greeting.len
+ syscall
+
+ rax = SYS_EXIT
+ rdi = 0
+ syscall
+}
diff --git a/examples/loop_sum.hdass b/examples/loop_sum.hdass
new file mode 100644
index 0000000..bb8d115
--- /dev/null
+++ b/examples/loop_sum.hdass
@@ -0,0 +1,18 @@
+// Sums 5 + 4 + 3 + 2 + 1 = 15 with a countdown loop, returns it as the status.
+const SYS_EXIT = 60
+
+proc main
+{
+ rbx = 0 // running total
+ rcx = 5 // counter
+
+loop:
+ rbx += rcx
+ rcx -= 1
+ if rcx != 0
+ goto loop
+
+ rdi = rbx
+ rax = SYS_EXIT
+ syscall
+}
diff --git a/scripts/docker_test.sh b/scripts/docker_test.sh
new file mode 100755
index 0000000..93f0bca
--- /dev/null
+++ b/scripts/docker_test.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+# Convenience wrapper: build hdass and run the full test suite (unit tests plus
+# the end-to-end example tests) inside the amd64 Linux Docker environment.
+# Run this from the host; it brings the container up if it is not already.
+set -euo pipefail
+
+root="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$root"
+
+docker compose up -d
+
+docker compose exec -T hdass bash -c '
+ set -e
+ premake5 gmake
+ make config=debug
+ ./bin/debug-linux/tests
+ ./scripts/test_examples.sh
+'
diff --git a/scripts/test_examples.sh b/scripts/test_examples.sh
new file mode 100755
index 0000000..2fb4e5f
--- /dev/null
+++ b/scripts/test_examples.sh
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+# End-to-end example tests: transpile each program with hdass, assemble it with
+# nasm, link it with ld, run it, and compare its stdout and exit status against
+# the expected values below.
+#
+# The generated programs use Linux x86-64 syscalls, so this must run in the
+# amd64 Linux environment (see the Docker setup in the README), not on macOS.
+set -u
+
+root="$(cd "$(dirname "$0")/.." && pwd)"
+cd "$root"
+
+hdass="${HDASS:-./bin/debug-linux/hdass}"
+if [ ! -x "$hdass" ]; then
+ echo "error: '$hdass' not found; build it first with:" >&2
+ echo " premake5 gmake && make config=debug" >&2
+ exit 1
+fi
+
+for tool in nasm ld; do
+ if ! command -v "$tool" >/dev/null 2>&1; then
+ echo "error: '$tool' not found; run this inside the Docker environment" >&2
+ exit 1
+ fi
+done
+
+work="$(mktemp -d)"
+trap 'rm -rf "$work"' EXIT
+
+pass=0
+fail=0
+
+# check <name> <source> <expected_exit> <expected_stdout>
+# expected_stdout is compared after trailing newlines are stripped (as $() does).
+check()
+{
+ local name="$1" source="$2" expected_exit="$3" expected_stdout="$4"
+ local asm="$work/$name.asm" obj="$work/$name.o" bin="$work/$name"
+
+ if ! "$hdass" "$source" -o "$asm" 2>"$work/err"; then
+ echo "FAIL $name (transpile)"; cat "$work/err"; fail=$((fail + 1)); return
+ fi
+ if ! nasm -f elf64 "$asm" -o "$obj" 2>"$work/err"; then
+ echo "FAIL $name (assemble)"; cat "$work/err"; fail=$((fail + 1)); return
+ fi
+ if ! ld "$obj" -o "$bin" 2>"$work/err"; then
+ echo "FAIL $name (link)"; cat "$work/err"; fail=$((fail + 1)); return
+ fi
+
+ local actual_stdout actual_exit
+ actual_stdout="$("$bin")"
+ actual_exit=$?
+
+ if [ "$actual_exit" != "$expected_exit" ] || [ "$actual_stdout" != "$expected_stdout" ]; then
+ echo "FAIL $name"
+ echo " expected: exit=$expected_exit stdout=$(printf '%q' "$expected_stdout")"
+ echo " actual: exit=$actual_exit stdout=$(printf '%q' "$actual_stdout")"
+ fail=$((fail + 1))
+ return
+ fi
+
+ echo "PASS $name"
+ pass=$((pass + 1))
+}
+
+check hello_world examples/hello_world.hdass 0 "Hello, World!"
+check greet examples/greet.hdass 0 "hdass works!"
+check exit_code examples/exit_code.hdass 42 ""
+check arithmetic examples/arithmetic.hdass 15 ""
+check loop_sum examples/loop_sum.hdass 15 ""
+check branch examples/branch.hdass 8 ""
+check call examples/call.hdass 21 ""
+
+echo
+echo "$pass passed, $fail failed"
+[ "$fail" -eq 0 ]