From 9c6f14583169100b7cd70346c7dc3d13d82806aa Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Sat, 25 Jul 2026 11:25:59 +0200 Subject: kapi: implement interface registry notifications --- kernel/kapi/devices/interface_registry.tests.cpp | 131 ++++++++++++++++++++++- 1 file changed, 127 insertions(+), 4 deletions(-) (limited to 'kernel/kapi/devices/interface_registry.tests.cpp') diff --git a/kernel/kapi/devices/interface_registry.tests.cpp b/kernel/kapi/devices/interface_registry.tests.cpp index 4d2e2097..071b9a6d 100644 --- a/kernel/kapi/devices/interface_registry.tests.cpp +++ b/kernel/kapi/devices/interface_registry.tests.cpp @@ -4,6 +4,8 @@ #include +#include + namespace { struct probe_device @@ -66,6 +68,44 @@ namespace const_device * m_const_device; }; + struct counting_observer final : kapi::devices::interface_registry_observer + { + auto on_interface_published(kapi::devices::interface_id, kapi::devices::interface_registry::entry const &) + -> void override + { + ++published; + } + + auto on_interface_withdrawn(kapi::devices::interface_id, kapi::devices::device &) -> void override + { + ++withdrawn; + } + + std::size_t published{}; + std::size_t withdrawn{}; + }; + + struct evil_observer final : kapi::devices::interface_registry_observer + { + explicit evil_observer(kstd::shared_ptr & victim) + : m_victim{victim} + {} + + auto on_interface_published(kapi::devices::interface_id, kapi::devices::interface_registry::entry const &) + -> void override + { + m_victim.reset(); + } + + auto on_interface_withdrawn(kapi::devices::interface_id, kapi::devices::device &) -> void override + { + m_victim.reset(); + } + + private: + kstd::shared_ptr & m_victim; // NOLINT + }; + } // namespace SCENARIO("Publishing and finding a device", "[kapi][devices][interface_registry]") @@ -138,7 +178,7 @@ SCENARIO("Publishing and finding a device", "[kapi][devices][interface_registry] } } - THEN("find finds the published capability by name") + THEN("resolve finds the published capability by name") { REQUIRE(registry.resolve("probe0")); } @@ -153,14 +193,14 @@ SCENARIO("Publishing and finding a device", "[kapi][devices][interface_registry] REQUIRE_FALSE(registry.resolve("probe0")); } - THEN("find for an unpublished device does not find a device") + THEN("resolve for an unpublished device does not find a device") { REQUIRE_FALSE(registry.resolve("probe1")); } THEN("withdrawing an interface for a device removes it from the registry") { - registry.unpublish(*device, probe_device::id); + registry.withdraw(*device, probe_device::id); REQUIRE_FALSE(registry.resolve("probe0")); } } @@ -175,7 +215,7 @@ SCENARIO("Publishing and finding a device", "[kapi][devices][interface_registry] REQUIRE(registry.all(probe_device::id).empty()); } - THEN("find() no longer finds it") + THEN("resolve() no longer finds it") { REQUIRE_FALSE(registry.resolve("probe0")); } @@ -204,3 +244,86 @@ SCENARIO("Publishing and finding a device", "[kapi][devices][interface_registry] } } } + +SCENARIO("Interface registry notifies subscribers", "[kapi][devices][interface_registry]") +{ + GIVEN("a device and a subscribed observer") + { + auto registry = kapi::devices::interface_registry{}; + auto free_standing_interface = const_device{}; + auto device = kstd::make_shared(128, free_standing_interface); + auto observer = kstd::make_shared(); + + registry.subscribe(observer); + + WHEN("an interface is published for the device") + { + CHECK(registry.publish(device, "probe0", device->as())); + + THEN("the observer is notified exactly once") + { + REQUIRE(observer->published == 1); + REQUIRE(observer->withdrawn == 0); + } + } + + WHEN("an interface is published and then withdrawn") + { + CHECK(registry.publish(device, "probe0", device->as())); + registry.withdraw(*device, probe_device::id); + + THEN("the observer is notified of both exactly one") + { + REQUIRE(observer->published == 1); + REQUIRE(observer->withdrawn == 1); + } + } + + WHEN("the observer is destroyed before anything is published") + { + observer.reset(); + + THEN("publishing afterward does not crash") + { + REQUIRE_NOTHROW(registry.publish(device, "probe0", device->as())); + } + } + + WHEN("the observer unsubscribes before anything is published") + { + registry.unsubscribe(*observer); + + THEN("publishing afterward does not crash") + { + REQUIRE_NOTHROW(registry.publish(device, "probe0", device->as())); + } + + THEN("the subscriber is not notified") + { + REQUIRE_NOTHROW(registry.publish(device, "probe0", device->as())); + REQUIRE_NOTHROW(registry.withdraw(*device, probe_device::id)); + REQUIRE(observer->published == 0); + REQUIRE(observer->withdrawn == 0); + } + } + } + + GIVEN("two subscriber, the first destroying the second") + { + auto registry = kapi::devices::interface_registry{}; + auto free_standing_interface = const_device{}; + auto device = kstd::make_shared(128, free_standing_interface); + + auto second = kstd::make_shared(); + auto first = kstd::make_shared(second); + + registry.subscribe(first); + registry.subscribe(second); + + THEN("publishing an interface does not crash") + { + REQUIRE_NOTHROW(registry.publish(device, "probe0", device->as())); + REQUIRE(second == nullptr); + } + } +} \ No newline at end of file -- cgit v1.2.3