aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorKimplul <kimi.h.kuparinen@gmail.com>2026-05-01 22:16:32 +0300
committerKimplul <kimi.h.kuparinen@gmail.com>2026-05-01 22:16:43 +0300
commitdb6de6955dfef602d2cc708767cad39268b3a91c (patch)
tree095c9e30ff4e7dd01b54e5b110dd95351e55cff9 /tests
parent8946c27306abed7065afad3f015df5ee81e72ad2 (diff)
downloadfwd-db6de6955dfef602d2cc708767cad39268b3a91c.tar.gz
fwd-db6de6955dfef602d2cc708767cad39268b3a91c.zip
add vec test
+ Not necessarily a super great test, since it goes over a lot of existing functionality
Diffstat (limited to 'tests')
-rw-r--r--tests/vec/test.mk1
-rw-r--r--tests/vec/vec.fwd153
2 files changed, 154 insertions, 0 deletions
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();
+}