From e7a7c314c94cc390110b450541ccacd8a55e0500 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Sat, 2 May 2026 23:37:39 +0800 Subject: feat: add klog self-test suite with various test cases --- na/test/test_klog.cc | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 na/test/test_klog.cc diff --git a/na/test/test_klog.cc b/na/test/test_klog.cc new file mode 100644 index 0000000..896e7ed --- /dev/null +++ b/na/test/test_klog.cc @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2026 Richard Qin */ + +/* + * Boot Self-Test: klog + * + * Tests: + * 1. read vs read_consume semantics + * 2. subsystem mask filtering + * 3. ratelimit bookkeeping + * 4. flush advances console tail when console filtering is active + * 5. seq counter increments on writes + */ + +#include +#include +#include + +static void drain_klog() +{ + static char buf[512]; + while (true) { + int n = klog_read_consume(buf, sizeof(buf)); + if (n <= 0) { + break; + } + } +} + +static bool test_read_peek_and_consume() +{ + drain_klog(); + + klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_test_a"); + klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_test_b"); + + KlogStats s0; + klog_stats(&s0); + + char buf[1024]; + int n = klog_read(buf, sizeof(buf) - 1); + if (n <= 0) { + return false; + } + buf[n] = '\0'; + if (!strstr(buf, "klog_test_a") || !strstr(buf, "klog_test_b")) { + return false; + } + + KlogStats s1; + klog_stats(&s1); + if (s1.tail != s0.tail) { + return false; + } + + int n2 = klog_read_consume(buf, sizeof(buf) - 1); + if (n2 <= 0) { + return false; + } + buf[n2] = '\0'; + if (!strstr(buf, "klog_test_a") || !strstr(buf, "klog_test_b")) { + return false; + } + + KlogStats s2; + klog_stats(&s2); + return s2.tail != s0.tail; +} + +static bool test_subsys_mask() +{ + drain_klog(); + + const uint64 all_mask = (1ULL << KLOG_SUBSYS_MAX) - 1; + klog_set_subsys_mask(1ULL << KLOG_SUBSYS_TEST); + + klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_subsys_ok"); + klog_ex(KLOG_INFO, KLOG_SUBSYS_GENERIC, "klog_subsys_drop"); + + char buf[1024]; + int n = klog_read_consume(buf, sizeof(buf) - 1); + klog_set_subsys_mask(all_mask); + + if (n <= 0) { + return false; + } + buf[n] = '\0'; + return strstr(buf, "klog_subsys_ok") && !strstr(buf, "klog_subsys_drop"); +} + +static bool test_ratelimit() +{ + KlogRateLimit rl = {}; + rl.interval = 100; + rl.burst = 2; + + bool a = klog_ratelimit(&rl, 10); + bool b = klog_ratelimit(&rl, 20); + bool c = klog_ratelimit(&rl, 30); + + if (!a || !b || c) { + return false; + } + if (rl.printed != 2 || rl.suppressed != 1) { + return false; + } + + bool d = klog_ratelimit(&rl, 200); + if (!d) { + return false; + } + return rl.printed == 1 && rl.suppressed == 0; +} + +static bool test_flush_advances_console_tail() +{ + drain_klog(); + + klog_set_level(KLOG_ERR); + klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_flush_msg"); + + KlogStats s1; + klog_stats(&s1); + if (s1.console_tail == s1.head) { + klog_set_level(static_cast(KLOG_DEFAULT_LEVEL)); + return false; + } + + klog_flush(); + + KlogStats s2; + klog_stats(&s2); + + klog_set_level(static_cast(KLOG_DEFAULT_LEVEL)); + return s2.console_tail == s2.head; +} + +static bool test_seq_increments() +{ + drain_klog(); + + KlogStats s0; + klog_stats(&s0); + + klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_seq_1"); + klog_ex(KLOG_INFO, KLOG_SUBSYS_TEST, "klog_seq_2"); + + KlogStats s1; + klog_stats(&s1); + + return s1.seq >= s0.seq + 2; +} + +void selftest_klog() +{ + st_begin("klog: read vs read_consume"); + ST_ASSERT(test_read_peek_and_consume()); + st_pass(); + + st_begin("klog: subsys mask filters"); + ST_ASSERT(test_subsys_mask()); + st_pass(); + + st_begin("klog: ratelimit bookkeeping"); + ST_ASSERT(test_ratelimit()); + st_pass(); + + st_begin("klog: flush advances console tail"); + ST_ASSERT(test_flush_advances_console_tail()); + st_pass(); + + st_begin("klog: seq increments on write"); + ST_ASSERT(test_seq_increments()); + st_pass(); +} -- cgit v1.2.3