diff --git a/src/ktest.c b/src/ktest.c new file mode 100644 index 0000000..eb40870 --- /dev/null +++ b/src/ktest.c @@ -0,0 +1,59 @@ +#include "ktest.h" +#include "console.h" + +/* Bracketed by the linker script (see linker.ld, .rodata output section): + the ".ktests" input section holds one const KtestEntry* per registered + test. Iterating [start, end) visits every test in link order. */ +extern const KtestEntry* const ktestArrayStart[]; +extern const KtestEntry* const ktestArrayEnd[]; + +bool ktestStrEq(const char* a, const char* b) +{ + if (a == nullptr || b == nullptr) + return a == b; + while (*a && (*a == *b)) { + a++; + b++; + } + return *a == *b; +} + +bool ktestCheck(KtestCtx* t, bool ok, const char* expr, const char* file, int line) +{ + t->checks++; + if (!ok) { + t->failures++; + kprintf(" FAIL %s:%d %s\n", file, line, expr); + } + return ok; +} + +uint64 ktestRunAll(void) +{ + uint64 totalTests = 0; + uint64 totalChecks = 0; + uint64 totalFailures = 0; + uint64 failedTests = 0; + + kprintf("\n==== ktest: running in-kernel self-tests ====\n"); + + for (const KtestEntry* const* p = ktestArrayStart; p < ktestArrayEnd; p++) { + const KtestEntry* e = *p; + KtestCtx t = { e->name, 0, 0 }; + e->fn(&t); + totalTests++; + totalChecks += t.checks; + totalFailures += t.failures; + if (t.failures) { + failedTests++; + kprintf(" [FAIL] %s (%lu/%lu checks failed)\n", e->name, t.failures, t.checks); + } else { + kprintf(" [ ok ] %s (%lu checks)\n", e->name, t.checks); + } + } + + kprintf("==== ktest done: %lu of %lu tests failed ====\n", failedTests, totalTests); + kprintf("KTEST SUMMARY: %lu tests, %lu checks, %lu failed\n", + totalTests, totalChecks, totalFailures); + return totalFailures; +} diff --git a/src/listTest.c b/src/listTest.c new file mode 100644 index 0000000..4b78a61 --- /dev/null +++ b/src/listTest.c @@ -0,0 +1,112 @@ +#include "ktest.h" +#include "list.h" + +typedef struct TestItem { + int value; + ListNode node; +} TestItem; + +KTEST(listEmptyInit) +{ + ListNode head; + listInit(&head); + KT_TRUE(t, listEmpty(&head)); + KT_EQ(t, listLen(&head), 0); + KT_NULL(t, listFront(&head)); + KT_NULL(t, listBack(&head)); +} + +KTEST(listPushBackOrder) +{ + ListNode head; + listInit(&head); + + TestItem items[4]; + for (int i = 0; i < 4; i++) { + items[i].value = i; + listPushBack(&head, &items[i].node); + } + + KT_FALSE(t, listEmpty(&head)); + KT_EQ(t, listLen(&head), 4); + + int expected = 0; + ListNode* cursor; + listForEach(cursor, &head) { + TestItem* item = listEntry(cursor, TestItem, node); + KT_EQ(t, item->value, expected); + expected++; + } + KT_EQ(t, expected, 4); + + TestItem* front = listEntry(listFront(&head), TestItem, node); + TestItem* back = listEntry(listBack(&head), TestItem, node); + KT_EQ(t, front->value, 0); + KT_EQ(t, back->value, 3); +} + +KTEST(listPushFrontOrder) +{ + ListNode head; + listInit(&head); + + TestItem items[3]; + for (int i = 0; i < 3; i++) { + items[i].value = i; + listPushFront(&head, &items[i].node); + } + /* pushing 0,1,2 to the front in order yields 2,1,0 */ + int expected[3] = { 2, 1, 0 }; + int idx = 0; + ListNode* cursor; + listForEach(cursor, &head) { + TestItem* item = listEntry(cursor, TestItem, node); + KT_EQ(t, item->value, expected[idx]); + idx++; + } +} + +KTEST(listRemoveMiddle) +{ + ListNode head; + listInit(&head); + + TestItem items[3]; + for (int i = 0; i < 3; i++) { + items[i].value = i; + listPushBack(&head, &items[i].node); + } + + listRemove(&items[1].node); + KT_EQ(t, listLen(&head), 2); + + int idx = 0; + int expected[2] = { 0, 2 }; + ListNode* cursor; + listForEach(cursor, &head) { + TestItem* item = listEntry(cursor, TestItem, node); + KT_EQ(t, item->value, expected[idx]); + idx++; + } + + /* a removed node becomes its own trivial list */ + KT_TRUE(t, listEmpty(&items[1].node)); +} + +KTEST(listRemoveAllEmptiesList) +{ + ListNode head; + listInit(&head); + + TestItem items[2]; + items[0].value = 10; + items[1].value = 20; + listPushBack(&head, &items[0].node); + listPushBack(&head, &items[1].node); + + listRemove(&items[0].node); + listRemove(&items[1].node); + + KT_TRUE(t, listEmpty(&head)); + KT_EQ(t, listLen(&head), 0); +}