#include "../src/tile_cache.h"
#include <cassert>
#include <cstdio>
static int tests_passed = 0;
#define TEST(name) \
do { \
printf(" %-40s", #name "..."); \
} while (0)
#define PASS() \
do { \
printf("PASS\n"); \
tests_passed++; \
} while (0)
void test_init() {
TEST(init);
TileCache cache;
cache.init(32, 32);
assert(cache.total_slots() == 1024);
assert(cache.occupied_count() == 0);
PASS();
}
void test_alloc_basic() {
TEST(alloc_basic);
TileCache cache;
cache.init(4, 4); // 16 slots
TileId tile = { 0, 5, 3, 0 };
AllocResult r = cache.alloc(tile, 1);
assert(r.success);
assert(!r.was_eviction);
assert(cache.occupied_count() == 1);
// Find it
AllocResult f = cache.find(tile);
assert(f.success);
assert(f.location.x == r.location.x);
assert(f.location.y == r.location.y);
PASS();
}
void test_alloc_duplicate() {
TEST(alloc_duplicate);
TileCache cache;
cache.init(4, 4);
TileId tile = { 0, 5, 3, 0 };
AllocResult r1 = cache.alloc(tile, 1);
assert(r1.success);
// Allocate same tile again — should return same slot, no eviction
AllocResult r2 = cache.alloc(tile, 2);
assert(r2.success);
assert(!r2.was_eviction);
assert(r2.location.x == r1.location.x);
assert(r2.location.y == r1.location.y);
assert(cache.occupied_count() == 1);
PASS();
}
void test_lru_eviction() {
TEST(lru_eviction);
TileCache cache;
cache.init(2, 2); // 4 slots
// Fill all 4 slots
for (int i = 0; i < 4; i++) {
TileId tile = { 0, i, 0, 0 };
AllocResult r = cache.alloc(tile, (uint64_t)(i + 1));
assert(r.success);
assert(!r.was_eviction);
}
assert(cache.occupied_count() == 4);
// Allocate a 5th tile — should evict the oldest (frame 1, tile_x=0)
TileId new_tile = { 0, 10, 0, 0 };
AllocResult r = cache.alloc(new_tile, 5);
assert(r.success);
assert(r.was_eviction);
assert(r.evicted_tile.tile_x == 0); // oldest tile
// Oldest should be gone
AllocResult gone = cache.find({ 0, 0, 0, 0 });
assert(!gone.success);
PASS();
}
void test_touch_updates_lru() {
TEST(touch_updates_lru);
TileCache cache;
cache.init(2, 2); // 4 slots
// Fill all 4 slots
for (int i = 0; i < 4; i++) {
TileId tile = { 0, i, 0, 0 };
cache.alloc(tile, (uint64_t)(i + 1));
}
// Touch the oldest tile (frame 1) at a newer frame
AllocResult found = cache.find({ 0, 0, 0, 0 });
assert(found.success);
cache.touch(found.location, 10);
// Now evict — should evict tile_x=1 (frame 2) instead of tile_x=0 (now frame 10)
TileId new_tile = { 0, 10, 0, 0 };
AllocResult r = cache.alloc(new_tile, 11);
assert(r.success);
assert(r.was_eviction);
assert(r.evicted_tile.tile_x == 1); // tile_x=1 is now oldest at frame 2
PASS();
}
void test_locked_slots() {
TEST(locked_slots);
TileCache cache;
cache.init(2, 2); // 4 slots
// Fill all 4 slots and lock ALL of them
PhysicalLocation locs[4];
for (int i = 0; i < 4; i++) {
TileId tile = { 0, i, 0, 0 };
AllocResult r = cache.alloc(tile, 1);
locs[i] = r.location;
cache.lock(r.location);
}
assert(cache.locked_count() == 4);
// Try to allocate — all locked, should fail
TileId new_tile = { 0, 10, 0, 0 };
AllocResult r = cache.alloc(new_tile, 2);
assert(!r.success);
// Unlock one, now allocation should succeed by evicting it
cache.unlock(locs[0]);
AllocResult r2 = cache.alloc(new_tile, 3);
assert(r2.success);
assert(r2.was_eviction);
assert(r2.evicted_tile.tile_x == 0);
PASS();
}
void test_unlock() {
TEST(unlock);
TileCache cache;
cache.init(2, 2);
TileId tile = { 0, 0, 0, 0 };
AllocResult r = cache.alloc(tile, 1);
cache.lock(r.location);
assert(cache.is_locked(r.location));
cache.unlock(r.location);
assert(!cache.is_locked(r.location));
PASS();
}
void test_free() {
TEST(free);
TileCache cache;
cache.init(4, 4);
TileId tile = { 0, 5, 3, 0 };
cache.alloc(tile, 1);
assert(cache.occupied_count() == 1);
cache.free(tile);
assert(cache.occupied_count() == 0);
assert(!cache.find(tile).success);
PASS();
}
void test_free_at() {
TEST(free_at);
TileCache cache;
cache.init(4, 4);
TileId tile = { 0, 5, 3, 0 };
AllocResult r = cache.alloc(tile, 1);
assert(cache.occupied_count() == 1);
cache.free_at(r.location);
assert(cache.occupied_count() == 0);
PASS();
}
void test_clear() {
TEST(clear);
TileCache cache;
cache.init(4, 4);
for (int i = 0; i < 10; i++) {
TileId tile = { 0, i, 0, 0 };
cache.alloc(tile, 1);
}
assert(cache.occupied_count() == 10);
cache.clear();
assert(cache.occupied_count() == 0);
PASS();
}
void test_many_allocs() {
TEST(many_allocs);
TileCache cache;
cache.init(16, 16); // 256 slots
// Fill all 256 slots
for (int i = 0; i < 256; i++) {
TileId tile = { 0, i % 128, i / 128, 0 };
AllocResult r = cache.alloc(tile, (uint64_t)i);
assert(r.success);
assert(!r.was_eviction);
}
assert(cache.occupied_count() == 256);
// 257th should evict
TileId extra = { 1, 0, 0, 0 };
AllocResult r = cache.alloc(extra, 256);
assert(r.success);
assert(r.was_eviction);
PASS();
}
int main() {
printf("=== Tile Cache Tests ===\n");
test_init();
test_alloc_basic();
test_alloc_duplicate();
test_lru_eviction();
test_touch_updates_lru();
test_locked_slots();
test_unlock();
test_free();
test_free_at();
test_clear();
test_many_allocs();
printf("\nAll %d tests passed.\n", tests_passed);
return 0;
}