test: cover shuffle

This commit is contained in:
dualfroz committed 2026-09-10 21:07:18 +02:00
1 parent bac6a25d5d
commit 0e3cd5ed71
1 file changed
+182
+182
View File
@@ -0,0 +1,182 @@
#include "ktest.h"
#include "prioq.h"
#include "heap.h"
/* Fisher-Yates over a small LCG so insertion order is scrambled but
reproducible across runs (same generator as rbtTest.c's shuffle). */
static void shuffle(uint32* arr, int32 n, uint32 seed)
{
uint32 state = seed;
for (int32 i = n - 1; i > 0; i--) {
state = state * 1664525u + 1013904223u;
int32 j = (int32)(state % (uint32)(i + 1));
uint32 tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
KTEST(prioqEmptyQueue)
{
PrioQueue q;
prioqInit(&q);
KT_TRUE(t, prioqIsEmpty(&q));
KT_EQ(t, prioqCount(&q), 0);
KT_NULL(t, prioqPeekMin(&q));
KT_NULL(t, prioqPopMin(&q));
}
KTEST(prioqOrdersByVruntimeAscending)
{
PrioQueue q;
prioqInit(&q);
PrioQNode nodes[5];
uint64 vruntimes[] = { 300, 100, 500, 200, 400 };
for (uint32 i = 0; i < 5; i++)
prioqInsert(&q, &nodes[i], vruntimes[i], i);
KT_EQ(t, prioqCount(&q), 5);
uint64 expected[] = { 100, 200, 300, 400, 500 };
for (uint32 i = 0; i < 5; i++) {
PrioQNode* min = prioqPopMin(&q);
KT_NOTNULL(t, min);
if (min)
KT_EQU(t, min->vruntime, expected[i]);
}
KT_TRUE(t, prioqIsEmpty(&q));
}
KTEST(prioqPeekDoesNotRemove)
{
PrioQueue q;
prioqInit(&q);
PrioQNode a, b;
prioqInsert(&q, &a, 50, 1);
prioqInsert(&q, &b, 10, 2);
PrioQNode* peeked = prioqPeekMin(&q);
KT_NOTNULL(t, peeked);
if (peeked)
KT_EQ(t, peeked->id, 2);
KT_EQ(t, prioqCount(&q), 2);
PrioQNode* popped = prioqPopMin(&q);
KT_EQP(t, popped, peeked);
KT_EQ(t, prioqCount(&q), 1);
}
KTEST(prioqTiesBreakByIdAscending)
{
PrioQueue q;
prioqInit(&q);
PrioQNode nodes[4];
uint32 ids[] = { 7, 3, 9, 1 };
for (uint32 i = 0; i < 4; i++)
prioqInsert(&q, &nodes[i], 1000, ids[i]); /* identical vruntime */
uint32 expected[] = { 1, 3, 7, 9 };
for (uint32 i = 0; i < 4; i++) {
PrioQNode* min = prioqPopMin(&q);
KT_NOTNULL(t, min);
if (min)
KT_EQ(t, min->id, expected[i]);
}
}
KTEST(prioqRemoveArbitraryNodePreservesOrder)
{
PrioQueue q;
prioqInit(&q);
PrioQNode nodes[4];
uint64 vruntimes[] = { 10, 20, 30, 40 };
for (uint32 i = 0; i < 4; i++)
prioqInsert(&q, &nodes[i], vruntimes[i], i);
prioqRemove(&q, &nodes[1]); /* drop vruntime=20 before it is ever popped */
KT_EQ(t, prioqCount(&q), 3);
uint64 expected[] = { 10, 30, 40 };
for (uint32 i = 0; i < 3; i++) {
PrioQNode* min = prioqPopMin(&q);
KT_NOTNULL(t, min);
if (min)
KT_EQU(t, min->vruntime, expected[i]);
}
KT_TRUE(t, prioqIsEmpty(&q));
}
KTEST(prioqNodeStorageIsReusableAcrossQueueLifetimes)
{
PrioQNode node;
PrioQueue first;
prioqInit(&first);
prioqInsert(&first, &node, 500, 42);
PrioQNode* popped = prioqPopMin(&first);
KT_EQP(t, popped, &node);
/* Same PrioQNode storage, brand new queue: rbtInsert must not be
tripped up by stale left/right/parent pointers left over from the
first tree. */
PrioQueue second;
prioqInit(&second);
prioqInsert(&second, &node, 5, 1);
KT_EQ(t, prioqCount(&second), 1);
PrioQNode* min = prioqPeekMin(&second);
KT_NOTNULL(t, min);
if (min)
KT_EQU(t, min->vruntime, 5);
}
#define prioqStressN 300
KTEST(prioqStressDrainsInFullyAscendingOrder)
{
PrioQNode* pool = (PrioQNode*)kmalloc(sizeof(PrioQNode) * prioqStressN);
uint32* order = (uint32*)kmalloc(sizeof(uint32) * prioqStressN);
KT_NOTNULL(t, pool);
KT_NOTNULL(t, order);
if (!pool || !order) {
kfree(pool);
kfree(order);
return;
}
for (uint32 i = 0; i < prioqStressN; i++)
order[i] = i;
shuffle(order, prioqStressN, 0xFEEDFACEu);
PrioQueue q;
prioqInit(&q);
for (uint32 i = 0; i < prioqStressN; i++) {
uint32 id = order[i];
/* vruntime = id * 3 keeps a strict, checkable total order while
still exercising ids that arrive in scrambled sequence. */
prioqInsert(&q, &pool[id], (uint64)id * 3, id);
}
KT_EQ(t, prioqCount(&q), prioqStressN);
uint64 lastVruntime = 0;
uint32 visited = 0;
for (;;) {
PrioQNode* min = prioqPopMin(&q);
if (!min)
break;
if (visited > 0)
KT_TRUE(t, min->vruntime >= lastVruntime);
lastVruntime = min->vruntime;
KT_EQU(t, min->vruntime, (uint64)min->id * 3);
visited++;
}
KT_EQ(t, visited, prioqStressN);
KT_TRUE(t, prioqIsEmpty(&q));
kfree(pool);
kfree(order);
}