#include "../src/tile_request_queue.h"
#include <cassert>
#include <cstdio>
#include <cstring>
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_pack_unpack() {
TEST(pack_unpack);
uint32_t packed = pack_feedback(42, 100, 200, 7);
int tex_id, tile_x, tile_y, mip;
unpack_feedback(packed, tex_id, tile_x, tile_y, mip);
assert(tex_id == 42);
assert(tile_x == 100);
assert(tile_y == 200);
assert(mip == 7);
// Edge cases
packed = pack_feedback(4095, 255, 255, 15);
unpack_feedback(packed, tex_id, tile_x, tile_y, mip);
assert(tex_id == 4095);
assert(tile_x == 255);
assert(tile_y == 255);
assert(mip == 15);
// Zero = no request (all fields zero packs to 0)
assert(pack_feedback(0, 0, 0, 0) == 0);
PASS();
}
void test_add_from_feedback() {
TEST(add_from_feedback);
TileRequestQueue queue;
uint32_t feedback[] = {
pack_feedback(1, 10, 20, 0),
pack_feedback(1, 10, 20, 0), // duplicate
pack_feedback(1, 11, 20, 0), // different tile
0, // skip empty
pack_feedback(2, 5, 5, 1),
};
queue.add_from_feedback(feedback, 5);
// Should have 3 unique requests (deduped the duplicate)
assert(queue.pending_count() == 3);
PASS();
}
void test_dedup_coverage() {
TEST(dedup_coverage);
TileRequestQueue queue;
queue.set_coverage_threshold(3);
// Same tile appears 5 times → high coverage
uint32_t feedback[5];
for (int i = 0; i < 5; i++) {
feedback[i] = pack_feedback(1, 10, 20, 0);
}
queue.add_from_feedback(feedback, 5);
auto batch = queue.get_next_batch(10);
assert(batch.size() == 1);
assert(batch[0].pixel_coverage == 5);
assert(batch[0].priority == PRIORITY_HIGH_COVERAGE);
PASS();
}
void test_invalidated_priority() {
TEST(invalidated_priority);
TileRequestQueue queue;
// Add feedback requests
uint32_t feedback[] = {
pack_feedback(1, 10, 20, 0),
pack_feedback(1, 10, 20, 0),
pack_feedback(1, 10, 20, 0),
pack_feedback(1, 10, 20, 0),
pack_feedback(1, 10, 20, 0),
pack_feedback(1, 11, 20, 0),
};
queue.add_from_feedback(feedback, 6);
// Invalidate a different tile
queue.add_invalidated(2, 0, 0, 0);
auto batch = queue.get_next_batch(10);
assert(batch.size() == 3);
// Invalidated should be first
assert(batch[0].priority == PRIORITY_INVALIDATED);
assert(batch[0].tile.texture_id == 2);
PASS();
}
void test_priority_sort_order() {
TEST(priority_sort_order);
TileRequestQueue queue;
queue.set_coverage_threshold(3);
// Tile A: 1 pixel (low coverage)
uint32_t fb_a[] = { pack_feedback(1, 0, 0, 0) };
queue.add_from_feedback(fb_a, 1);
// Tile B: 10 pixels (high coverage)
uint32_t fb_b[10];
for (int i = 0; i < 10; i++)
fb_b[i] = pack_feedback(1, 1, 0, 0);
queue.add_from_feedback(fb_b, 10);
// Tile C: invalidated
queue.add_invalidated(1, 2, 0, 0);
auto batch = queue.get_next_batch(10);
assert(batch.size() == 3);
// Order: invalidated (C) > high coverage (B) > low coverage (A)
assert(batch[0].tile.tile_x == 2); // C - invalidated
assert(batch[1].tile.tile_x == 1); // B - high coverage (10 pixels)
assert(batch[2].tile.tile_x == 0); // A - low coverage (1 pixel)
PASS();
}
void test_budget_throttle() {
TEST(budget_throttle);
TileRequestQueue queue;
// Add 20 requests (tex_id=1 to avoid zero-packed being skipped)
for (int i = 0; i < 20; i++) {
uint32_t fb[] = { pack_feedback(1, i, 0, 0) };
queue.add_from_feedback(fb, 1);
}
assert(queue.pending_count() == 20);
// Budget of 5 — should only return 5
auto batch = queue.get_next_batch(5);
assert((int)batch.size() == 5);
// Queue should be cleared after get_next_batch
assert(queue.pending_count() == 0);
PASS();
}
void test_invalidated_upgrades_existing() {
TEST(invalidated_upgrades_existing);
TileRequestQueue queue;
// Add from feedback first
uint32_t fb[] = { pack_feedback(1, 10, 20, 0) };
queue.add_from_feedback(fb, 1);
// Now invalidate the same tile — should upgrade priority
queue.add_invalidated(1, 10, 20, 0);
auto batch = queue.get_next_batch(10);
assert(batch.size() == 1);
assert(batch[0].priority == PRIORITY_INVALIDATED);
assert(batch[0].pixel_coverage == 1); // coverage preserved
PASS();
}
void test_empty_feedback() {
TEST(empty_feedback);
TileRequestQueue queue;
// All zeros — nothing added
uint32_t fb[10] = {};
queue.add_from_feedback(fb, 10);
assert(queue.pending_count() == 0);
auto batch = queue.get_next_batch(10);
assert(batch.empty());
PASS();
}
void test_clear() {
TEST(clear);
TileRequestQueue queue;
uint32_t fb[] = { pack_feedback(1, 0, 0, 0), pack_feedback(2, 0, 0, 0) };
queue.add_from_feedback(fb, 2);
assert(queue.pending_count() == 2);
queue.clear();
assert(queue.pending_count() == 0);
PASS();
}
void test_high_coverage_sorts_within_priority() {
TEST(high_coverage_within_priority);
TileRequestQueue queue;
queue.set_coverage_threshold(2);
// Tile A: 5 pixels (tex_id=1 to avoid zero packing)
for (int i = 0; i < 5; i++) {
uint32_t fb[] = { pack_feedback(1, 0, 0, 0) };
queue.add_from_feedback(fb, 1);
}
// Tile B: 10 pixels
for (int i = 0; i < 10; i++) {
uint32_t fb[] = { pack_feedback(1, 1, 0, 0) };
queue.add_from_feedback(fb, 1);
}
auto batch = queue.get_next_batch(10);
assert(batch.size() == 2);
// Both are HIGH priority, but B has more coverage → B first
assert(batch[0].tile.tile_x == 1);
assert(batch[0].pixel_coverage == 10);
assert(batch[1].tile.tile_x == 0);
assert(batch[1].pixel_coverage == 5);
PASS();
}
int main() {
printf("=== Tile Request Queue Tests ===\n");
test_pack_unpack();
test_add_from_feedback();
test_dedup_coverage();
test_invalidated_priority();
test_priority_sort_order();
test_budget_throttle();
test_invalidated_upgrades_existing();
test_empty_feedback();
test_clear();
test_high_coverage_sorts_within_priority();
printf("\nAll %d tests passed.\n", tests_passed);
return 0;
}