#include #include #include namespace { struct probe_device { constexpr auto static id = kapi::devices::interface{"probe"}; virtual ~probe_device() = default; [[nodiscard]] auto virtual get_value() const noexcept -> int = 0; }; struct const_device { constexpr auto static id = kapi::devices::interface{"flip"}; virtual ~const_device() = default; [[nodiscard]] auto get_constant() const noexcept -> int { return 0; } }; struct unimplemented_device { constexpr auto static id = kapi::devices::interface{"unimplemented"}; virtual ~unimplemented_device() = default; }; struct test_device final : kapi::devices::device, probe_device { explicit test_device(int value, const_device & const_device) : device{"probeable"} , m_value{value} , m_const_device{&const_device} {} [[nodiscard]] constexpr auto get_value() const noexcept -> int override { return m_value; } auto query_interface(kapi::devices::interface interface) -> void * override { if (interface == probe_device::id) { return static_cast(this); } if (interface == const_device::id) { return m_const_device; } return kapi::devices::device::query_interface(interface); } private: int m_value; const_device * m_const_device; }; } // namespace SCENARIO("Publishing and finding a device", "[kapi][devices][interface_registry]") { GIVEN("An empty registry, a device, and an interface the device implements by inheritance") { auto registry = kapi::devices::interface_registry{}; auto free_standing_interface = const_device{}; auto device = kstd::make_shared(128, free_standing_interface); THEN("Publishing an interface without a name fails") { REQUIRE_FALSE(registry.publish(device, "")); } WHEN("publishing the interface for the device") { CHECK(device->is_a()); CHECK(device->is_a()); auto published = registry.publish(device, "probe0", device->as()); THEN("publishing is successful") { REQUIRE(published); } THEN("publishing the same device and interface with a different name fails") { REQUIRE_FALSE(registry.publish(device, "probe1", device->as())); } THEN("publishing a second interface for the same device succeeds") { REQUIRE(registry.publish(device, "probe0")); } THEN("publishing a free standing second interface for the same device succeeds") { REQUIRE(registry.publish(device, "probe0", &free_standing_interface)); } AND_WHEN("getting all devices implementing that interface") { auto probeable_devices = registry.all(probe_device::id); THEN("there is exactly one such device") { REQUIRE(probeable_devices.size() == 1); } THEN("the name of the device is 'probe0'") { REQUIRE(probeable_devices[0].name() == "probe0"); } THEN("the returned device pointer is valid") { REQUIRE(probeable_devices[0].device()); } THEN("the returned implementation equals the result of device::as") { REQUIRE(probeable_devices[0].implementation() == device->as()); } THEN("the interface provided function can be invoked") { auto implementation = probeable_devices[0].as(); REQUIRE(implementation->get_value() == device->get_value()); } } THEN("find finds the published capability by name") { REQUIRE(registry.resolve("probe0")); } THEN("resolve finds a capability the device implements but never published") { REQUIRE(registry.resolve("probe0")); } THEN("resolve for a capability the device implements nowhere at all does not find anything") { REQUIRE_FALSE(registry.resolve("probe0")); } THEN("find 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); REQUIRE_FALSE(registry.resolve("probe0")); } } WHEN("the device is destroyed without withdrawing it") { CHECK(registry.publish(device, "probe0", device->as())); device.reset(); THEN("querying all does no longer report it") { REQUIRE(registry.all(probe_device::id).empty()); } THEN("find() no longer finds it") { REQUIRE_FALSE(registry.resolve("probe0")); } } } GIVEN("no device has ever been published") { auto registry = kapi::devices::interface_registry{}; THEN("all() returns an empty vector") { REQUIRE(registry.all(probe_device::id).empty()); } } GIVEN("a null device") { auto registry = kapi::devices::interface_registry{}; auto device = kstd::shared_ptr{}; auto interface = const_device{}; THEN("publishing the interface for the device fails") { REQUIRE_FALSE(registry.publish(device, "probe0", &interface)); } } }