OverviewFilesCommitsIssuesDocsReleasesPeers

blender-bridge / test / test_tile_cache.cpp

246 lines · cpp ref:
#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;
}