1#define _POSIX_C_SOURCE 200112L
   2#include "minunit.h"
   3
   4#define NONSTD_IMPLEMENTATION
   5#include "nonstd.h"
   6
   7#include <fcntl.h>
   8#include <stdlib.h>
   9#include <unistd.h>
  10
  11// Custom test runner that prints test names
  12#define RUN_TEST_WITH_NAME(test)                   \
  13	do {                                           \
  14		printf("  %-50s ", #test);                 \
  15		fflush(stdout);                            \
  16		int before_fail = minunit_fail;            \
  17		int saved_stdout = dup(STDOUT_FILENO);     \
  18		int devnull = open("/dev/null", O_WRONLY); \
  19		dup2(devnull, STDOUT_FILENO);              \
  20		close(devnull);                            \
  21		MU_RUN_TEST(test);                         \
  22		fflush(stdout);                            \
  23		dup2(saved_stdout, STDOUT_FILENO);         \
  24		close(saved_stdout);                       \
  25		if (minunit_fail == before_fail) {         \
  26			printf("PASS\n");                      \
  27		}                                          \
  28	} while (0)
  29
  30// String view tests
  31MU_TEST(test_sv_from_cstr) {
  32	stringv sv = sv_from_cstr("hello");
  33	mu_assert_int_eq(5, sv.length);
  34	mu_check(sv.data != NULL);
  35	mu_check(strncmp(sv.data, "hello", 5) == 0);
  36}
  37
  38MU_TEST(test_sv_from_cstr_null) {
  39	stringv sv = sv_from_cstr(NULL);
  40	mu_assert_int_eq(0, sv.length);
  41}
  42
  43MU_TEST(test_sv_from_parts) {
  44	const char *str = "hello world";
  45	stringv sv = sv_from_parts(str, 5);
  46	mu_assert_int_eq(5, sv.length);
  47	mu_check(sv.data == str);
  48}
  49
  50MU_TEST(test_sv_from_cstr_literal) {
  51	stringv sv = sv_from_cstr("test");
  52	mu_assert_int_eq(4, sv.length);
  53	mu_check(strncmp(sv.data, "test", 4) == 0);
  54}
  55
  56MU_TEST(test_sv_slice_normal) {
  57	stringv sv = sv_from_cstr("hello world");
  58	stringv sliced = sv_slice(sv, 0, 5);
  59	mu_assert_int_eq(5, sliced.length);
  60	mu_check(strncmp(sliced.data, "hello", 5) == 0);
  61}
  62
  63MU_TEST(test_sv_slice_middle) {
  64	stringv sv = sv_from_cstr("hello world");
  65	stringv sliced = sv_slice(sv, 6, 11);
  66	mu_assert_int_eq(5, sliced.length);
  67	mu_check(strncmp(sliced.data, "world", 5) == 0);
  68}
  69
  70MU_TEST(test_sv_slice_out_of_bounds) {
  71	stringv sv = sv_from_cstr("hello");
  72	stringv sliced = sv_slice(sv, 0, 100);
  73	mu_assert_int_eq(5, sliced.length);
  74}
  75
  76MU_TEST(test_sv_slice_invalid_range) {
  77	stringv sv = sv_from_cstr("hello");
  78	stringv sliced = sv_slice(sv, 10, 5);
  79	mu_assert_int_eq(0, sliced.length);
  80}
  81
  82MU_TEST(test_sv_equals_same) {
  83	stringv a = sv_from_cstr("hello");
  84	stringv b = sv_from_cstr("hello");
  85	mu_check(sv_equals(a, b));
  86}
  87
  88MU_TEST(test_sv_equals_different) {
  89	stringv a = sv_from_cstr("hello");
  90	stringv b = sv_from_cstr("world");
  91	mu_check(!sv_equals(a, b));
  92}
  93
  94MU_TEST(test_sv_equals_different_length) {
  95	stringv a = sv_from_cstr("hello");
  96	stringv b = sv_from_cstr("hi");
  97	mu_check(!sv_equals(a, b));
  98}
  99
 100MU_TEST(test_sv_equals_empty) {
 101	stringv a = sv_from_parts(NULL, 0);
 102	stringv b = sv_from_parts(NULL, 0);
 103	mu_check(sv_equals(a, b));
 104}
 105
 106MU_TEST(test_sv_starts_with_true) {
 107	stringv sv = sv_from_cstr("hello world");
 108	stringv prefix = sv_from_cstr("hello");
 109	mu_check(sv_starts_with(sv, prefix));
 110}
 111
 112MU_TEST(test_sv_starts_with_false) {
 113	stringv sv = sv_from_cstr("hello world");
 114	stringv prefix = sv_from_cstr("world");
 115	mu_check(!sv_starts_with(sv, prefix));
 116}
 117
 118MU_TEST(test_sv_starts_with_empty_prefix) {
 119	stringv sv = sv_from_cstr("hello");
 120	stringv prefix = sv_from_parts(NULL, 0);
 121	mu_check(sv_starts_with(sv, prefix));
 122}
 123
 124MU_TEST(test_sv_starts_with_longer_prefix) {
 125	stringv sv = sv_from_cstr("hi");
 126	stringv prefix = sv_from_cstr("hello");
 127	mu_check(!sv_starts_with(sv, prefix));
 128}
 129
 130MU_TEST(test_sv_ends_with_true) {
 131	stringv sv = sv_from_cstr("hello world");
 132	stringv suffix = sv_from_cstr("world");
 133	mu_check(sv_ends_with(sv, suffix));
 134}
 135
 136MU_TEST(test_sv_ends_with_false) {
 137	stringv sv = sv_from_cstr("hello world");
 138	stringv suffix = sv_from_cstr("hello");
 139	mu_check(!sv_ends_with(sv, suffix));
 140}
 141
 142MU_TEST(test_sv_ends_with_empty_suffix) {
 143	stringv sv = sv_from_cstr("hello");
 144	stringv suffix = sv_from_parts(NULL, 0);
 145	mu_check(sv_ends_with(sv, suffix));
 146}
 147
 148MU_TEST(test_sv_ends_with_longer_suffix) {
 149	stringv sv = sv_from_cstr("hi");
 150	stringv suffix = sv_from_cstr("world");
 151	mu_check(!sv_ends_with(sv, suffix));
 152}
 153
 154// Macro tests
 155MU_TEST(test_countof) {
 156	int array[10];
 157	mu_assert_int_eq(10, countof(array));
 158
 159	char str[] = "hello";
 160	mu_assert_int_eq(6, countof(str)); // includes null terminator
 161}
 162
 163MU_TEST(test_min_max_clamp) {
 164	mu_assert_int_eq(5, MIN(5, 10));
 165	mu_assert_int_eq(5, MIN(10, 5));
 166
 167	mu_assert_int_eq(10, MAX(5, 10));
 168	mu_assert_int_eq(10, MAX(10, 5));
 169
 170	mu_assert_int_eq(5, CLAMP(3, 5, 10));
 171	mu_assert_int_eq(7, CLAMP(7, 5, 10));
 172	mu_assert_int_eq(10, CLAMP(15, 5, 10));
 173}
 174
 175MU_TEST(test_static_foreach_macro) {
 176	int values[] = {1, 2, 3, 4, 5};
 177	int sum = 0;
 178	int val;
 179
 180	static_foreach(int, val, values) { sum += val; }
 181
 182	mu_assert_int_eq(15, sum);
 183}
 184
 185MU_TEST(test_static_foreach_with_strings) {
 186	const char *words[] = {"hello", "world", "test"};
 187	int count = 0;
 188	const char *word;
 189
 190	static_foreach(const char *, word, words) {
 191		count++;
 192		mu_check(word != NULL);
 193	}
 194
 195	mu_assert_int_eq(3, count);
 196}
 197
 198MU_TEST(test_static_foreach_single_element) {
 199	int single[] = {42};
 200	int result = 0;
 201	int val;
 202
 203	static_foreach(int, val, single) { result = val; }
 204
 205	mu_assert_int_eq(42, result);
 206}
 207
 208MU_TEST(test_static_foreach_modify_counter) {
 209	u32 numbers[] = {10, 20, 30, 40};
 210	u32 max = 0;
 211	u32 num;
 212
 213	static_foreach(u32, num, numbers) {
 214		if (num > max)
 215			max = num;
 216	}
 217
 218	mu_assert_int_eq(40, max);
 219}
 220
 221MU_TEST(test_static_foreach_with_structs) {
 222	struct Point {
 223		int x, y;
 224	};
 225
 226	struct Point pts[] = {{1, 2}, {3, 4}, {5, 6}};
 227	int sum_x = 0;
 228	int sum_y = 0;
 229	struct Point p;
 230
 231	static_foreach(struct Point, p, pts) {
 232		sum_x += p.x;
 233		sum_y += p.y;
 234	}
 235
 236	mu_assert_int_eq(9, sum_x);	 // 1 + 3 + 5
 237	mu_assert_int_eq(12, sum_y); // 2 + 4 + 6
 238}
 239
 240MU_TEST(test_alloc_and_free) {
 241	int *ptr = ALLOC(int, 10);
 242	mu_check(ptr != NULL);
 243
 244	// Use the memory
 245	ptr[0] = 42;
 246	ptr[9] = 99;
 247	mu_assert_int_eq(42, ptr[0]);
 248	mu_assert_int_eq(99, ptr[9]);
 249
 250	FREE(ptr);
 251	mu_check(ptr == NULL);
 252}
 253
 254MU_TEST(test_realloc) {
 255	int *ptr = ALLOC(int, 5);
 256	mu_check(ptr != NULL);
 257
 258	ptr[0] = 1;
 259	ptr[4] = 5;
 260
 261	ptr = REALLOC(ptr, int, 10);
 262	mu_check(ptr != NULL);
 263
 264	mu_assert_int_eq(1, ptr[0]);
 265	mu_assert_int_eq(5, ptr[4]);
 266	ptr[9] = 99;
 267	mu_assert_int_eq(99, ptr[9]);
 268
 269	free(ptr);
 270}
 271
 272MU_TEST(test_typedefs) {
 273	// Just verify the types compile and have expected sizes
 274	i8 a = -1;
 275	u8 b = 255;
 276	i16 c = -1000;
 277	u16 d = 60000;
 278	i32 e = -100000;
 279	u32 f = 4000000000U;
 280	i64 g = -1000000000LL;
 281	u64 h = 10000000000ULL;
 282
 283	mu_check(a == -1);
 284	mu_check(b == 255);
 285	mu_check(c == -1000);
 286	mu_check(d == 60000);
 287	mu_check(e == -100000);
 288	mu_check(f == 4000000000U);
 289	mu_check(g == -1000000000LL);
 290	mu_check(h == 10000000000ULL);
 291}
 292
 293// String builder tests
 294MU_TEST(test_sb_init) {
 295	stringb sb;
 296	sb_init(&sb, 0);
 297	mu_check(sb.data != NULL);
 298	mu_assert_int_eq(0, sb.length);
 299	mu_check(sb.capacity >= 16); // default capacity
 300	mu_assert_int_eq('\0', sb.data[0]);
 301	sb_free(&sb);
 302}
 303
 304MU_TEST(test_sb_init_with_capacity) {
 305	stringb sb;
 306	sb_init(&sb, 64);
 307	mu_check(sb.data != NULL);
 308	mu_assert_int_eq(0, sb.length);
 309	mu_assert_int_eq(64, sb.capacity);
 310	sb_free(&sb);
 311}
 312
 313MU_TEST(test_sb_append_cstr) {
 314	stringb sb;
 315	sb_init(&sb, 0);
 316	sb_append_cstr(&sb, "hello");
 317	mu_assert_int_eq(5, sb.length);
 318	mu_check(strcmp(sb.data, "hello") == 0);
 319	sb_free(&sb);
 320}
 321
 322MU_TEST(test_sb_append_cstr_multiple) {
 323	stringb sb;
 324	sb_init(&sb, 0);
 325	sb_append_cstr(&sb, "hello");
 326	sb_append_cstr(&sb, " ");
 327	sb_append_cstr(&sb, "world");
 328	mu_assert_int_eq(11, sb.length);
 329	mu_check(strcmp(sb.data, "hello world") == 0);
 330	sb_free(&sb);
 331}
 332
 333MU_TEST(test_sb_append_cstr_null) {
 334	stringb sb;
 335	sb_init(&sb, 0);
 336	sb_append_cstr(&sb, "test");
 337	sb_append_cstr(&sb, NULL);
 338	mu_assert_int_eq(4, sb.length);
 339	mu_check(strcmp(sb.data, "test") == 0);
 340	sb_free(&sb);
 341}
 342
 343MU_TEST(test_sb_append_sv) {
 344	stringb sb;
 345	sb_init(&sb, 0);
 346	stringv sv = sv_from_cstr("hello");
 347	sb_append_sv(&sb, sv);
 348	mu_assert_int_eq(5, sb.length);
 349	mu_check(strcmp(sb.data, "hello") == 0);
 350	sb_free(&sb);
 351}
 352
 353MU_TEST(test_sb_append_sv_slice) {
 354	stringb sb;
 355	sb_init(&sb, 0);
 356	stringv full = sv_from_cstr("hello world");
 357	stringv slice = sv_slice(full, 6, 11);
 358	sb_append_sv(&sb, slice);
 359	mu_assert_int_eq(5, sb.length);
 360	mu_check(strcmp(sb.data, "world") == 0);
 361	sb_free(&sb);
 362}
 363
 364MU_TEST(test_sb_append_sv_empty) {
 365	stringb sb;
 366	sb_init(&sb, 0);
 367	sb_append_cstr(&sb, "test");
 368	stringv empty = sv_from_parts(NULL, 0);
 369	sb_append_sv(&sb, empty);
 370	mu_assert_int_eq(4, sb.length);
 371	mu_check(strcmp(sb.data, "test") == 0);
 372	sb_free(&sb);
 373}
 374
 375MU_TEST(test_sb_append_char) {
 376	stringb sb;
 377	sb_init(&sb, 0);
 378	sb_append_char(&sb, 'a');
 379	sb_append_char(&sb, 'b');
 380	sb_append_char(&sb, 'c');
 381	mu_assert_int_eq(3, sb.length);
 382	mu_check(strcmp(sb.data, "abc") == 0);
 383	sb_free(&sb);
 384}
 385
 386MU_TEST(test_sb_mixed_append) {
 387	stringb sb;
 388	sb_init(&sb, 0);
 389	sb_append_cstr(&sb, "Hello");
 390	sb_append_char(&sb, ' ');
 391	stringv sv = sv_from_cstr("beautiful");
 392	sb_append_sv(&sb, sv);
 393	sb_append_char(&sb, ' ');
 394	sb_append_cstr(&sb, "world!");
 395	mu_assert_int_eq(22, sb.length);
 396	mu_check(strcmp(sb.data, "Hello beautiful world!") == 0);
 397	sb_free(&sb);
 398}
 399
 400MU_TEST(test_sb_as_sv) {
 401	stringb sb;
 402	sb_init(&sb, 0);
 403	sb_append_cstr(&sb, "test string");
 404	stringv sv = sb_as_sv(&sb);
 405	mu_assert_int_eq(11, sv.length);
 406	mu_check(sv.data == sb.data);
 407	mu_check(strncmp(sv.data, "test string", sv.length) == 0);
 408	sb_free(&sb);
 409}
 410
 411MU_TEST(test_sb_growth) {
 412	stringb sb;
 413	sb_init(&sb, 4); // very small initial capacity
 414	mu_assert_int_eq(4, sb.capacity);
 415
 416	// Append enough to trigger growth
 417	sb_append_cstr(&sb, "this is a long string that will exceed initial capacity");
 418	mu_check(sb.capacity > 4); // capacity should have grown
 419	mu_check(sb.length == strlen("this is a long string that will exceed initial capacity"));
 420	mu_check(strcmp(sb.data, "this is a long string that will exceed initial capacity") == 0);
 421	sb_free(&sb);
 422}
 423
 424MU_TEST(test_sb_free) {
 425	stringb sb;
 426	sb_init(&sb, 32);
 427	sb_append_cstr(&sb, "test");
 428	sb_free(&sb);
 429	mu_check(sb.data == NULL);
 430	mu_assert_int_eq(0, sb.length);
 431	mu_assert_int_eq(0, sb.capacity);
 432}
 433
 434MU_TEST(test_sb_sv_interop) {
 435	// Test that sb can be used to build a string, then viewed with sv
 436	stringb sb;
 437	sb_init(&sb, 0);
 438
 439	const char *words[] = {"one", "two", "three"};
 440	const char *word;
 441	static_foreach(const char *, word, words) {
 442		if (sb.length > 0) {
 443			sb_append_char(&sb, ',');
 444		}
 445		sb_append_cstr(&sb, word);
 446	}
 447
 448	stringv result = sb_as_sv(&sb);
 449	mu_check(sv_equals(result, sv_from_cstr("one,two,three")));
 450
 451	// Can use sv operations on the builder's content
 452	mu_check(sv_starts_with(result, sv_from_cstr("one")));
 453	mu_check(sv_ends_with(result, sv_from_cstr("three")));
 454
 455	sb_free(&sb);
 456}
 457
 458MU_TEST(test_sb_append_sv_from_sb) {
 459	// Test appending a string view of one builder to another
 460	stringb sb1, sb2;
 461	sb_init(&sb1, 0);
 462	sb_init(&sb2, 0);
 463
 464	sb_append_cstr(&sb1, "hello");
 465	sb_append_cstr(&sb2, "world");
 466
 467	stringv sv1 = sb_as_sv(&sb1);
 468	sb_append_char(&sb2, ' ');
 469	sb_append_sv(&sb2, sv1);
 470
 471	mu_check(strcmp(sb2.data, "world hello") == 0);
 472
 473	sb_free(&sb1);
 474	sb_free(&sb2);
 475}
 476
 477// Array tests
 478MU_TEST(test_array_init) {
 479	array(int) arr;
 480	array_init(arr);
 481	mu_check(arr.data == NULL);
 482	mu_assert_int_eq(0, arr.length);
 483	mu_assert_int_eq(0, arr.capacity);
 484	array_free(arr);
 485}
 486
 487MU_TEST(test_array_init_cap) {
 488	array(int) arr;
 489	array_init_cap(arr, 32);
 490	mu_check(arr.data != NULL);
 491	mu_assert_int_eq(0, arr.length);
 492	mu_assert_int_eq(32, arr.capacity);
 493	array_free(arr);
 494}
 495
 496MU_TEST(test_array_push) {
 497	array(int) arr;
 498	array_init(arr);
 499	array_push(arr, 10);
 500	array_push(arr, 20);
 501	array_push(arr, 30);
 502
 503	mu_assert_int_eq(3, arr.length);
 504	mu_check(arr.capacity >= 3);
 505	mu_assert_int_eq(10, arr.data[0]);
 506	mu_assert_int_eq(20, arr.data[1]);
 507	mu_assert_int_eq(30, arr.data[2]);
 508
 509	array_free(arr);
 510}
 511
 512MU_TEST(test_array_pop) {
 513	array(int) arr;
 514	array_init(arr);
 515	array_push(arr, 10);
 516	array_push(arr, 20);
 517
 518	int val = array_pop(arr);
 519	mu_assert_int_eq(20, val);
 520	mu_assert_int_eq(1, arr.length);
 521
 522	val = array_pop(arr);
 523	mu_assert_int_eq(10, val);
 524	mu_assert_int_eq(0, arr.length);
 525
 526	array_free(arr);
 527}
 528
 529MU_TEST(test_array_pop_empty) {
 530	array(int) arr;
 531	array_init(arr);
 532	int val = array_pop(arr);
 533	mu_assert_int_eq(0, val);
 534	mu_assert_int_eq(0, arr.length);
 535	array_free(arr);
 536}
 537
 538MU_TEST(test_array_get_set) {
 539	array(int) arr;
 540	array_init(arr);
 541	array_push(arr, 10);
 542	array_push(arr, 20);
 543
 544	mu_assert_int_eq(10, array_get(arr, 0));
 545	mu_assert_int_eq(20, array_get(arr, 1));
 546
 547	array_set(arr, 0, 100);
 548	mu_assert_int_eq(100, array_get(arr, 0));
 549
 550	// Test out of bounds set (should do nothing)
 551	array_set(arr, 5, 500);
 552	mu_assert_int_eq(2, arr.length);
 553
 554	array_free(arr);
 555}
 556
 557MU_TEST(test_array_insert) {
 558	array(int) arr;
 559	array_init(arr);
 560	array_push(arr, 10);
 561	array_push(arr, 30);
 562
 563	array_insert(arr, 1, 20); // Insert between 10 and 30
 564	mu_assert_int_eq(3, arr.length);
 565	mu_assert_int_eq(10, arr.data[0]);
 566	mu_assert_int_eq(20, arr.data[1]);
 567	mu_assert_int_eq(30, arr.data[2]);
 568
 569	array_insert(arr, 0, 5); // Insert at start
 570	mu_assert_int_eq(5, arr.data[0]);
 571	mu_assert_int_eq(10, arr.data[1]);
 572
 573	array_insert(arr, 4, 40); // Insert at end
 574	mu_assert_int_eq(40, arr.data[4]);
 575	mu_assert_int_eq(5, arr.length);
 576
 577	array_free(arr);
 578}
 579
 580MU_TEST(test_array_remove) {
 581	array(int) arr;
 582	array_init(arr);
 583	array_push(arr, 10);
 584	array_push(arr, 20);
 585	array_push(arr, 30);
 586	array_push(arr, 40);
 587
 588	array_remove(arr, 1); // Remove 20
 589	mu_assert_int_eq(3, arr.length);
 590	mu_assert_int_eq(10, arr.data[0]);
 591	mu_assert_int_eq(30, arr.data[1]);
 592	mu_assert_int_eq(40, arr.data[2]);
 593
 594	array_remove(arr, 0); // Remove 10 (start)
 595	mu_assert_int_eq(30, arr.data[0]);
 596
 597	array_remove(arr, 1); // Remove 40 (end)
 598	mu_assert_int_eq(30, arr.data[0]);
 599	mu_assert_int_eq(1, arr.length);
 600
 601	array_free(arr);
 602}
 603
 604MU_TEST(test_array_growth) {
 605	array(int) arr;
 606	array_init_cap(arr, 4);
 607
 608	for (int i = 0; i < 20; i++) {
 609		array_push(arr, i);
 610	}
 611
 612	mu_assert_int_eq(20, arr.length);
 613	mu_check(arr.capacity >= 20);
 614	mu_check(arr.capacity > 4);
 615
 616	for (int i = 0; i < 20; i++) {
 617		mu_assert_int_eq(i, arr.data[i]);
 618	}
 619
 620	array_free(arr);
 621}
 622
 623MU_TEST(test_array_reserve) {
 624	array(int) arr;
 625	array_init(arr);
 626
 627	array_reserve(arr, 100);
 628	mu_assert_int_eq(100, arr.capacity);
 629	mu_assert_int_eq(0, arr.length);
 630	mu_check(arr.data != NULL);
 631
 632	array_free(arr);
 633}
 634
 635MU_TEST(test_array_clear) {
 636	array(int) arr;
 637	array_init(arr);
 638	array_push(arr, 1);
 639	array_push(arr, 2);
 640
 641	size_t cap = arr.capacity;
 642	array_clear(arr);
 643
 644	mu_assert_int_eq(0, arr.length);
 645	mu_assert_int_eq(cap, arr.capacity); // Capacity stays
 646	mu_check(arr.data != NULL);
 647
 648	array_free(arr);
 649}
 650
 651MU_TEST(test_array_foreach) {
 652	array(int) arr;
 653	array_init(arr);
 654	array_push(arr, 1);
 655	array_push(arr, 2);
 656	array_push(arr, 3);
 657
 658	int sum = 0;
 659	int val;
 660	array_foreach(arr, val) { sum += val; }
 661
 662	mu_assert_int_eq(6, sum);
 663	array_free(arr);
 664}
 665
 666MU_TEST(test_array_foreach_idx) {
 667	array(int) arr;
 668	array_init(arr);
 669	array_push(arr, 10);
 670	array_push(arr, 20);
 671	array_push(arr, 30);
 672
 673	int sum_val = 0;
 674	size_t sum_idx = 0;
 675	int val;
 676
 677	array_foreach_idx(arr, val, i) {
 678		sum_val += val;
 679		sum_idx += i;
 680		mu_assert_int_eq(val, arr.data[i]);
 681	}
 682
 683	mu_assert_int_eq(60, sum_val);
 684	mu_assert_int_eq(3, sum_idx); // 0+1+2
 685	array_free(arr);
 686}
 687
 688// Slice Tests
 689SLICE_DEF(int);
 690
 691MU_TEST(test_slice_make) {
 692	int nums[] = {1, 2, 3, 4, 5};
 693	slice(int) s = make_slice(int, nums, 5);
 694
 695	mu_assert_int_eq(5, s.length);
 696	mu_check(s.data == nums);
 697	mu_assert_int_eq(1, s.data[0]);
 698	mu_assert_int_eq(5, s.data[4]);
 699}
 700
 701MU_TEST(test_slice_make_partial) {
 702	int nums[] = {10, 20, 30, 40, 50};
 703	slice(int) s = make_slice(int, nums + 1, 3); // 20, 30, 40
 704
 705	mu_assert_int_eq(3, s.length);
 706	mu_assert_int_eq(20, s.data[0]);
 707	mu_assert_int_eq(40, s.data[2]);
 708}
 709
 710MU_TEST(test_slice_from_dynamic_array) {
 711	array(int) arr;
 712	array_init(arr);
 713	array_push(arr, 100);
 714	array_push(arr, 200);
 715
 716	slice(int) s = array_as_slice(int, arr);
 717
 718	mu_assert_int_eq(2, s.length);
 719	mu_check(s.data == arr.data);
 720	mu_assert_int_eq(100, s.data[0]);
 721
 722	array_free(arr);
 723}
 724
 725MU_TEST(test_slice_modification) {
 726	int nums[] = {1, 1, 1};
 727	slice(int) s = make_slice(int, nums, 3);
 728
 729	s.data[1] = 99;
 730
 731	mu_assert_int_eq(99, nums[1]);
 732	mu_assert_int_eq(1, nums[0]);
 733}
 734
 735// Arena tests
 736MU_TEST(test_arena_basic) {
 737	Arena a = arena_make();
 738	void *p1 = arena_alloc(&a, 10);
 739	void *p2 = arena_alloc(&a, 20);
 740
 741	mu_check(p1 != NULL);
 742	mu_check(p2 != NULL);
 743	mu_check(p1 != p2);
 744
 745	// Basic usage check
 746	memset(p1, 1, 10);
 747	memset(p2, 2, 20);
 748
 749	arena_free(&a);
 750}
 751
 752MU_TEST(test_arena_growth) {
 753	Arena a = arena_make();
 754
 755	// Force growth by allocating more than default block size (4096)
 756	// or by allocating many small objects.
 757	// Let's alloc a big chunk first.
 758	void *big = arena_alloc(&a, 5000);
 759	mu_check(big != NULL);
 760	mu_check(a.blocks.length >= 1);
 761
 762	// Alloc another big chunk
 763	void *big2 = arena_alloc(&a, 5000);
 764	mu_check(big2 != NULL);
 765	mu_check(big2 != big);
 766	mu_check(a.blocks.length >= 2);
 767
 768	arena_free(&a);
 769}
 770
 771MU_TEST(test_arena_alignment) {
 772	Arena a = arena_make();
 773
 774	void *p1 = arena_alloc(&a, 1); // 1 byte
 775	void *p2 = arena_alloc(&a, 1); // 1 byte
 776
 777	uintptr_t addr1 = (uintptr_t)p1;
 778	uintptr_t addr2 = (uintptr_t)p2;
 779
 780	mu_check(addr1 % sizeof(void *) == 0);
 781	mu_check(addr2 % sizeof(void *) == 0);
 782	mu_check(addr2 - addr1 >= sizeof(void *));
 783
 784	arena_free(&a);
 785}
 786
 787MU_TEST(test_arena_safety) {
 788	Arena a = arena_make();
 789
 790	// Test 1: Integer overflow in allocation should be caught by safe_alloc
 791	// Requesting 2 items of size (SIZE_MAX/2 + 1) should fail
 792	// This tests ns_safe_malloc directly or via macro if we exposed it,
 793	// but arena_alloc calls ensure/grow which calls ALLOC(char, size).
 794	// To test ALLOC overflow: ALLOC(char, SIZE_MAX) is just 1*SIZE_MAX.
 795	// We need a way to trigger safe_alloc failure.
 796
 797	// Let's test the underlying safe allocator directly since we exposed it in nonstd.h
 798	void *p = safe_malloc(SIZE_MAX / 2 + 100, 2);
 799	mu_check(p == NULL);
 800
 801	// Test 2: Ensure normal allocations still work
 802	void *p2 = arena_alloc(&a, 100);
 803	mu_check(p2 != NULL);
 804
 805	arena_free(&a);
 806}
 807
 808// File I/O tests
 809MU_TEST(test_file_io_basic) {
 810	const char *filename = "test_io_basic.txt";
 811	const char *content = "Hello, file!";
 812	size_t len = strlen(content);
 813
 814	// Write
 815	mu_check(write_entire_file(filename, content, len));
 816
 817	// Read
 818	size_t read_len;
 819	char *read_content = read_entire_file(filename, &read_len);
 820	mu_check(read_content != NULL);
 821	mu_assert_int_eq(len, read_len);
 822	mu_check(strncmp(content, read_content, len) == 0);
 823
 824	FREE(read_content);
 825	remove(filename);
 826}
 827
 828MU_TEST(test_file_io_sb) {
 829	const char *filename = "test_io_sb.txt";
 830	stringb sb;
 831	sb_init(&sb, 0);
 832	sb_append_cstr(&sb, "Hello from sb!");
 833
 834	// Write
 835	mu_check(write_file_sb(filename, &sb));
 836
 837	// Read
 838	stringb read_sb = read_entire_file_sb(filename);
 839	mu_check(read_sb.data != NULL);
 840	mu_assert_int_eq(sb.length, read_sb.length);
 841	mu_check(strcmp(sb.data, read_sb.data) == 0);
 842
 843	sb_free(&sb);
 844	sb_free(&read_sb);
 845	remove(filename);
 846}
 847
 848MU_TEST(test_file_io_read_missing) {
 849	size_t len;
 850	char *content = read_entire_file("non_existent_file.txt", &len);
 851	mu_check(content == NULL);
 852}
 853
 854MU_TEST(test_file_io_read_missing_sb) {
 855	stringb sb = read_entire_file_sb("non_existent_file.txt");
 856	mu_check(sb.data == NULL);
 857	mu_assert_int_eq(0, sb.length);
 858}
 859
 860// Logging tests
 861MU_TEST(test_logging_level_filtering) {
 862	FILE *tmp = tmpfile();
 863	mu_check(tmp != NULL);
 864
 865	set_log_level(LOG_WARN);
 866
 867	log_message(tmp, LOG_INFO, "Info message");	  // Should not be logged
 868	log_message(tmp, LOG_ERROR, "Error message"); // Should be logged
 869
 870	rewind(tmp);
 871	char buffer[1024];
 872	size_t read = fread(buffer, 1, sizeof(buffer), tmp);
 873	buffer[read] = '\0';
 874
 875	mu_check(strstr(buffer, "Info message") == NULL);
 876	mu_check(strstr(buffer, "Error message") != NULL);
 877	mu_check(strstr(buffer, "[ERROR]") != NULL);
 878
 879	fclose(tmp);
 880}
 881
 882MU_TEST(test_logging_env_level) {
 883	// Enum: ERROR=0, WARN=1, INFO=2, DEBUG=3
 884	setenv("LOG_LEVEL", "0", 1); // ERROR
 885	mu_assert_int_eq(LOG_ERROR, get_log_level_from_env());
 886
 887	setenv("LOG_LEVEL", "3", 1); // DEBUG
 888	mu_assert_int_eq(LOG_DEBUG, get_log_level_from_env());
 889
 890	// Invalid level
 891	set_log_level(LOG_INFO); // Reset
 892	setenv("LOG_LEVEL", "99", 1);
 893	mu_assert_int_eq(LOG_INFO, get_log_level_from_env()); // Should return current max_level (defaults check)
 894
 895	unsetenv("LOG_LEVEL");
 896}
 897
 898MU_TEST(test_logging_format) {
 899	FILE *tmp = tmpfile();
 900	mu_check(tmp != NULL);
 901
 902	set_log_level(LOG_INFO);
 903	log_message(tmp, LOG_INFO, "Test %d %s", 123, "format");
 904
 905	rewind(tmp);
 906	char buffer[1024];
 907	size_t read = fread(buffer, 1, sizeof(buffer), tmp);
 908	buffer[read] = '\0';
 909
 910	mu_check(strstr(buffer, "Test 123 format") != NULL);
 911	mu_check(strstr(buffer, "[INFO ]") != NULL);
 912	// Check timestamp format roughly (YYYY-MM-DD)
 913	mu_check(strstr(buffer, "20") != NULL);
 914
 915	fclose(tmp);
 916}
 917
 918// Image tests
 919MU_TEST(test_ppm_init_free) {
 920	Canvas img = ppm_init(100, 100);
 921	mu_assert_int_eq(100, (int)img.width);
 922	mu_assert_int_eq(100, (int)img.height);
 923	mu_check(img.pixels != NULL);
 924	ppm_free(&img);
 925	mu_assert_int_eq(0, (int)img.width);
 926	mu_assert_int_eq(0, (int)img.height);
 927	mu_check(img.pixels == NULL);
 928}
 929
 930MU_TEST(test_ppm_set_get_pixel) {
 931	Canvas img = ppm_init(10, 10);
 932	Color c = {255, 128, 64};
 933	ppm_set_pixel(&img, 5, 5, c);
 934	Color got = ppm_get_pixel(&img, 5, 5);
 935	mu_assert_int_eq(255, got.r);
 936	mu_assert_int_eq(128, got.g);
 937	mu_assert_int_eq(64, got.b);
 938
 939	// Test out of bounds (should return black)
 940	got = ppm_get_pixel(&img, 100, 100);
 941	mu_assert_int_eq(0, got.r);
 942
 943	ppm_free(&img);
 944}
 945
 946MU_TEST(test_ppm_save_read) {
 947	Canvas img = ppm_init(10, 10);
 948	for (u32 y = 0; y < 10; ++y) {
 949		for (u32 x = 0; x < 10; ++x) {
 950			ppm_set_pixel(&img, x, y, (Color){(u8)(x * 20), (u8)(y * 20), 100});
 951		}
 952	}
 953
 954	const char *tmp_ppm = "test_image.ppm";
 955	mu_check(ppm_save(&img, tmp_ppm));
 956
 957	Canvas read = ppm_read(tmp_ppm);
 958	mu_assert_int_eq((int)img.width, (int)read.width);
 959	mu_assert_int_eq((int)img.height, (int)read.height);
 960	mu_check(read.pixels != NULL);
 961
 962	for (u32 i = 0; i < 100; ++i) {
 963		mu_assert_int_eq(img.pixels[i].r, read.pixels[i].r);
 964		mu_assert_int_eq(img.pixels[i].g, read.pixels[i].g);
 965		mu_assert_int_eq(img.pixels[i].b, read.pixels[i].b);
 966	}
 967
 968	ppm_free(&img);
 969	ppm_free(&read);
 970	remove(tmp_ppm);
 971}
 972
 973MU_TEST(test_ppm_draw_helpers) {
 974	Canvas img = ppm_init(100, 100);
 975
 976	// Test fill
 977	ppm_fill(&img, COLOR_RED);
 978	Color c1 = ppm_get_pixel(&img, 0, 0);
 979	mu_assert_int_eq(255, c1.r);
 980	mu_assert_int_eq(0, c1.g);
 981
 982	// Test rect
 983	ppm_fill(&img, COLOR_BLACK);
 984	ppm_draw_rect(&img, 10, 10, 20, 20, COLOR_WHITE);
 985	mu_assert_int_eq(255, ppm_get_pixel(&img, 10, 10).r);
 986	mu_assert_int_eq(0, ppm_get_pixel(&img, 15, 15).r); // Inside should be black
 987
 988	// Test line
 989	ppm_fill(&img, COLOR_BLACK);
 990	ppm_draw_line(&img, 0, 0, 10, 10, COLOR_GREEN);
 991	mu_assert_int_eq(255, ppm_get_pixel(&img, 5, 5).g);
 992
 993	// Test hexagon/color macros
 994	Color hex = COLOR_HEX(0x112233);
 995	mu_assert_int_eq(0x11, hex.r);
 996	mu_assert_int_eq(0x22, hex.g);
 997	mu_assert_int_eq(0x33, hex.b);
 998
 999	ppm_free(&img);
1000}
1001
1002// Test suites
1003MU_TEST_SUITE(test_suite_stringv) {
1004	printf("\n[String View Tests]\n");
1005	RUN_TEST_WITH_NAME(test_sv_from_cstr);
1006	RUN_TEST_WITH_NAME(test_sv_from_cstr_null);
1007	RUN_TEST_WITH_NAME(test_sv_from_parts);
1008	RUN_TEST_WITH_NAME(test_sv_from_cstr_literal);
1009	RUN_TEST_WITH_NAME(test_sv_slice_normal);
1010	RUN_TEST_WITH_NAME(test_sv_slice_middle);
1011	RUN_TEST_WITH_NAME(test_sv_slice_out_of_bounds);
1012	RUN_TEST_WITH_NAME(test_sv_slice_invalid_range);
1013	RUN_TEST_WITH_NAME(test_sv_equals_same);
1014	RUN_TEST_WITH_NAME(test_sv_equals_different);
1015	RUN_TEST_WITH_NAME(test_sv_equals_different_length);
1016	RUN_TEST_WITH_NAME(test_sv_equals_empty);
1017	RUN_TEST_WITH_NAME(test_sv_starts_with_true);
1018	RUN_TEST_WITH_NAME(test_sv_starts_with_false);
1019	RUN_TEST_WITH_NAME(test_sv_starts_with_empty_prefix);
1020	RUN_TEST_WITH_NAME(test_sv_starts_with_longer_prefix);
1021	RUN_TEST_WITH_NAME(test_sv_ends_with_true);
1022	RUN_TEST_WITH_NAME(test_sv_ends_with_false);
1023	RUN_TEST_WITH_NAME(test_sv_ends_with_empty_suffix);
1024	RUN_TEST_WITH_NAME(test_sv_ends_with_longer_suffix);
1025}
1026
1027MU_TEST_SUITE(test_suite_stringb) {
1028	printf("\n[String Builder Tests]\n");
1029	RUN_TEST_WITH_NAME(test_sb_init);
1030	RUN_TEST_WITH_NAME(test_sb_init_with_capacity);
1031	RUN_TEST_WITH_NAME(test_sb_append_cstr);
1032	RUN_TEST_WITH_NAME(test_sb_append_cstr_multiple);
1033	RUN_TEST_WITH_NAME(test_sb_append_cstr_null);
1034	RUN_TEST_WITH_NAME(test_sb_append_sv);
1035	RUN_TEST_WITH_NAME(test_sb_append_sv_slice);
1036	RUN_TEST_WITH_NAME(test_sb_append_sv_empty);
1037	RUN_TEST_WITH_NAME(test_sb_append_char);
1038	RUN_TEST_WITH_NAME(test_sb_mixed_append);
1039	RUN_TEST_WITH_NAME(test_sb_as_sv);
1040	RUN_TEST_WITH_NAME(test_sb_growth);
1041	RUN_TEST_WITH_NAME(test_sb_free);
1042	RUN_TEST_WITH_NAME(test_sb_sv_interop);
1043	RUN_TEST_WITH_NAME(test_sb_append_sv_from_sb);
1044}
1045
1046MU_TEST_SUITE(test_suite_macros) {
1047	printf("\n[Macro Tests]\n");
1048	RUN_TEST_WITH_NAME(test_countof);
1049	RUN_TEST_WITH_NAME(test_min_max_clamp);
1050	RUN_TEST_WITH_NAME(test_static_foreach_macro);
1051	RUN_TEST_WITH_NAME(test_static_foreach_with_strings);
1052	RUN_TEST_WITH_NAME(test_static_foreach_single_element);
1053	RUN_TEST_WITH_NAME(test_static_foreach_modify_counter);
1054	RUN_TEST_WITH_NAME(test_static_foreach_with_structs);
1055	RUN_TEST_WITH_NAME(test_alloc_and_free);
1056	RUN_TEST_WITH_NAME(test_realloc);
1057}
1058
1059MU_TEST_SUITE(test_suite_array) {
1060	printf("\n[Array Tests]\n");
1061	RUN_TEST_WITH_NAME(test_array_init);
1062	RUN_TEST_WITH_NAME(test_array_init_cap);
1063	RUN_TEST_WITH_NAME(test_array_push);
1064	RUN_TEST_WITH_NAME(test_array_pop);
1065	RUN_TEST_WITH_NAME(test_array_pop_empty);
1066	RUN_TEST_WITH_NAME(test_array_get_set);
1067	RUN_TEST_WITH_NAME(test_array_insert);
1068	RUN_TEST_WITH_NAME(test_array_remove);
1069	RUN_TEST_WITH_NAME(test_array_growth);
1070	RUN_TEST_WITH_NAME(test_array_reserve);
1071	RUN_TEST_WITH_NAME(test_array_clear);
1072	RUN_TEST_WITH_NAME(test_array_foreach);
1073	RUN_TEST_WITH_NAME(test_array_foreach_idx);
1074}
1075
1076MU_TEST_SUITE(test_suite_slice) {
1077	printf("\n[Slice Tests]\n");
1078	RUN_TEST_WITH_NAME(test_slice_make);
1079	RUN_TEST_WITH_NAME(test_slice_make_partial);
1080	RUN_TEST_WITH_NAME(test_slice_from_dynamic_array);
1081	RUN_TEST_WITH_NAME(test_slice_modification);
1082}
1083
1084MU_TEST_SUITE(test_suite_types) {
1085	printf("\n[Type Tests]\n");
1086	RUN_TEST_WITH_NAME(test_typedefs);
1087}
1088
1089MU_TEST_SUITE(test_suite_arena) {
1090	printf("\n[Arena Tests]\n");
1091	RUN_TEST_WITH_NAME(test_arena_basic);
1092	RUN_TEST_WITH_NAME(test_arena_growth);
1093	RUN_TEST_WITH_NAME(test_arena_alignment);
1094	RUN_TEST_WITH_NAME(test_arena_safety);
1095}
1096
1097MU_TEST_SUITE(test_suite_files) {
1098	printf("\n[File I/O Tests]\n");
1099	RUN_TEST_WITH_NAME(test_file_io_basic);
1100	RUN_TEST_WITH_NAME(test_file_io_sb);
1101	RUN_TEST_WITH_NAME(test_file_io_read_missing);
1102	RUN_TEST_WITH_NAME(test_file_io_read_missing_sb);
1103}
1104
1105MU_TEST_SUITE(test_suite_logging) {
1106	printf("\n[Logging Tests]\n");
1107	RUN_TEST_WITH_NAME(test_logging_level_filtering);
1108	RUN_TEST_WITH_NAME(test_logging_env_level);
1109	RUN_TEST_WITH_NAME(test_logging_format);
1110}
1111
1112MU_TEST_SUITE(test_suite_image) {
1113	printf("\n[Image Tests]\n");
1114	RUN_TEST_WITH_NAME(test_ppm_init_free);
1115	RUN_TEST_WITH_NAME(test_ppm_set_get_pixel);
1116	RUN_TEST_WITH_NAME(test_ppm_save_read);
1117	RUN_TEST_WITH_NAME(test_ppm_draw_helpers);
1118}
1119
1120int main(int argc, char *argv[]) {
1121	(void)argc;
1122	(void)argv;
1123
1124	MU_RUN_SUITE(test_suite_stringv);
1125	MU_RUN_SUITE(test_suite_stringb);
1126	MU_RUN_SUITE(test_suite_macros);
1127	MU_RUN_SUITE(test_suite_array);
1128	MU_RUN_SUITE(test_suite_slice);
1129	MU_RUN_SUITE(test_suite_types);
1130	MU_RUN_SUITE(test_suite_arena);
1131	MU_RUN_SUITE(test_suite_files);
1132	MU_RUN_SUITE(test_suite_logging);
1133	MU_RUN_SUITE(test_suite_image);
1134
1135	MU_REPORT();
1136
1137	return MU_EXIT_CODE;
1138}