]> Cypherpunks repositories - keks.git/commitdiff
Initial draft tests
authorSergey Matveev <stargrave@stargrave.org>
Thu, 16 Jan 2025 12:58:42 +0000 (15:58 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sun, 29 Jun 2025 07:30:49 +0000 (10:30 +0300)
14 files changed:
c/t/.gitignore [new file with mode: 0644]
c/t/all.do [new file with mode: 0644]
c/t/bool.t.c [new file with mode: 0644]
c/t/clean [new file with mode: 0755]
c/t/compile_flags.txt.do [new file with mode: 0644]
c/t/conf/.gitignore [new file with mode: 0644]
c/t/conf/tap.rc.do [new file with mode: 0644]
c/t/default.t.do [new file with mode: 0644]
c/t/hexlet.t.c [new file with mode: 0644]
c/t/magic.t.c [new file with mode: 0644]
c/t/nil.t.c [new file with mode: 0644]
c/t/t.list [new file with mode: 0644]
c/t/tai.t.c [new file with mode: 0644]
c/t/tai64.t.c [new file with mode: 0644]

diff --git a/c/t/.gitignore b/c/t/.gitignore
new file mode 100644 (file)
index 0000000..bc084b3
--- /dev/null
@@ -0,0 +1,7 @@
+/bool.t
+/compile_flags.txt
+/hexlet.t
+/magic.t
+/nil.t
+/tai.t
+/tai64.t
diff --git a/c/t/all.do b/c/t/all.do
new file mode 100644 (file)
index 0000000..51a0a18
--- /dev/null
@@ -0,0 +1 @@
+redo-ifchange $(cat t.list)
diff --git a/c/t/bool.t.c b/c/t/bool.t.c
new file mode 100644 (file)
index 0000000..cf90ff0
--- /dev/null
@@ -0,0 +1,65 @@
+#include <stdbool.h>
+#include <stdlib.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
+#include <tap.h>
+#pragma clang diagnostic pop
+
+#include <keks/atom.h>
+#include <keks/enc.h>
+#include <keks/err.h>
+#include <keks/items.h>
+
+static void
+test(
+    bool (*encode)(size_t *len, unsigned char *buf, const size_t cap, const bool v),
+    enum KEKSItemType typ,
+    const bool encodeVal,
+    unsigned char binVal)
+{
+    size_t len = 0;
+    unsigned char buf[] = {0, 0};
+    size_t cap = 0;
+    ok(!encode(&len, buf, cap, encodeVal), "atom small buf");
+    ok(len == 1, "atom small buf len");
+
+    cap = 1;
+    len = 0;
+    ok(encode(&len, buf, cap, encodeVal), "atom encode");
+    ok(len == 1, "atom len");
+    ok(buf[0] == binVal, "atom val");
+
+    struct KEKSItems items;
+    KEKSItemsInit(&items, 1 + 1);
+    size_t off = 0;
+    enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "parse");
+    ok(off == 1, "parse off");
+    ok(items.reallocs == 0, "parse no reallocs");
+    ok(items.len == 1, "parse len");
+    ok(items.offsets[0] == 0, "parse offset");
+    struct KEKSItem item = items.list[0];
+    ok(item.next == 0, "atom next");
+    ok(item.atom.typ == typ, "atom typ");
+
+    cap = 0;
+    off = 0;
+    ok(!KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode small buf");
+    ok(off == 0, "items encode off");
+    cap = sizeof buf;
+    ok(KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode");
+    ok(off == 1, "items encode off");
+    ok(buf[0] == binVal, "items encode val");
+}
+
+int
+main(void)
+{
+    plan(NO_PLAN);
+
+    test(KEKSAtomBoolEncode, KEKSItemFalse, false, 0x02);
+    test(KEKSAtomBoolEncode, KEKSItemTrue, true, 0x03);
+
+    return exit_status();
+}
diff --git a/c/t/clean b/c/t/clean
new file mode 100755 (executable)
index 0000000..84b2fc5
--- /dev/null
+++ b/c/t/clean
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+
+rm -f $(cat t.list) compile_flags.txt
diff --git a/c/t/compile_flags.txt.do b/c/t/compile_flags.txt.do
new file mode 100644 (file)
index 0000000..d35d986
--- /dev/null
@@ -0,0 +1,5 @@
+redo-ifchange ../conf/prefix conf/tap.rc
+read PREFIX <../conf/prefix
+printf "%s\n" "-I$PREFIX/include"
+. conf/tap.rc
+printf "%s\n" "$TAP_CFLAGS"
diff --git a/c/t/conf/.gitignore b/c/t/conf/.gitignore
new file mode 100644 (file)
index 0000000..4cdbe3c
--- /dev/null
@@ -0,0 +1 @@
+/tap.rc
diff --git a/c/t/conf/tap.rc.do b/c/t/conf/tap.rc.do
new file mode 100644 (file)
index 0000000..114257b
--- /dev/null
@@ -0,0 +1,12 @@
+PKGCONF=${PKGCONF:-`command -v pkgconf || command -v pkg-config`}
+cat <<EOF
+{
+    read TAP_CFLAGS
+    read TAP_LDFLAGS
+    read TAP_LDLIBS
+} <<EOF
+EOF
+$PKGCONF --cflags tap
+$PKGCONF --libs-only-L tap
+$PKGCONF --libs-only-l tap
+echo EOF
diff --git a/c/t/default.t.do b/c/t/default.t.do
new file mode 100644 (file)
index 0000000..3bf0b57
--- /dev/null
@@ -0,0 +1,13 @@
+redo-ifchange $1.c \
+    ../conf/cc \
+    ../conf/cflags \
+    ../conf/ldflags \
+    ../conf/prefix \
+    conf/tap.rc
+read CC <../conf/cc
+CFLAGS=$(cat ../conf/cflags)
+LDFLAGS=$(cat ../conf/ldflags)
+read PREFIX <../conf/prefix
+. conf/tap.rc
+$CC $CFLAGS -I$PREFIX/include $TAP_CFLAGS -o $3 $1.c \
+    $LDFLAGS $TAP_LDFLAGS -L$PREFIX/lib -lkeks $TAP_LDLIBS
diff --git a/c/t/hexlet.t.c b/c/t/hexlet.t.c
new file mode 100644 (file)
index 0000000..5a0dbd4
--- /dev/null
@@ -0,0 +1,63 @@
+#include <string.h>
+#include <sys/random.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
+#include <tap.h>
+#pragma clang diagnostic pop
+
+#include <keks/atom.h>
+#include <keks/enc.h>
+#include <keks/err.h>
+#include <keks/items.h>
+
+int
+main(void)
+{
+    plan(NO_PLAN);
+
+    unsigned char hexlet[16] = {0};
+    getrandom(hexlet, 16, 0);
+
+    size_t len = 0;
+    unsigned char buf[16 + 1] = {0};
+    size_t cap = 16;
+    ok(!KEKSAtomHexletEncode(&len, buf, cap, hexlet), "atom small buf");
+    ok(len == 16 + 1, "atom small buf len");
+
+    cap = 16 + 1;
+    len = 0;
+    ok(KEKSAtomHexletEncode(&len, buf, cap, hexlet), "atom encode");
+    ok(len == 16 + 1, "atom len");
+    ok(buf[0] == 0x04, "atom tag");
+    ok(memcmp(buf + 1, hexlet, 16) == 0, "atom val");
+
+    struct KEKSItems items;
+    KEKSItemsInit(&items, 1 + 1);
+    size_t off = 0;
+    enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap - 1);
+    ok(err == KEKSErrNotEnough, "parse not enough");
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "parse");
+    ok(off == 16 + 1, "parse off");
+    ok(items.reallocs == 0, "parse no reallocs");
+    ok(items.len == 1, "parse len");
+    ok(items.offsets[0] == 0, "parse offset");
+    struct KEKSItem item = items.list[0];
+    ok(item.next == 0, "atom next");
+    ok(item.atom.typ == KEKSItemHexlet, "atom typ");
+    ok(memcmp(item.atom.v.str.ptr, hexlet, 16) == 0, "atom val ptr");
+    ok(item.atom.v.str.len == 16, "atom val len");
+
+    cap = 0;
+    off = 0;
+    ok(!KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode small buf");
+    ok(off == 0, "items encode off");
+    cap = sizeof buf;
+    ok(KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode");
+    ok(off == 16 + 1, "items encode off");
+    ok(buf[0] == 0x04, "items encode tag");
+    ok(memcmp(buf + 1, hexlet, 16) == 0, "items encode val");
+
+    return exit_status();
+}
diff --git a/c/t/magic.t.c b/c/t/magic.t.c
new file mode 100644 (file)
index 0000000..a8924b5
--- /dev/null
@@ -0,0 +1,165 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/random.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
+#include <tap.h>
+#pragma clang diagnostic pop
+
+#include <keks/atom.h>
+#include <keks/enc.h>
+#include <keks/err.h>
+#include <keks/items.h>
+
+int
+main(void)
+{
+    plan(NO_PLAN);
+
+    unsigned char magic[12] = {0};
+    getrandom(magic, 12, 0);
+    magic[11] = 123;
+
+    size_t len = 0;
+    unsigned char buf[16] = {0};
+    size_t cap = 16 - 1;
+    ok(!KEKSAtomMagicEncode(&len, buf, cap, magic, 13), "atom too long");
+    ok(len == 0, "atom too long len");
+    ok(!KEKSAtomMagicEncode(&len, buf, cap, magic, 12), "atom small buf");
+    ok(len == 16, "atom small buf len");
+
+    cap = 16;
+    len = 0;
+    ok(KEKSAtomMagicEncode(&len, buf, cap, magic, 12), "atom encode");
+    ok(len == 16, "atom len");
+    ok(memcmp(buf, (const unsigned char *)"KEKS", 4) == 0, "atom tag");
+    ok(memcmp(buf + 4, magic, 12) == 0, "atom val");
+
+    struct KEKSItems items;
+    KEKSItemsInit(&items, 1 + 1);
+    size_t off = 0;
+    enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap - 1);
+    ok(err == KEKSErrNotEnough, "parse not enough");
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "parse");
+    ok(off == 16, "parse off");
+    ok(items.reallocs == 0, "parse no reallocs");
+    ok(items.len == 1, "parse len");
+    ok(items.offsets[0] == 0, "parse offset");
+    struct KEKSItem item = items.list[0];
+    ok(item.next == 0, "atom next");
+    ok(item.atom.typ == KEKSItemMagic, "atom typ");
+    ok(item.atom.v.str.len == 12, "atom typ");
+    ok(memcmp(item.atom.v.str.ptr, magic, 12) == 0, "atom val");
+
+    cap = 0;
+    off = 0;
+    ok(!KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode small buf");
+    ok(off == 0, "items encode off");
+    cap = sizeof buf;
+    ok(KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode");
+    ok(off == 16, "items encode off");
+    ok(memcmp(buf, (const unsigned char *)"KEKS", 4) == 0, "items encode tag");
+    ok(memcmp(buf + 4, magic, 12) == 0, "items encode val");
+
+    cap = sizeof buf;
+    item.atom.v.str.len = 12 + 1;
+    ok(!KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode too long");
+    ok(off == 16, "items encode too long off");
+
+    // ------------------------ >8 ------------------------
+
+    memset(magic + 7, 0, 5);
+
+    cap = 16;
+    len = 0;
+    ok(KEKSAtomMagicEncode(&len, buf, cap, magic, 7), "shorter atom encode");
+    ok(len == 16, "shorter atom len");
+    ok(memcmp(buf, (const unsigned char *)"KEKS", 4) == 0, "shorter atom tag");
+    ok(memcmp(buf + 4, magic, 7) == 0, "shorter atom val");
+    ok(memcmp(buf + 4 + 7, (const unsigned char *)"\x00\x00\x00\x00\x00", 5) == 0,
+       "shorter atom suffix");
+
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "shorter parse");
+    ok(off == 16, "parse off");
+    ok(items.reallocs == 0, "shorter parse no reallocs");
+    ok(items.len == 1, "shorter parse len");
+    ok(items.offsets[0] == 0, "shorter parse offset");
+    item = items.list[0];
+    ok(item.next == 0, "shorter atom next");
+    ok(item.atom.typ == KEKSItemMagic, "shorter atom typ");
+    ok(item.atom.v.str.len == 7, "shorter atom typ");
+    ok(memcmp(item.atom.v.str.ptr, magic, 7) == 0, "shorter atom val");
+
+    cap = 0;
+    off = 0;
+    ok(!KEKSItemsEncode(&items, 0, &off, buf, cap), "shorter items encode small buf");
+    ok(off == 0, "shorter items encode off");
+    cap = sizeof buf;
+    ok(KEKSItemsEncode(&items, 0, &off, buf, cap), "shorter items encode");
+    ok(off == 16, "shorter items encode off");
+    ok(memcmp(buf, (const unsigned char *)"KEKS", 4) == 0, "shorter items encode tag");
+    ok(memcmp(buf + 4, magic, 7) == 0, "shorter items encode val");
+    ok(memcmp(buf + 4 + 7, (const unsigned char *)"\x00\x00\x00\x00\x00", 5) == 0,
+       "shorter atom suffix");
+
+    // ------------------------ >8 ------------------------
+
+    cap = 16;
+    len = 0;
+    ok(KEKSAtomMagicEncode(&len, buf, cap, NULL, 0), "atom encode empty");
+    ok(len == 16, "atom empty len");
+    ok(memcmp(buf, (const unsigned char *)"KEKS", 4) == 0, "atom empty tag");
+    {
+        bool allZeros = true;
+        for (size_t i = 0; i < 12; i++) {
+            if (buf[4 + i] != 0) {
+                allZeros = false;
+            }
+        }
+        ok(allZeros, "atom empty val");
+    }
+
+    // ------------------------ >8 ------------------------
+
+    cap = 16;
+    len = 0;
+    ok(KEKSAtomMagicEncode(&len, buf, cap, NULL, 0), "atom encode empty");
+    ok(len == 16, "atom empty len");
+    ok(memcmp(buf, (const unsigned char *)"KEKS", 4) == 0, "atom empty tag");
+    {
+        bool allZeros = true;
+        for (size_t i = 0; i < 12; i++) {
+            if (buf[4 + i] != 0) {
+                allZeros = false;
+            }
+        }
+        ok(allZeros, "atom empty val");
+    }
+
+    // ------------------------ >8 ------------------------
+
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    buf[0] = 'K';
+    buf[1] = 'E';
+    buf[2] = 'K';
+    buf[3] = 's';
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrBadMagic, "bad magic KEKs");
+    buf[0] = 'K';
+    buf[1] = 'e';
+    buf[2] = 'K';
+    buf[3] = 'S';
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrBadMagic, "bad magic KeKS");
+
+    return exit_status();
+}
diff --git a/c/t/nil.t.c b/c/t/nil.t.c
new file mode 100644 (file)
index 0000000..a179247
--- /dev/null
@@ -0,0 +1,53 @@
+#include <stdlib.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
+#include <tap.h>
+#pragma clang diagnostic pop
+
+#include <keks/atom.h>
+#include <keks/enc.h>
+#include <keks/err.h>
+#include <keks/items.h>
+
+int
+main(void)
+{
+    plan(NO_PLAN);
+
+    size_t len = 0;
+    unsigned char buf[] = {0, 0};
+    size_t cap = 0;
+    ok(!KEKSAtomNILEncode(&len, buf, cap), "atom small buf");
+    ok(len == 1, "atom small buf len");
+
+    cap = 1;
+    len = 0;
+    ok(KEKSAtomNILEncode(&len, buf, cap), "atom encode");
+    ok(len == 1, "atom len");
+    ok(buf[0] == 0x01, "atom val");
+
+    struct KEKSItems items;
+    KEKSItemsInit(&items, 1 + 1);
+    size_t off = 0;
+    enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "parse");
+    ok(off == 1, "parse off");
+    ok(items.reallocs == 0, "parse no reallocs");
+    ok(items.len == 1, "parse len");
+    ok(items.offsets[0] == 0, "parse offset");
+    struct KEKSItem item = items.list[0];
+    ok(item.next == 0, "atom next");
+    ok(item.atom.typ == KEKSItemNIL, "atom typ");
+
+    cap = 0;
+    off = 0;
+    ok(!KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode small buf");
+    ok(off == 0, "items encode off");
+    cap = sizeof buf;
+    ok(KEKSItemsEncode(&items, 0, &off, buf, cap), "items encode");
+    ok(off == 1, "items encode off");
+    ok(buf[0] == 0x01, "items encode val");
+
+    return exit_status();
+}
diff --git a/c/t/t.list b/c/t/t.list
new file mode 100644 (file)
index 0000000..e439d91
--- /dev/null
@@ -0,0 +1,6 @@
+bool.t
+hexlet.t
+magic.t
+nil.t
+tai.t
+tai64.t
diff --git a/c/t/tai.t.c b/c/t/tai.t.c
new file mode 100644 (file)
index 0000000..27c5a36
--- /dev/null
@@ -0,0 +1,115 @@
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/random.h>
+#include <time.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
+#include <tap.h>
+#pragma clang diagnostic pop
+
+#include <keks/dectai.h>
+#include <keks/enctai.h>
+#include <keks/err.h>
+#include <keks/frombe.h>
+#include <keks/leapsecs.h>
+#include <keks/tobe.h>
+
+int
+main(void)
+{
+    plan(NO_PLAN);
+
+    unsigned char src[8] = {0};
+    getrandom(src, 4, 0);
+    struct timespec ts;
+    ts.tv_sec = (time_t)(0xFFFFFFFFFFFFFFFF - keksFromBE(src + 0, 4) % 1000000);
+    ts.tv_nsec = 0;
+    unsigned char buf[16] = {0};
+    ok(!KEKSTimespecToTAI64(buf, &ts), "too big secs");
+
+    getrandom(src, 8, 0);
+    ts.tv_sec = (time_t)(keksFromBE(src + 0, 8) % 0x4000000000000000);
+    ok(KEKSTimespecToTAI64(buf, &ts), "secs ok");
+    ok(memcmp(buf + 1 + 8, (const unsigned char *)"\x00\x00\x00\x00", 4) == 0,
+       "ns empty");
+
+    ts.tv_nsec = (long)(1000000000 + 1);
+    ok(!KEKSTimespecToTAI64(buf, &ts), "too big ns");
+
+    getrandom(src, 4, 0);
+    keksToBE(src, 4, keksFromBE(src, 4) % 1000000000);
+    ts.tv_nsec = (long)(keksFromBE(src, 4));
+    ok(KEKSTimespecToTAI64(buf, &ts), "ns ok");
+    ok(keksFromBE(buf + 8, 4) == keksFromBE(src, 4), "ns eq");
+
+    // ------------------------ >8 ------------------------
+
+    ts.tv_sec = (time_t)(0xFFFFFFFFFFFFFFFF);
+    ok(!KEKSTimespecToTAI(&ts), "to tai overflow, negative");
+
+    ts.tv_sec = (time_t)(0x8000000000000000 - 1);
+    ok(!KEKSTimespecToTAI(&ts), "to tai overflow, too big positive");
+
+    // ------------------------ >8 ------------------------
+
+    ts.tv_nsec = 0;
+    enum KEKSErr err = KEKSErrNo;
+    bool failed = false;
+    for (size_t i = 0; i < 1000; i++) {
+        getrandom(src, 4, 0);
+        ts.tv_sec = (time_t)(keksFromBE(src, 4));
+        if (!KEKSTimespecToTAI(&ts)) {
+            failed = false;
+            break;
+        }
+        err = KEKSTimespecToUTC(&ts);
+        if (err != KEKSErrNo) {
+            failed = false;
+            break;
+        }
+    }
+    ok(!failed, "symmetric ok");
+
+    // ------------------------ >8 ------------------------
+
+    ts.tv_sec = -1;
+    ok(KEKSTimespecToUTC(&ts) == KEKSErrTAI64InPast, "to utc, negative");
+
+    ts.tv_sec = 9;
+    ok(KEKSTimespecToUTC(&ts) == KEKSErrTAI64InPast, "to utc, in past");
+
+    // ------------------------ >8 ------------------------
+
+    for (size_t i = 0; i < KEKSLeapsecsN; i++) {
+        ts.tv_sec = (time_t)(KEKSLeapsecs[i]);
+        ok(KEKSTimespecToUTC(&ts) == KEKSErrTAI64IsLeap, "to utc, leap");
+        ts.tv_sec++;
+        ok(KEKSTimespecToUTC(&ts) == KEKSErrNo, "to utc, non-leap");
+    }
+
+    // ------------------------ >8 ------------------------
+
+    ok(KEKSTAI64ToTimespec(&ts, NULL, 13) == KEKSErrTAI64BadAsec, "too long");
+    ok(KEKSTAI64ToTimespec(&ts, NULL, 11) == KEKSErrTAI64BadAsec, "bad len");
+    ok(KEKSTAI64ToTimespec(&ts, NULL, 7) == KEKSErrTAI64BadAsec, "too short");
+
+    ok(KEKSTAI64ToTimespec(
+           &ts, (const unsigned char *)"\x3F\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) ==
+           KEKSErrTAI64InPast,
+       "in past");
+    ok(KEKSTAI64ToTimespec(
+           &ts, (const unsigned char *)"\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 8) ==
+           KEKSErrTAI64TooBig,
+       "too big");
+
+    ok(KEKSTAI64ToTimespec(
+           &ts,
+           (const unsigned char *)"\x40\xFF\x00\x00\x00\x00\x00\x10\x3B\xA0\x00\x00",
+           12) == KEKSErrTAI64BadNsec,
+       "too big ns");
+
+    return exit_status();
+}
diff --git a/c/t/tai64.t.c b/c/t/tai64.t.c
new file mode 100644 (file)
index 0000000..d980aa6
--- /dev/null
@@ -0,0 +1,197 @@
+#include <stdlib.h>
+#include <string.h>
+#include <sys/random.h>
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wreserved-macro-identifier"
+#include <tap.h>
+#pragma clang diagnostic pop
+
+#include <keks/atom.h>
+#include <keks/enc.h>
+#include <keks/err.h>
+#include <keks/frombe.h>
+#include <keks/items.h>
+#include <keks/tobe.h>
+
+int
+main(void)
+{
+    plan(NO_PLAN);
+
+    unsigned char src[16] = {0};
+    getrandom(src, 16, 0);
+    size_t len = 0;
+    unsigned char buf[16 + 1] = {0};
+
+    // ------------------------ >8 ------------------------
+
+    size_t cap = 16;
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 8 - 1), "64: bad src len");
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 8 + 1), "64: bad src len");
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 12 - 1), "64N: bad src len");
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 12 + 1), "64N: bad src len");
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 16 - 1), "64NA: bad src len");
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 16 + 1), "64NA: bad src len");
+
+    // ------------------------ >8 ------------------------
+
+    cap = 8;
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 8), "64: encode small buf");
+    ok(len == 8 + 1, "64: encode small buf len");
+    cap = 12;
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 12), "64N: encode small buf");
+    ok(len == 12 + 1, "64: encode small buf len");
+    cap = 16;
+    ok(!KEKSAtomTAI64Encode(&len, buf, cap, src, 16), "64NA: encode small buf");
+    ok(len == 16 + 1, "64: encode small buf len");
+
+    // ------------------------ >8 ------------------------
+
+    cap = 8 + 1;
+    ok(KEKSAtomTAI64Encode(&len, buf, cap, src, 8), "64: encode");
+    ok(len == 8 + 1, "64: encode len");
+    ok(buf[0] == 0x18, "64: tag");
+    ok(memcmp(buf + 1, src, 8) == 0, "64: val");
+
+    cap = 12 + 1;
+    ok(KEKSAtomTAI64Encode(&len, buf, cap, src, 12), "64N: encode");
+    ok(len == 12 + 1, "64N: encode len");
+    ok(buf[0] == 0x19, "64N: tag");
+    ok(memcmp(buf + 1, src, 12) == 0, "64N: val");
+
+    cap = 16 + 1;
+    ok(KEKSAtomTAI64Encode(&len, buf, cap, src, 16), "64NA: encode");
+    ok(len == 16 + 1, "64NA: encode len");
+    ok(buf[0] == 0x1A, "64NA: tag");
+    ok(memcmp(buf + 1, src, 16) == 0, "64NA: val");
+
+    // ------------------------ >8 ------------------------
+
+    cap = 8 + 1;
+    src[0] &= (unsigned char)0x7F;
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 8);
+    struct KEKSItems items;
+    KEKSItemsInit(&items, 1 + 1);
+    size_t off = 0;
+    enum KEKSErr err = KEKSItemsParse(&items, &off, buf, cap - 1);
+    ok(err == KEKSErrNotEnough, "64: parse not enough");
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "64: parse");
+    ok(off == 8 + 1, "64: parse off");
+    ok(items.reallocs == 0, "64: parse no reallocs");
+    ok(items.len == 1, "64: parse len");
+    ok(items.offsets[0] == 0, "64: parse offset");
+    struct KEKSItem item = items.list[0];
+    ok(item.next == 0, "64: atom next");
+    ok(item.atom.typ == KEKSItemTAI64, "64: atom typ");
+    ok(item.atom.v.str.len == 8, "64: atom len");
+    ok(memcmp(item.atom.v.str.ptr, src, 8) == 0, "64: atom val");
+    src[0] |= (unsigned char)0x80;
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 8);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64TooBig, "64: highest bit set");
+
+    // ------------------------ >8 ------------------------
+
+    cap = 12 + 1;
+    src[0] &= (unsigned char)0x7F;
+    memset(src + 8, 0, 4);
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 12);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64NonMinimal, "64N: parse non-minimal");
+
+    getrandom(src + 8, 4, 0);
+    keksToBE(src + 8, 4, keksFromBE(src + 8, 4) % 1000000000);
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 12);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap - 1);
+    ok(err == KEKSErrNotEnough, "64N: parse not enough");
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "64N: parse");
+    ok(off == 12 + 1, "64N: parse off");
+    ok(items.reallocs == 0, "64N: parse no reallocs");
+    ok(items.len == 1, "64N: parse len");
+    ok(items.offsets[0] == 0, "64N: parse offset");
+    item = items.list[0];
+    ok(item.next == 0, "64N: atom next");
+    ok(item.atom.typ == KEKSItemTAI64, "64N: atom typ");
+    ok(item.atom.v.str.len == 12, "64N: atom len");
+    ok(memcmp(item.atom.v.str.ptr, src, 12) == 0, "64N: atom val");
+
+    keksToBE(src + 8, 4, keksFromBE(src + 8, 4) + 1000000000);
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 12);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64BadNsec, "64N: bad N");
+
+    src[0] |= (unsigned char)0x80;
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 12);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64TooBig, "64N: highest bit set");
+
+    // ------------------------ >8 ------------------------
+
+    cap = 16 + 1;
+    src[0] &= (unsigned char)0x7F;
+    keksToBE(src + 8, 4, keksFromBE(src + 8, 4) % 1000000000);
+    memset(src + 12, 0, 4);
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 16);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64NonMinimal, "64NA: parse non-minimal");
+
+    memset(src + 8, 0, 4);
+    getrandom(src + 12, 4, 0);
+    keksToBE(src + 12, 4, keksFromBE(src + 12, 4) % 1000000000);
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 16);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap - 1);
+    ok(err == KEKSErrNotEnough, "64NA: parse not enough");
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrNo, "64NA: parse");
+    ok(off == 16 + 1, "64NA: parse off");
+    ok(items.reallocs == 0, "64NA: parse no reallocs");
+    ok(items.len == 1, "64NA: parse len");
+    ok(items.offsets[0] == 0, "64NA: parse offset");
+    item = items.list[0];
+    ok(item.next == 0, "64NA: atom next");
+    ok(item.atom.typ == KEKSItemTAI64, "64NA: atom typ");
+    ok(item.atom.v.str.len == 16, "64NA: atom len");
+    ok(memcmp(item.atom.v.str.ptr, src, 16) == 0, "64NA: atom val");
+
+    keksToBE(src + 12, 4, keksFromBE(src + 12, 4) + 1000000000);
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 16);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64BadAsec, "64NA: bad A");
+
+    src[0] |= (unsigned char)0x80;
+    KEKSAtomTAI64Encode(&len, buf, cap, src, 16);
+    KEKSItemsFree(&items);
+    KEKSItemsInit(&items, 1 + 1);
+    off = 0;
+    err = KEKSItemsParse(&items, &off, buf, cap);
+    ok(err == KEKSErrTAI64TooBig, "64NA: highest bit set");
+
+    return exit_status();
+}