aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormanuel <manuel.bott@eps.ch>2026-10-29 09:24:10 +0000
committermanuel <manuel.bott@eps.ch>2026-10-31 09:55:53 +0000
commitc61a7a3f4cbd610db502b0e14a659adc9289bf23 (patch)
tree74f3a5fb2241eff5c030ce23354a164c3aa54bf3
parent2aab0ae5763a2ebf7bbb26e8381005c40cf4c3df (diff)
downloadkernel-c61a7a3f4cbd610db502b0e14a659adc9289bf23.tar.xz
kernel-c61a7a3f4cbd610db502b0e14a659adc9289bf23.zip
kernel: add Catch2 string makers for printable test output
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--kernel/CMakeLists.txt4
-rw-r--r--kernel/kernel/test_support/string_makers.hpp132
-rw-r--r--kernel/kernel/test_support/string_makers.tests.cpp61
3 files changed, 197 insertions, 0 deletions
diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt
index ca44e79b..fc25f1ca 100644
--- a/kernel/CMakeLists.txt
+++ b/kernel/CMakeLists.txt
@@ -193,6 +193,10 @@ if(BUILD_TESTING)
${KERNEL_TEST_SUPPORT_SOURCES}
)
+ target_compile_options("${TEST_TARGET}" PRIVATE
+ "SHELL:-include ${CMAKE_CURRENT_SOURCE_DIR}/kernel/test_support/string_makers.hpp"
+ )
+
target_compile_definitions("${TEST_TARGET}" PRIVATE
KERNEL_TEST_ASSETS_DIR="${CMAKE_CURRENT_SOURCE_DIR}/kernel/test_support/vfs/test_assets"
)
diff --git a/kernel/kernel/test_support/string_makers.hpp b/kernel/kernel/test_support/string_makers.hpp
new file mode 100644
index 00000000..98eed3e8
--- /dev/null
+++ b/kernel/kernel/test_support/string_makers.hpp
@@ -0,0 +1,132 @@
+#ifndef TEACHOS_KERNEL_TEST_SUPPORT_STRING_MAKERS_HPP
+#define TEACHOS_KERNEL_TEST_SUPPORT_STRING_MAKERS_HPP
+
+//! Catch2 string conversions for kapi and kernel types.
+//!
+//! This header is force-included into every kernel test translation unit by the build system and must therefore never
+//! be included by kernel code. See kstd/test_support/string_makers.hpp for the conversions of kstd types.
+
+#include <kstd/test_support/string_makers.hpp> // IWYU pragma: export
+
+#include <kapi/devices.hpp>
+#include <kapi/filesystem.hpp>
+#include <kapi/memory.hpp>
+
+#include <catch2/catch_tostring.hpp>
+
+#include <string>
+
+namespace Catch
+{
+
+ template<>
+ struct StringMaker<kapi::filesystem::device_number>
+ {
+ static auto convert(kapi::filesystem::device_number number) -> std::string
+ {
+ return std::to_string(number.major) + ':' + std::to_string(number.minor);
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::filesystem::directory_cursor>
+ {
+ static auto convert(kapi::filesystem::directory_cursor cursor) -> std::string
+ {
+ return "directory_cursor{" + std::to_string(cursor.value) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::filesystem::file_status>
+ {
+ static auto convert(kapi::filesystem::file_status const & status) -> std::string
+ {
+ return "file_status{mode: " + Catch::Detail::stringify(status.mode) +
+ ", size: " + Catch::Detail::stringify(status.size) +
+ ", raw_device: " + Catch::Detail::stringify(status.raw_device) +
+ ", inode_number: " + Catch::Detail::stringify(status.inode_number) +
+ ", link_count: " + Catch::Detail::stringify(status.link_count) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::memory::frame>
+ {
+ static auto convert(kapi::memory::frame frame) -> std::string
+ {
+ return "frame{" + std::to_string(frame.number()) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::memory::page>
+ {
+ static auto convert(kapi::memory::page page) -> std::string
+ {
+ return "page{" + std::to_string(page.number()) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::devices::mmio_range>
+ {
+ static auto convert(kapi::devices::mmio_range const & range) -> std::string
+ {
+ return "mmio{" + Catch::Detail::stringify(range.start) + ", " + Catch::Detail::stringify(range.size) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::devices::io_port>
+ {
+ static auto convert(kapi::devices::io_port const & port) -> std::string
+ {
+ return "port{" + std::to_string(port.start) + ", " + std::to_string(port.count) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::devices::interrupt_line>
+ {
+ static auto convert(kapi::devices::interrupt_line const & line) -> std::string
+ {
+ return "irq{" + std::to_string(line.number) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::devices::dma_channel>
+ {
+ static auto convert(kapi::devices::dma_channel const & channel) -> std::string
+ {
+ return "dma{" + std::to_string(channel.number) + '}';
+ }
+ };
+
+ template<>
+ struct StringMaker<kapi::devices::resource>
+ {
+ static auto convert(kapi::devices::resource const & resource) -> std::string
+ {
+ using kapi::devices::resource_type;
+
+ switch (resource.type)
+ {
+ case resource_type::mmio:
+ return Catch::Detail::stringify(resource.get<resource_type::mmio>());
+ case resource_type::port:
+ return Catch::Detail::stringify(resource.get<resource_type::port>());
+ case resource_type::irq:
+ return Catch::Detail::stringify(resource.get<resource_type::irq>());
+ case resource_type::dma:
+ return Catch::Detail::stringify(resource.get<resource_type::dma>());
+ }
+
+ return "resource{<unknown>}";
+ }
+ };
+
+} // namespace Catch
+
+#endif
diff --git a/kernel/kernel/test_support/string_makers.tests.cpp b/kernel/kernel/test_support/string_makers.tests.cpp
new file mode 100644
index 00000000..1bcb332f
--- /dev/null
+++ b/kernel/kernel/test_support/string_makers.tests.cpp
@@ -0,0 +1,61 @@
+#include <kernel/test_support/string_makers.hpp>
+
+#include <kapi/devices.hpp>
+#include <kapi/filesystem.hpp>
+#include <kapi/memory.hpp>
+
+#include <kstd/units.hpp>
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_tostring.hpp>
+
+using namespace kstd::units_literals;
+
+SCENARIO("Catch2 string conversion of kapi types", "[test_support][string_makers]")
+{
+ GIVEN("filesystem types")
+ {
+ auto const number = kapi::filesystem::device_number{.major = 1, .minor = 2};
+ auto const cursor = kapi::filesystem::directory_cursor{3};
+ auto const status = kapi::filesystem::file_status{
+ .mode = 4, .size = 5_B, .raw_device = number, .inode_number = 6, .link_count = 7};
+
+ THEN("they are printed with their fields")
+ {
+ REQUIRE(Catch::Detail::stringify(number) == "1:2");
+ REQUIRE(Catch::Detail::stringify(cursor) == "directory_cursor{3}");
+ REQUIRE(Catch::Detail::stringify(status) ==
+ "file_status{mode: 4, size: 5, raw_device: 1:2, inode_number: 6, link_count: 7}");
+ }
+ }
+
+ GIVEN("memory chunks")
+ {
+ THEN("they are printed with their number")
+ {
+ REQUIRE(Catch::Detail::stringify(kapi::memory::frame{8}) == "frame{8}");
+ REQUIRE(Catch::Detail::stringify(kapi::memory::page{9}) == "page{9}");
+ }
+ }
+
+ GIVEN("device resources")
+ {
+ auto const port = kapi::devices::io_port{.start = 0x60, .count = 2};
+ auto const line = kapi::devices::interrupt_line{.number = 1};
+ auto const channel = kapi::devices::dma_channel{.number = 3};
+
+ THEN("they are printed with their kind and fields")
+ {
+ REQUIRE(Catch::Detail::stringify(port) == "port{96, 2}");
+ REQUIRE(Catch::Detail::stringify(line) == "irq{1}");
+ REQUIRE(Catch::Detail::stringify(channel) == "dma{3}");
+ }
+
+ THEN("a resource is printed like the value it holds")
+ {
+ REQUIRE(Catch::Detail::stringify(kapi::devices::resource{port}) == "port{96, 2}");
+ REQUIRE(Catch::Detail::stringify(kapi::devices::resource{line}) == "irq{1}");
+ REQUIRE(Catch::Detail::stringify(kapi::devices::resource{channel}) == "dma{3}");
+ }
+ }
+}