kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

release_index_test.c (5696B)


      1 /* release_index_test — public <kit/package.h> channel-index surface:
      2  *
      3  *   - Build a KitReleaseIndex (two hosts, inserted out of target order, with
      4  *     both ids, a size, and a mirror list), emit it to a memory KitWriter, and
      5  *     assert the EXACT canonical bytes (byte-stability: hosts sorted ascending
      6  *     by target, fixed key order).
      7  *   - Parse those bytes back and assert full round-trip equality.
      8  *   - kit_calver_compare: numeric (not lexical) ordering and malformed input.
      9  *
     10  * Run by: make test-release-index
     11  */
     12 
     13 #include <kit/core.h>
     14 #include <kit/package.h>
     15 #include <stddef.h>
     16 #include <stdint.h>
     17 #include <string.h>
     18 
     19 #include "lib/kit_unit.h"
     20 
     21 static KitUnit g_u;
     22 #define EXPECT(c, ...) CU_EXPECT(&g_u, c, __VA_ARGS__)
     23 
     24 /* aarch64-macos sorts before x86_64-linux-gnu, so it is emitted first even
     25  * though we register it second. kpkg id = bytes 0x00..0x1f, targz = 0x20..0x3f.
     26  */
     27 static const char* const GOLDEN =
     28     "kit-release 1\n"
     29     "channel = stable\n"
     30     "version = 2026.6.0\n"
     31     "hash = blake2b-256\n"
     32     "\n"
     33     "[host]\n"
     34     "target = aarch64-macos\n"
     35     "kpkg = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n"
     36     "targz = 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f\n"
     37     "size = 31457280\n"
     38     "url = https://example/a.kpkg\n"
     39     "url = https://dl.example/a.kpkg\n"
     40     "url = https://mirror.example/a.kpkg\n"
     41     "\n"
     42     "[host]\n"
     43     "target = x86_64-linux-gnu\n"
     44     "kpkg = 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n"
     45     "url = https://example/b.kpkg\n";
     46 
     47 static void fill_index(KitReleaseIndex* idx) {
     48   KitReleaseHost* a;
     49   KitReleaseHost* b;
     50   unsigned i;
     51   memset(idx, 0, sizeof *idx);
     52   snprintf(idx->channel, sizeof idx->channel, "%s", "stable");
     53   snprintf(idx->version, sizeof idx->version, "%s", "2026.6.0");
     54   idx->n_hosts = 2;
     55 
     56   /* Register x86_64-linux-gnu FIRST to prove emit re-sorts ascending. */
     57   b = &idx->hosts[0];
     58   snprintf(b->target, sizeof b->target, "%s", "x86_64-linux-gnu");
     59   for (i = 0; i < KIT_CAS_HASH_LEN; ++i) b->kpkg_id[i] = (uint8_t)i;
     60   b->has_kpkg = 1;
     61   b->has_targz = 0;
     62   b->size = 0; /* omitted */
     63   snprintf(b->urls[0], sizeof b->urls[0], "%s", "https://example/b.kpkg");
     64   b->n_urls = 1;
     65 
     66   a = &idx->hosts[1];
     67   snprintf(a->target, sizeof a->target, "%s", "aarch64-macos");
     68   for (i = 0; i < KIT_CAS_HASH_LEN; ++i) a->kpkg_id[i] = (uint8_t)i;
     69   for (i = 0; i < KIT_CAS_HASH_LEN; ++i) a->targz_id[i] = (uint8_t)(i + 32u);
     70   a->has_kpkg = 1;
     71   a->has_targz = 1;
     72   a->size = 31457280u;
     73   snprintf(a->urls[0], sizeof a->urls[0], "%s", "https://example/a.kpkg");
     74   snprintf(a->urls[1], sizeof a->urls[1], "%s", "https://dl.example/a.kpkg");
     75   snprintf(a->urls[2], sizeof a->urls[2], "%s", "https://mirror.example/a.kpkg");
     76   a->n_urls = 3;
     77 }
     78 
     79 static int host_eq(const KitReleaseHost* x, const KitReleaseHost* y) {
     80   unsigned i;
     81   if (strcmp(x->target, y->target) != 0) return 0;
     82   if (x->has_kpkg != y->has_kpkg || x->has_targz != y->has_targz) return 0;
     83   if (x->size != y->size || x->n_urls != y->n_urls) return 0;
     84   if (x->has_kpkg && memcmp(x->kpkg_id, y->kpkg_id, KIT_CAS_HASH_LEN) != 0)
     85     return 0;
     86   if (x->has_targz && memcmp(x->targz_id, y->targz_id, KIT_CAS_HASH_LEN) != 0)
     87     return 0;
     88   for (i = 0; i < x->n_urls; ++i)
     89     if (strcmp(x->urls[i], y->urls[i]) != 0) return 0;
     90   return 1;
     91 }
     92 
     93 /* The parsed index is canonical (ascending by target), so compare it to the
     94  * sorted expectation: aarch64-macos first, then x86_64-linux-gnu. */
     95 static void check_roundtrip(const uint8_t* bytes, size_t len) {
     96   static KitReleaseIndex want; /* large; keep off the stack */
     97   static KitReleaseIndex got;
     98   fill_index(&want);
     99   EXPECT(kit_release_index_parse(&g_u.ctx, bytes, len, &got) == KIT_OK,
    100          "parse golden bytes OK");
    101   EXPECT(strcmp(got.channel, want.channel) == 0, "channel round-trips");
    102   EXPECT(strcmp(got.version, want.version) == 0, "version round-trips");
    103   EXPECT(got.n_hosts == want.n_hosts, "n_hosts round-trips (%u)", got.n_hosts);
    104   if (got.n_hosts == 2) {
    105     EXPECT(host_eq(&got.hosts[0], &want.hosts[1]), "host[0] == aarch64-macos");
    106     EXPECT(host_eq(&got.hosts[1], &want.hosts[0]),
    107            "host[1] == x86_64-linux-gnu");
    108   }
    109 }
    110 
    111 static void check_emit_parse(void) {
    112   static KitReleaseIndex idx; /* large; keep off the stack */
    113   KitWriter* w = NULL;
    114   const uint8_t* bytes;
    115   size_t len, want_len;
    116   fill_index(&idx);
    117   EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK, "writer_mem OK");
    118   EXPECT(kit_release_index_emit(&g_u.ctx, &idx, w) == KIT_OK, "emit OK");
    119   bytes = kit_writer_mem_bytes(w, &len);
    120   want_len = strlen(GOLDEN);
    121   EXPECT(len == want_len, "emitted length %zu == golden %zu", len, want_len);
    122   EXPECT(len == want_len && memcmp(bytes, GOLDEN, len) == 0,
    123          "emitted bytes match golden exactly");
    124   check_roundtrip(bytes, len);
    125   kit_writer_close(w);
    126 }
    127 
    128 static void check_calver(void) {
    129   int cmp = 99;
    130   EXPECT(kit_calver_compare("2026.6.0", "2026.6.1", &cmp) == KIT_OK && cmp < 0,
    131          "2026.6.0 < 2026.6.1");
    132   EXPECT(kit_calver_compare("2026.10.0", "2026.9.0", &cmp) == KIT_OK && cmp > 0,
    133          "2026.10.0 > 2026.9.0 (numeric, not lexical)");
    134   EXPECT(kit_calver_compare("2026.6.0", "2026.6.0", &cmp) == KIT_OK && cmp == 0,
    135          "2026.6.0 == 2026.6.0");
    136   EXPECT(kit_calver_compare("2026.6", "2026.6.0", &cmp) == KIT_INVALID,
    137          "too few fields -> KIT_INVALID");
    138   EXPECT(kit_calver_compare("2026.x.0", "2026.6.0", &cmp) == KIT_INVALID,
    139          "non-numeric field -> KIT_INVALID");
    140 }
    141 
    142 int main(void) {
    143   kit_unit_init(&g_u);
    144   check_emit_parse();
    145   check_calver();
    146   kit_unit_summary(&g_u, "release_index_test");
    147   return kit_unit_status(&g_u);
    148 }