From: Sergey Matveev Date: Thu, 16 Jan 2025 12:58:42 +0000 (+0300) Subject: Initial draft tests X-Git-Tag: v0.0.0~5 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=72d9a6c40182cb0fbfa2d2900b6d4a58a84ed18c59de44adc258952f150ab075;p=keks.git Initial draft tests --- diff --git a/c/t/.gitignore b/c/t/.gitignore new file mode 100644 index 0000000..bc084b3 --- /dev/null +++ b/c/t/.gitignore @@ -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 index 0000000..51a0a18 --- /dev/null +++ b/c/t/all.do @@ -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 index 0000000..cf90ff0 --- /dev/null +++ b/c/t/bool.t.c @@ -0,0 +1,65 @@ +#include +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#include +#pragma clang diagnostic pop + +#include +#include +#include +#include + +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 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 index 0000000..d35d986 --- /dev/null +++ b/c/t/compile_flags.txt.do @@ -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 index 0000000..4cdbe3c --- /dev/null +++ b/c/t/conf/.gitignore @@ -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 index 0000000..114257b --- /dev/null +++ b/c/t/conf/tap.rc.do @@ -0,0 +1,12 @@ +PKGCONF=${PKGCONF:-`command -v pkgconf || command -v pkg-config`} +cat < +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#include +#pragma clang diagnostic pop + +#include +#include +#include +#include + +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 index 0000000..a8924b5 --- /dev/null +++ b/c/t/magic.t.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#include +#pragma clang diagnostic pop + +#include +#include +#include +#include + +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 index 0000000..a179247 --- /dev/null +++ b/c/t/nil.t.c @@ -0,0 +1,53 @@ +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#include +#pragma clang diagnostic pop + +#include +#include +#include +#include + +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 index 0000000..e439d91 --- /dev/null +++ b/c/t/t.list @@ -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 index 0000000..27c5a36 --- /dev/null +++ b/c/t/tai.t.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#include +#pragma clang diagnostic pop + +#include +#include +#include +#include +#include +#include + +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 index 0000000..d980aa6 --- /dev/null +++ b/c/t/tai64.t.c @@ -0,0 +1,197 @@ +#include +#include +#include + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreserved-macro-identifier" +#include +#pragma clang diagnostic pop + +#include +#include +#include +#include +#include +#include + +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(); +}