ar_test.c (28381B)
1 /* Round-trip tests for the kit ar reader/writer. 2 * 3 * Builds against just include/kit.h + libkit.a and a few libc calls 4 * (malloc/realloc/free, printf for diagnostics). kit_ar_write itself 5 * makes no heap allocations, so the test does not need a KitHeap. 6 * 7 * Set KIT_AR_TEST_HOST=1 to also dump the produced symbol-index 8 * archive to /tmp/kit_ar_test.a and run the host's `ar t` and 9 * `nm --print-armap` on it as a cross-check. */ 10 #include <kit/archive.h> 11 #include <kit/core.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include "lib/kit_unit.h" 17 18 /* ===== minimal KitWriter over a growing buffer ===== */ 19 20 typedef struct BufW { 21 KitWriter base; 22 uint8_t* data; 23 size_t len; 24 size_t cap; 25 KitStatus st; 26 } BufW; 27 28 static KitStatus bufw_write(KitWriter* w, const void* data, size_t n) { 29 BufW* b = (BufW*)w; 30 if (b->st != KIT_OK) return b->st; 31 if (b->len + n > b->cap) { 32 size_t nc = b->cap ? b->cap * 2 : 256; 33 while (nc < b->len + n) nc *= 2; 34 uint8_t* p = (uint8_t*)realloc(b->data, nc); 35 if (!p) { 36 b->st = KIT_NOMEM; 37 return KIT_NOMEM; 38 } 39 b->data = p; 40 b->cap = nc; 41 } 42 memcpy(b->data + b->len, data, n); 43 b->len += n; 44 return KIT_OK; 45 } 46 47 static KitStatus bufw_seek(KitWriter* w, uint64_t off) { 48 (void)w; 49 (void)off; 50 return KIT_OK; 51 } 52 static uint64_t bufw_tell(KitWriter* w) { return ((BufW*)w)->len; } 53 static KitStatus bufw_status(KitWriter* w) { return ((BufW*)w)->st; } 54 static void bufw_close(KitWriter* w) { (void)w; } 55 56 static void bufw_init(BufW* b) { 57 b->base.write = bufw_write; 58 b->base.seek = bufw_seek; 59 b->base.tell = bufw_tell; 60 b->base.status = bufw_status; 61 b->base.close = bufw_close; 62 b->data = NULL; 63 b->len = 0; 64 b->cap = 0; 65 b->st = KIT_OK; 66 } 67 68 static void bufw_fini(BufW* b) { free(b->data); } 69 70 /* ===== libc heap + context (needed by kit_ar_iter_new) ===== */ 71 72 static KitUnit g_u; 73 74 /* ar_test keeps its own context: now=-1 and no diag sink, since its archive 75 * assertions depend on exact header bytes (the clock) and never expect a 76 * diagnostic. The heap comes from g_u, filled by kit_unit_init in main. */ 77 static KitContext g_ctx = {.heap = &g_u.heap, .now = -1}; 78 79 /* ===== assertion helpers ===== */ 80 81 /* EXPECT keeps its per-test early-return-on-fail shape via CU_CHECK_RET. */ 82 #define EXPECT(cond, ...) CU_CHECK_RET(&g_u, cond, __VA_ARGS__) 83 84 static uint32_t be32(const uint8_t* p) { 85 return ((uint32_t)p[0] << 24) | ((uint32_t)p[1] << 16) | 86 ((uint32_t)p[2] << 8) | (uint32_t)p[3]; 87 } 88 89 /* Decode the ar_size field (10-byte ASCII decimal, space-padded). */ 90 static uint64_t ar_size_field(const uint8_t* hdr) { 91 uint64_t v = 0; 92 int j; 93 for (j = 48; j < 58; ++j) { 94 char c = (char)hdr[j]; 95 if (c < '0' || c > '9') break; 96 v = v * 10 + (uint64_t)(c - '0'); 97 } 98 return v; 99 } 100 101 /* Decode the ar_date field (12-byte ASCII decimal, space-padded). */ 102 static uint64_t ar_date_field(const uint8_t* hdr) { 103 uint64_t v = 0; 104 int j; 105 for (j = 16; j < 28; ++j) { 106 char c = (char)hdr[j]; 107 if (c < '0' || c > '9') break; 108 v = v * 10 + (uint64_t)(c - '0'); 109 } 110 return v; 111 } 112 113 /* ===== tests ===== */ 114 115 static int test_basic_roundtrip(void) { 116 KitArInput ms[2]; 117 BufW bw; 118 KitSlice in; 119 KitArIter* it = NULL; 120 KitArMember m; 121 int rc; 122 123 ms[0].name = KIT_SLICE_LIT("a.o"); 124 ms[0].bytes.data = (const uint8_t*)"AAAA"; 125 ms[0].bytes.len = 4; 126 ms[1].name = KIT_SLICE_LIT("b.o"); 127 ms[1].bytes.data = (const uint8_t*)"BBBBB"; 128 ms[1].bytes.len = 5; 129 130 bufw_init(&bw); 131 rc = kit_ar_write(&bw.base, ms, 2, NULL); 132 EXPECT(rc == 0, "kit_ar_write returned %d", rc); 133 EXPECT(bw.st == KIT_OK, "writer error"); 134 EXPECT(bw.len >= 8, "archive too short"); 135 EXPECT(memcmp(bw.data, "!<arch>\n", 8) == 0, "magic"); 136 137 in.data = bw.data; 138 in.len = bw.len; 139 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 140 141 EXPECT(kit_ar_iter_next(it, &m), "first member"); 142 EXPECT(kit_slice_eq_cstr(m.name, "a.o"), "name 0 = %.*s", 143 KIT_SLICE_ARG(m.name)); 144 EXPECT(m.size == 4 && memcmp(m.data, "AAAA", 4) == 0, "data 0"); 145 146 EXPECT(kit_ar_iter_next(it, &m), "second member"); 147 EXPECT(kit_slice_eq_cstr(m.name, "b.o"), "name 1 = %.*s", 148 KIT_SLICE_ARG(m.name)); 149 EXPECT(m.size == 5 && memcmp(m.data, "BBBBB", 5) == 0, "data 1"); 150 151 EXPECT(!kit_ar_iter_next(it, &m), "iter end"); 152 153 kit_ar_iter_free(it); 154 bufw_fini(&bw); 155 return 1; 156 } 157 158 static int test_long_name_table(void) { 159 /* >15 chars triggers the // long-name table. */ 160 KitArInput ms[2]; 161 BufW bw; 162 KitSlice in; 163 KitArIter* it = NULL; 164 KitArMember m; 165 KitArWriteOptions opts = {0}; 166 int rc; 167 168 opts.long_names = 1; 169 ms[0].name = KIT_SLICE_LIT("short.o"); 170 ms[0].bytes.data = (const uint8_t*)"x"; 171 ms[0].bytes.len = 1; 172 ms[1].name = KIT_SLICE_LIT("this_name_is_long_enough.o"); 173 ms[1].bytes.data = (const uint8_t*)"yy"; 174 ms[1].bytes.len = 2; 175 176 bufw_init(&bw); 177 rc = kit_ar_write(&bw.base, ms, 2, &opts); 178 EXPECT(rc == 0, "write rc=%d", rc); 179 180 in.data = bw.data; 181 in.len = bw.len; 182 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 183 EXPECT(kit_ar_iter_next(it, &m), "first member"); 184 EXPECT(kit_slice_eq_cstr(m.name, "short.o"), "name 0 = %.*s", 185 KIT_SLICE_ARG(m.name)); 186 EXPECT(kit_ar_iter_next(it, &m), "second member"); 187 EXPECT(kit_slice_eq_cstr(m.name, "this_name_is_long_enough.o"), 188 "long name = %.*s", KIT_SLICE_ARG(m.name)); 189 EXPECT(!kit_ar_iter_next(it, &m), "iter end"); 190 191 kit_ar_iter_free(it); 192 bufw_fini(&bw); 193 return 1; 194 } 195 196 static int test_symbol_index_empty(void) { 197 KitArInput ms[1]; 198 BufW bw; 199 KitSlice in; 200 KitArIter* it = NULL; 201 KitArMember m; 202 KitArWriteOptions opts = {0}; 203 int rc; 204 uint32_t nsyms; 205 206 opts.symbol_index = 1; 207 ms[0].name = KIT_SLICE_LIT("lonely.o"); 208 ms[0].bytes.data = (const uint8_t*)"P"; 209 ms[0].bytes.len = 1; 210 211 bufw_init(&bw); 212 rc = kit_ar_write(&bw.base, ms, 1, &opts); 213 EXPECT(rc == 0, "write rc=%d", rc); 214 215 /* First member after magic must be `/` index with count=0. */ 216 EXPECT(bw.len >= 8 + 60 + 4, "archive too short"); 217 EXPECT(bw.data[8] == '/' && bw.data[9] == ' ', "index name field"); 218 EXPECT(ar_size_field(bw.data + 8) == 4, "index payload size = 4"); 219 nsyms = be32(bw.data + 8 + 60); 220 EXPECT(nsyms == 0, "nsyms = %u", nsyms); 221 222 /* Iterator should skip the `/` and yield only the user member. */ 223 in.data = bw.data; 224 in.len = bw.len; 225 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 226 EXPECT(kit_ar_iter_next(it, &m), "first user member"); 227 EXPECT(kit_slice_eq_cstr(m.name, "lonely.o"), "name = %.*s", 228 KIT_SLICE_ARG(m.name)); 229 EXPECT(!kit_ar_iter_next(it, &m), "iter end"); 230 231 kit_ar_iter_free(it); 232 bufw_fini(&bw); 233 return 1; 234 } 235 236 static int test_symbol_index_basic(void) { 237 /* 2 members with symbol lists; verify count, offsets, and names. */ 238 KitSlice a_syms[] = {KIT_SLICE_LIT("foo"), KIT_SLICE_LIT("bar")}; 239 KitSlice b_syms[] = {KIT_SLICE_LIT("baz")}; 240 KitArMemberSymbols msyms[2]; 241 KitArInput ms[2]; 242 BufW bw; 243 KitArWriteOptions opts = {0}; 244 int rc; 245 uint32_t nsyms; 246 const uint8_t* p; 247 uint64_t index_payload; 248 uint32_t off0, off1, off2; 249 uint64_t a_hdr_off, b_hdr_off; 250 const char* name; 251 252 opts.symbol_index = 1; 253 opts.long_names = 1; 254 255 msyms[0].names = a_syms; 256 msyms[0].count = 2; 257 msyms[1].names = b_syms; 258 msyms[1].count = 1; 259 opts.member_symbols = msyms; 260 261 ms[0].name = KIT_SLICE_LIT("a.o"); 262 ms[0].bytes.data = (const uint8_t*)"AAAA"; 263 ms[0].bytes.len = 4; 264 ms[1].name = KIT_SLICE_LIT("b.o"); 265 ms[1].bytes.data = (const uint8_t*)"BBBBB"; 266 ms[1].bytes.len = 5; 267 268 bufw_init(&bw); 269 rc = kit_ar_write(&bw.base, ms, 2, &opts); 270 EXPECT(rc == 0, "write rc=%d", rc); 271 272 EXPECT(memcmp(bw.data, "!<arch>\n", 8) == 0, "magic"); 273 EXPECT(bw.data[8] == '/' && bw.data[9] == ' ', "index ar_name field"); 274 275 index_payload = ar_size_field(bw.data + 8); 276 /* 4 (count) + 4*3 (offsets) + 3+1 + 3+1 + 3+1 (names "foo\0bar\0baz\0") = 28 277 */ 278 EXPECT(index_payload == 28, "index payload = %llu", 279 (unsigned long long)index_payload); 280 281 p = bw.data + 8 + 60; 282 nsyms = be32(p); 283 p += 4; 284 EXPECT(nsyms == 3, "nsyms = %u", nsyms); 285 286 off0 = be32(p); /* foo → a.o */ 287 off1 = be32(p + 4); /* bar → a.o */ 288 off2 = be32(p + 8); /* baz → b.o */ 289 p += 12; 290 291 /* Compute expected header offsets: index_total = 60+28 (no pad, even). 292 * No long-name table is emitted (basenames ≤ 15 chars). So: 293 * a.o header at offset 8 + 88 = 96 294 * b.o header at offset 96 + 60 + 4 (+0 pad) = 160 */ 295 a_hdr_off = 8 + 60 + 28; 296 b_hdr_off = a_hdr_off + 60 + 4; 297 EXPECT(off0 == a_hdr_off, "off0 = %u, expected %llu", off0, 298 (unsigned long long)a_hdr_off); 299 EXPECT(off1 == a_hdr_off, "off1 = %u, expected %llu", off1, 300 (unsigned long long)a_hdr_off); 301 EXPECT(off2 == b_hdr_off, "off2 = %u, expected %llu", off2, 302 (unsigned long long)b_hdr_off); 303 304 /* Sanity: the member headers must actually live at those offsets. */ 305 EXPECT(memcmp(bw.data + a_hdr_off, "a.o/", 4) == 0, "a.o at offset"); 306 EXPECT(memcmp(bw.data + b_hdr_off, "b.o/", 4) == 0, "b.o at offset"); 307 308 /* Names: "foo\0bar\0baz\0" */ 309 name = (const char*)p; 310 EXPECT(strcmp(name, "foo") == 0, "name 0 = %s", name); 311 name += strlen(name) + 1; 312 EXPECT(strcmp(name, "bar") == 0, "name 1 = %s", name); 313 name += strlen(name) + 1; 314 EXPECT(strcmp(name, "baz") == 0, "name 2 = %s", name); 315 316 bufw_fini(&bw); 317 return 1; 318 } 319 320 static int test_symbol_index_with_long_names(void) { 321 /* `/` member must come BEFORE `//` long-name table, and offsets must 322 * still point at correct member-header positions. */ 323 KitSlice syms0[] = {KIT_SLICE_LIT("alpha")}; 324 KitSlice syms1[] = {KIT_SLICE_LIT("beta")}; 325 KitArMemberSymbols msyms[2]; 326 KitArInput ms[2]; 327 BufW bw; 328 KitSlice in; 329 KitArIter* it = NULL; 330 KitArMember m; 331 KitArWriteOptions opts = {0}; 332 int rc; 333 uint64_t index_payload, longtab_payload; 334 uint64_t index_total, longtab_total; 335 uint64_t m0_hdr, m1_hdr; 336 uint32_t off0, off1; 337 const uint8_t* p; 338 339 opts.symbol_index = 1; 340 opts.long_names = 1; 341 msyms[0].names = syms0; 342 msyms[0].count = 1; 343 msyms[1].names = syms1; 344 msyms[1].count = 1; 345 opts.member_symbols = msyms; 346 347 ms[0].name = KIT_SLICE_LIT("this_name_is_long_enough.o"); /* 26 chars → // */ 348 ms[0].bytes.data = (const uint8_t*)"X"; 349 ms[0].bytes.len = 1; 350 ms[1].name = KIT_SLICE_LIT("short.o"); 351 ms[1].bytes.data = (const uint8_t*)"YY"; 352 ms[1].bytes.len = 2; 353 354 bufw_init(&bw); 355 rc = kit_ar_write(&bw.base, ms, 2, &opts); 356 EXPECT(rc == 0, "write rc=%d", rc); 357 358 /* Layout: magic(8) | / index member | // long-name member | members. */ 359 EXPECT(bw.data[8] == '/' && bw.data[9] == ' ', "/ first"); 360 index_payload = ar_size_field(bw.data + 8); 361 /* 4 (count) + 4*2 (offsets) + 6 ("alpha\0") + 5 ("beta\0") = 23 → odd → pad 1 362 */ 363 EXPECT(index_payload == 23, "index_payload = %llu", 364 (unsigned long long)index_payload); 365 index_total = 60 + index_payload + (index_payload & 1); 366 367 /* Verify // header sits at 8 + index_total. */ 368 EXPECT(bw.data[8 + index_total] == '/', "// pos byte 0"); 369 EXPECT(bw.data[8 + index_total + 1] == '/', "// pos byte 1"); 370 longtab_payload = ar_size_field(bw.data + 8 + index_total); 371 /* "this_name_is_long_enough.o/\n" = 28 bytes → even → no pad */ 372 EXPECT(longtab_payload == 28, "longtab payload = %llu", 373 (unsigned long long)longtab_payload); 374 longtab_total = 60 + longtab_payload + (longtab_payload & 1); 375 376 m0_hdr = 8 + index_total + longtab_total; 377 m1_hdr = m0_hdr + 60 + 1 + 1 /* parity pad for odd len 1 */; 378 379 p = bw.data + 8 + 60; 380 EXPECT(be32(p) == 2, "nsyms = %u", be32(p)); 381 p += 4; 382 off0 = be32(p); 383 off1 = be32(p + 4); 384 EXPECT(off0 == m0_hdr, "off0 = %u, expected %llu", off0, 385 (unsigned long long)m0_hdr); 386 EXPECT(off1 == m1_hdr, "off1 = %u, expected %llu", off1, 387 (unsigned long long)m1_hdr); 388 /* Spot-check m1 starts with "short.o/" for sanity. */ 389 EXPECT(memcmp(bw.data + m1_hdr, "short.o/", 8) == 0, "m1 hdr"); 390 391 /* Iterator should walk past both /, // and yield 2 members. */ 392 in.data = bw.data; 393 in.len = bw.len; 394 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 395 EXPECT(kit_ar_iter_next(it, &m), "m0"); 396 EXPECT(kit_slice_eq_cstr(m.name, "this_name_is_long_enough.o"), 397 "m0 name = %.*s", KIT_SLICE_ARG(m.name)); 398 EXPECT(kit_ar_iter_next(it, &m), "m1"); 399 EXPECT(kit_slice_eq_cstr(m.name, "short.o"), "m1 name = %.*s", 400 KIT_SLICE_ARG(m.name)); 401 EXPECT(!kit_ar_iter_next(it, &m), "iter end"); 402 403 /* Optional host cross-check. */ 404 if (getenv("KIT_AR_TEST_HOST")) { 405 FILE* f = fopen("/tmp/kit_ar_test.a", "wb"); 406 if (f) { 407 fwrite(bw.data, 1, bw.len, f); 408 fclose(f); 409 fprintf(stderr, "host cross-check: ar t /tmp/kit_ar_test.a\n"); 410 (void)!system("ar t /tmp/kit_ar_test.a"); 411 fprintf(stderr, 412 "host cross-check: nm --print-armap /tmp/kit_ar_test.a\n"); 413 (void)!system("nm --print-armap /tmp/kit_ar_test.a 2>&1 || true"); 414 } 415 } 416 417 kit_ar_iter_free(it); 418 bufw_fini(&bw); 419 return 1; 420 } 421 422 static int test_iter_skips_index(void) { 423 /* Make sure the iterator never surfaces the `/` member as a user member. */ 424 KitSlice s[] = {KIT_SLICE_LIT("only_sym")}; 425 KitArMemberSymbols msyms[1]; 426 KitArInput ms[1]; 427 KitSlice in; 428 KitArIter* it = NULL; 429 KitArMember m; 430 BufW bw; 431 KitArWriteOptions opts = {0}; 432 int seen = 0; 433 434 opts.symbol_index = 1; 435 msyms[0].names = s; 436 msyms[0].count = 1; 437 opts.member_symbols = msyms; 438 ms[0].name = KIT_SLICE_LIT("only.o"); 439 ms[0].bytes.data = (const uint8_t*)"Z"; 440 ms[0].bytes.len = 1; 441 442 bufw_init(&bw); 443 EXPECT(kit_ar_write(&bw.base, ms, 1, &opts) == 0, "write"); 444 445 in.data = bw.data; 446 in.len = bw.len; 447 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 448 while (kit_ar_iter_next(it, &m)) { 449 EXPECT(!kit_slice_eq_cstr(m.name, "/"), "iter surfaced raw `/` member"); 450 seen++; 451 } 452 EXPECT(seen == 1, "saw %d members", seen); 453 454 kit_ar_iter_free(it); 455 bufw_fini(&bw); 456 return 1; 457 } 458 459 static int test_empty_archive(void) { 460 /* nmembers == 0 with NULL members should produce a magic-only archive. */ 461 BufW bw; 462 KitSlice in; 463 KitArIter* it = NULL; 464 KitArMember m; 465 int rc; 466 467 bufw_init(&bw); 468 rc = kit_ar_write(&bw.base, NULL, 0, NULL); 469 EXPECT(rc == 0, "write rc=%d", rc); 470 EXPECT(bw.len == 8, "size = %zu", bw.len); 471 EXPECT(memcmp(bw.data, "!<arch>\n", 8) == 0, "magic only"); 472 473 in.data = bw.data; 474 in.len = bw.len; 475 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 476 EXPECT(!kit_ar_iter_next(it, &m), "no members"); 477 478 kit_ar_iter_free(it); 479 bufw_fini(&bw); 480 return 1; 481 } 482 483 static int test_epoch_field(void) { 484 /* opts.epoch is written into ar_date for every member. */ 485 KitArInput ms[1]; 486 BufW bw; 487 KitArWriteOptions opts = {0}; 488 int rc; 489 490 ms[0].name = KIT_SLICE_LIT("x.o"); 491 ms[0].bytes.data = (const uint8_t*)"q"; 492 ms[0].bytes.len = 1; 493 494 opts.epoch = 1234567890u; 495 bufw_init(&bw); 496 rc = kit_ar_write(&bw.base, ms, 1, &opts); 497 EXPECT(rc == 0, "write rc=%d", rc); 498 EXPECT(ar_date_field(bw.data + 8) == 1234567890u, "ar_date = %llu", 499 (unsigned long long)ar_date_field(bw.data + 8)); 500 bufw_fini(&bw); 501 502 /* Default (epoch=0): single '0' followed by spaces. */ 503 opts.epoch = 0; 504 bufw_init(&bw); 505 rc = kit_ar_write(&bw.base, ms, 1, &opts); 506 EXPECT(rc == 0, "write rc=%d", rc); 507 EXPECT(bw.data[8 + 16] == '0', "epoch default first byte"); 508 EXPECT(bw.data[8 + 17] == ' ', "epoch default second byte = 0x%02x", 509 bw.data[8 + 17]); 510 bufw_fini(&bw); 511 return 1; 512 } 513 514 static int test_path_basename(void) { 515 /* Member name with path components is stored as basename only. */ 516 KitArInput ms[1]; 517 BufW bw; 518 KitSlice in; 519 KitArIter* it = NULL; 520 KitArMember m; 521 522 ms[0].name = KIT_SLICE_LIT("src/sub/foo.o"); 523 ms[0].bytes.data = (const uint8_t*)"D"; 524 ms[0].bytes.len = 1; 525 526 bufw_init(&bw); 527 EXPECT(kit_ar_write(&bw.base, ms, 1, NULL) == 0, "write"); 528 529 in.data = bw.data; 530 in.len = bw.len; 531 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 532 EXPECT(kit_ar_iter_next(it, &m), "first"); 533 EXPECT(kit_slice_eq_cstr(m.name, "foo.o"), "basename = %.*s", 534 KIT_SLICE_ARG(m.name)); 535 536 kit_ar_iter_free(it); 537 bufw_fini(&bw); 538 return 1; 539 } 540 541 static int test_truncate_when_long_names_off(void) { 542 /* >15 chars without long_names: name is truncated to 15. */ 543 KitArInput ms[1]; 544 BufW bw; 545 KitSlice in; 546 KitArIter* it = NULL; 547 KitArMember m; 548 549 ms[0].name = KIT_SLICE_LIT("abcdefghijklmnopqrst.o"); /* 22 chars */ 550 ms[0].bytes.data = (const uint8_t*)"D"; 551 ms[0].bytes.len = 1; 552 553 bufw_init(&bw); 554 EXPECT(kit_ar_write(&bw.base, ms, 1, NULL) == 0, "write"); 555 556 in.data = bw.data; 557 in.len = bw.len; 558 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 559 EXPECT(kit_ar_iter_next(it, &m), "first"); 560 EXPECT(kit_slice_eq_cstr(m.name, "abcdefghijklmno"), "truncated = %.*s", 561 KIT_SLICE_ARG(m.name)); 562 563 kit_ar_iter_free(it); 564 bufw_fini(&bw); 565 return 1; 566 } 567 568 static int test_name_15_char_boundary(void) { 569 /* Exactly 15 chars: fits in-header even with long_names enabled. */ 570 KitArInput ms[1]; 571 BufW bw; 572 KitSlice in; 573 KitArIter* it = NULL; 574 KitArMember m; 575 KitArWriteOptions opts = {0}; 576 577 opts.long_names = 1; 578 ms[0].name = KIT_SLICE_LIT("abcdefghijklmno"); /* 15 chars */ 579 ms[0].bytes.data = (const uint8_t*)"X"; 580 ms[0].bytes.len = 1; 581 582 bufw_init(&bw); 583 EXPECT(kit_ar_write(&bw.base, ms, 1, &opts) == 0, "write"); 584 585 /* No `//` long-name table: first member sits right after magic. */ 586 EXPECT(memcmp(bw.data + 8, "abcdefghijklmno/", 16) == 0, "name field = %.16s", 587 (const char*)(bw.data + 8)); 588 589 in.data = bw.data; 590 in.len = bw.len; 591 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 592 EXPECT(kit_ar_iter_next(it, &m), "first"); 593 EXPECT(kit_slice_eq_cstr(m.name, "abcdefghijklmno"), "name = %.*s", 594 KIT_SLICE_ARG(m.name)); 595 596 kit_ar_iter_free(it); 597 bufw_fini(&bw); 598 return 1; 599 } 600 601 static int test_name_16_char_boundary(void) { 602 /* Exactly 16 chars: triggers // long-name table. */ 603 KitArInput ms[1]; 604 BufW bw; 605 KitSlice in; 606 KitArIter* it = NULL; 607 KitArMember m; 608 KitArWriteOptions opts = {0}; 609 610 opts.long_names = 1; 611 ms[0].name = KIT_SLICE_LIT("abcdefghijklmnop"); /* 16 chars */ 612 ms[0].bytes.data = (const uint8_t*)"Y"; 613 ms[0].bytes.len = 1; 614 615 bufw_init(&bw); 616 EXPECT(kit_ar_write(&bw.base, ms, 1, &opts) == 0, "write"); 617 EXPECT(bw.data[8] == '/' && bw.data[9] == '/', "// header"); 618 619 in.data = bw.data; 620 in.len = bw.len; 621 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 622 EXPECT(kit_ar_iter_next(it, &m), "first"); 623 EXPECT(kit_slice_eq_cstr(m.name, "abcdefghijklmnop"), "name = %.*s", 624 KIT_SLICE_ARG(m.name)); 625 626 kit_ar_iter_free(it); 627 bufw_fini(&bw); 628 return 1; 629 } 630 631 static int test_empty_member_payload(void) { 632 /* len=0 (data=NULL): header only, no pad; followed by the next member. */ 633 KitArInput ms[2]; 634 BufW bw; 635 KitSlice in; 636 KitArIter* it = NULL; 637 KitArMember m; 638 639 ms[0].name = KIT_SLICE_LIT("empty.o"); 640 ms[0].bytes.data = NULL; 641 ms[0].bytes.len = 0; 642 ms[1].name = KIT_SLICE_LIT("next.o"); 643 ms[1].bytes.data = (const uint8_t*)"N"; 644 ms[1].bytes.len = 1; 645 646 bufw_init(&bw); 647 EXPECT(kit_ar_write(&bw.base, ms, 2, NULL) == 0, "write"); 648 /* magic(8) + hdr(60) + 0 + hdr(60) + 1 + pad(1) = 130 */ 649 EXPECT(bw.len == 130, "size = %zu", bw.len); 650 651 in.data = bw.data; 652 in.len = bw.len; 653 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 654 EXPECT(kit_ar_iter_next(it, &m), "first"); 655 EXPECT(kit_slice_eq_cstr(m.name, "empty.o") && m.size == 0, 656 "empty.o size=%zu", m.size); 657 EXPECT(kit_ar_iter_next(it, &m), "second"); 658 EXPECT(kit_slice_eq_cstr(m.name, "next.o") && m.size == 1 && m.data[0] == 'N', 659 "next.o"); 660 EXPECT(!kit_ar_iter_next(it, &m), "iter end"); 661 662 kit_ar_iter_free(it); 663 bufw_fini(&bw); 664 return 1; 665 } 666 667 static int test_odd_payload_pad(void) { 668 /* Odd-length payloads add a '\n' parity pad; even lengths do not. */ 669 KitArInput ms[3]; 670 BufW bw; 671 672 ms[0].name = KIT_SLICE_LIT("a.o"); 673 ms[0].bytes.data = (const uint8_t*)"x"; 674 ms[0].bytes.len = 1; 675 ms[1].name = KIT_SLICE_LIT("b.o"); 676 ms[1].bytes.data = (const uint8_t*)"yy"; 677 ms[1].bytes.len = 2; 678 ms[2].name = KIT_SLICE_LIT("c.o"); 679 ms[2].bytes.data = (const uint8_t*)"z"; 680 ms[2].bytes.len = 1; 681 682 bufw_init(&bw); 683 EXPECT(kit_ar_write(&bw.base, ms, 3, NULL) == 0, "write"); 684 /* 8 + (60+1+1) + (60+2) + (60+1+1) = 194 */ 685 EXPECT(bw.len == 194, "size = %zu", bw.len); 686 EXPECT(bw.data[8 + 60 + 1] == '\n', "pad after a.o = 0x%02x", 687 bw.data[8 + 60 + 1]); 688 /* No pad after b.o (even): next header begins immediately. */ 689 EXPECT(bw.data[8 + 62 + 60 + 2] == 'c', "c.o name follows b.o without pad"); 690 691 bufw_fini(&bw); 692 return 1; 693 } 694 695 static int test_ar_list_output(void) { 696 /* kit_ar_list emits one user member per line, skipping / and //. */ 697 KitArInput ms[3]; 698 BufW bw, lw; 699 KitSlice in; 700 KitArWriteOptions opts = {0}; 701 const char* expected = "a.o\nlong_name_member.o\nb.o\n"; 702 703 opts.symbol_index = 1; 704 opts.long_names = 1; 705 ms[0].name = KIT_SLICE_LIT("a.o"); 706 ms[0].bytes.data = (const uint8_t*)"A"; 707 ms[0].bytes.len = 1; 708 ms[1].name = KIT_SLICE_LIT("long_name_member.o"); 709 ms[1].bytes.data = (const uint8_t*)"B"; 710 ms[1].bytes.len = 1; 711 ms[2].name = KIT_SLICE_LIT("b.o"); 712 ms[2].bytes.data = (const uint8_t*)"C"; 713 ms[2].bytes.len = 1; 714 715 bufw_init(&bw); 716 EXPECT(kit_ar_write(&bw.base, ms, 3, &opts) == 0, "write"); 717 718 in.data = bw.data; 719 in.len = bw.len; 720 bufw_init(&lw); 721 EXPECT(kit_ar_list(&in, &lw.base) == 0, "list"); 722 EXPECT(lw.len == strlen(expected), "list len = %zu, want %zu", lw.len, 723 strlen(expected)); 724 EXPECT(memcmp(lw.data, expected, lw.len) == 0, "list = %.*s", (int)lw.len, 725 (const char*)lw.data); 726 727 bufw_fini(&lw); 728 bufw_fini(&bw); 729 return 1; 730 } 731 732 static int test_iter_bad_magic(void) { 733 /* iter_init must reject non-ar inputs. */ 734 static const uint8_t bad[] = "NOT-AN-AR"; 735 KitSlice in; 736 KitArIter* it = NULL; 737 738 in.data = bad; 739 in.len = sizeof(bad) - 1; 740 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) != KIT_OK, "rejects bad magic"); 741 742 in.data = NULL; 743 in.len = 0; 744 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) != KIT_OK, "rejects empty"); 745 746 in.data = bad; 747 in.len = 4; /* too short */ 748 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) != KIT_OK, "rejects short"); 749 750 return 1; 751 } 752 753 static int test_write_invalid_args(void) { 754 /* Bad argument combinations must return 1 from kit_ar_write. */ 755 KitArInput ms[1]; 756 BufW bw; 757 KitArWriteOptions opts = {0}; 758 KitSlice bad_syms[1]; 759 KitArMemberSymbols msyms[1]; 760 761 bufw_init(&bw); 762 763 EXPECT(kit_ar_write(NULL, NULL, 0, NULL) != KIT_OK, "NULL writer rejected"); 764 EXPECT(kit_ar_write(&bw.base, NULL, 1, NULL) != KIT_OK, 765 "NULL members with nmembers>0 rejected"); 766 767 ms[0].name = KIT_SLICE_NULL; 768 ms[0].bytes.data = (const uint8_t*)"X"; 769 ms[0].bytes.len = 1; 770 EXPECT(kit_ar_write(&bw.base, ms, 1, NULL) != KIT_OK, "NULL name rejected"); 771 772 /* NULL symbol-name with nonzero count. */ 773 bad_syms[0] = KIT_SLICE_NULL; 774 msyms[0].names = bad_syms; 775 msyms[0].count = 1; 776 opts.symbol_index = 1; 777 opts.member_symbols = msyms; 778 ms[0].name = KIT_SLICE_LIT("ok.o"); 779 EXPECT(kit_ar_write(&bw.base, ms, 1, &opts) != KIT_OK, 780 "NULL symbol name rejected"); 781 782 bufw_fini(&bw); 783 return 1; 784 } 785 786 static int test_iter_skips_bsd_symdef(void) { 787 /* Hand-craft an archive containing a BSD __.SYMDEF SORTED member; the 788 * iterator must not surface it. (kit_ar_write never emits BSD indexes, 789 * but the iterator is documented to handle them on read.) */ 790 BufW bw; 791 KitSlice in; 792 KitArIter* it = NULL; 793 KitArMember m; 794 char hdr[60]; 795 size_t j; 796 int seen = 0; 797 798 bufw_init(&bw); 799 bw.base.write(&bw.base, "!<arch>\n", 8); 800 801 /* __.SYMDEF SORTED with a 4-byte payload. */ 802 for (j = 0; j < 60; ++j) hdr[j] = ' '; 803 { 804 const char* nm = "__.SYMDEF SORTED"; 805 for (j = 0; j < 16 && nm[j]; ++j) hdr[j] = nm[j]; 806 } 807 hdr[16] = '0'; 808 hdr[28] = '0'; 809 hdr[34] = '0'; 810 hdr[40] = '6'; 811 hdr[41] = '4'; 812 hdr[42] = '4'; 813 hdr[48] = '4'; /* size = 4 */ 814 hdr[58] = '`'; 815 hdr[59] = '\n'; 816 bw.base.write(&bw.base, hdr, 60); 817 bw.base.write(&bw.base, "ZZZZ", 4); 818 819 /* User member u.o size=1 + parity pad. */ 820 for (j = 0; j < 60; ++j) hdr[j] = ' '; 821 hdr[0] = 'u'; 822 hdr[1] = '.'; 823 hdr[2] = 'o'; 824 hdr[3] = '/'; 825 hdr[16] = '0'; 826 hdr[28] = '0'; 827 hdr[34] = '0'; 828 hdr[40] = '6'; 829 hdr[41] = '4'; 830 hdr[42] = '4'; 831 hdr[48] = '1'; 832 hdr[58] = '`'; 833 hdr[59] = '\n'; 834 bw.base.write(&bw.base, hdr, 60); 835 bw.base.write(&bw.base, "U\n", 2); 836 837 in.data = bw.data; 838 in.len = bw.len; 839 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 840 while (kit_ar_iter_next(it, &m)) { 841 EXPECT(kit_slice_eq_cstr(m.name, "u.o"), "unexpected name = %.*s", 842 KIT_SLICE_ARG(m.name)); 843 seen++; 844 } 845 EXPECT(seen == 1, "saw %d members", seen); 846 847 kit_ar_iter_free(it); 848 bufw_fini(&bw); 849 return 1; 850 } 851 852 static int test_iter_data_aliases_archive(void) { 853 /* KitArMember.data must point into the archive's own bytes. */ 854 KitArInput ms[1]; 855 BufW bw; 856 KitSlice in; 857 KitArIter* it = NULL; 858 KitArMember m; 859 860 ms[0].name = KIT_SLICE_LIT("a.o"); 861 ms[0].bytes.data = (const uint8_t*)"PAYLOAD"; 862 ms[0].bytes.len = 7; 863 864 bufw_init(&bw); 865 EXPECT(kit_ar_write(&bw.base, ms, 1, NULL) == 0, "write"); 866 867 in.data = bw.data; 868 in.len = bw.len; 869 EXPECT(kit_ar_iter_new(&g_ctx, &in, &it) == KIT_OK, "iter_init"); 870 EXPECT(kit_ar_iter_next(it, &m), "first"); 871 EXPECT(m.data >= bw.data && m.data + m.size <= bw.data + bw.len, 872 "data aliases archive bytes"); 873 EXPECT(memcmp(m.data, "PAYLOAD", 7) == 0, "payload"); 874 875 kit_ar_iter_free(it); 876 bufw_fini(&bw); 877 return 1; 878 } 879 880 static int test_symbol_index_partial_members(void) { 881 /* Members with 0 symbols mid-list: cur_offset must still advance for 882 * every member so later offsets land on the right header. */ 883 KitSlice mid_syms[] = {KIT_SLICE_LIT("midsym")}; 884 KitArMemberSymbols msyms[3]; 885 KitArInput ms[3]; 886 BufW bw; 887 KitArWriteOptions opts = {0}; 888 uint32_t nsyms, off; 889 uint64_t expected_b_hdr; 890 891 opts.symbol_index = 1; 892 msyms[0].names = NULL; 893 msyms[0].count = 0; 894 msyms[1].names = mid_syms; 895 msyms[1].count = 1; 896 msyms[2].names = NULL; 897 msyms[2].count = 0; 898 opts.member_symbols = msyms; 899 900 ms[0].name = KIT_SLICE_LIT("a.o"); 901 ms[0].bytes.data = (const uint8_t*)"AA"; 902 ms[0].bytes.len = 2; 903 ms[1].name = KIT_SLICE_LIT("b.o"); 904 ms[1].bytes.data = (const uint8_t*)"BB"; 905 ms[1].bytes.len = 2; 906 ms[2].name = KIT_SLICE_LIT("c.o"); 907 ms[2].bytes.data = (const uint8_t*)"CC"; 908 ms[2].bytes.len = 2; 909 910 bufw_init(&bw); 911 EXPECT(kit_ar_write(&bw.base, ms, 3, &opts) == 0, "write"); 912 913 /* Index payload: 4(count) + 4(offset) + 7("midsym\0") = 15 → odd → +1 pad. */ 914 EXPECT(ar_size_field(bw.data + 8) == 15, "index payload = %llu", 915 (unsigned long long)ar_size_field(bw.data + 8)); 916 917 nsyms = be32(bw.data + 8 + 60); 918 EXPECT(nsyms == 1, "nsyms = %u", nsyms); 919 920 off = be32(bw.data + 8 + 60 + 4); 921 /* magic(8) + index(60+15+1) + a.o(60+2) = 146 */ 922 expected_b_hdr = 8 + 60 + 15 + 1 + 60 + 2; 923 EXPECT(off == expected_b_hdr, "off = %u, expected %llu", off, 924 (unsigned long long)expected_b_hdr); 925 EXPECT(memcmp(bw.data + off, "b.o/", 4) == 0, "b.o at offset"); 926 927 bufw_fini(&bw); 928 return 1; 929 } 930 931 int main(void) { 932 int passes = 0; 933 int total = 0; 934 #define RUN(t) \ 935 do { \ 936 total++; \ 937 passes += (t)(); \ 938 } while (0) 939 940 kit_unit_init(&g_u); 941 RUN(test_basic_roundtrip); 942 RUN(test_long_name_table); 943 RUN(test_symbol_index_empty); 944 RUN(test_symbol_index_basic); 945 RUN(test_symbol_index_with_long_names); 946 RUN(test_iter_skips_index); 947 RUN(test_empty_archive); 948 RUN(test_epoch_field); 949 RUN(test_path_basename); 950 RUN(test_truncate_when_long_names_off); 951 RUN(test_name_15_char_boundary); 952 RUN(test_name_16_char_boundary); 953 RUN(test_empty_member_payload); 954 RUN(test_odd_payload_pad); 955 RUN(test_ar_list_output); 956 RUN(test_iter_bad_magic); 957 RUN(test_write_invalid_args); 958 RUN(test_iter_skips_bsd_symdef); 959 RUN(test_iter_data_aliases_archive); 960 RUN(test_symbol_index_partial_members); 961 962 if (g_u.fails) { 963 fprintf(stderr, "ar_test: %d failure(s) (%d/%d passed)\n", g_u.fails, 964 passes, total); 965 return 1; 966 } 967 printf("ar_test: OK (%d/%d)\n", passes, total); 968 return 0; 969 }