#include #include #include #include namespace { struct test_bus final : kapi::devices::bus { using kapi::devices::bus::bus; }; struct test_device final : kapi::devices::device { using kapi::devices::device::device; auto init() -> bool override { return true; } }; struct flagging_device final : kapi::devices::device { flagging_device(kstd::string const & name, bool & destroyed_flag) : device{name} , m_destroyed_flag{&destroyed_flag} {} ~flagging_device() override { *m_destroyed_flag = true; } auto init() -> bool override { return true; } private: bool * m_destroyed_flag; }; } // namespace SCENARIO("A bus shares ownership of its children, holding a weak link to its parent", "[devices][bus]") { GIVEN("A bus with one attached child device") { auto parent_bus = kstd::make_shared("bus_ownership_parent"); auto child = kstd::make_shared("bus_ownership_child"); parent_bus->add_child(child); THEN("the child is reachable through children()") { REQUIRE(parent_bus->children().size() == 1); REQUIRE(parent_bus->children()[0]->name() == "bus_ownership_child"); } THEN("the child's parent resolves back to the bus") { auto locked_parent = child->parent(); REQUIRE(locked_parent); REQUIRE(locked_parent->name() == "bus_ownership_parent"); } WHEN("the bus is destroyed while the caller still holds the child directly") { parent_bus.reset(); THEN("the child outlives the bus") { REQUIRE(child != nullptr); } THEN("the child's parent link no longer resolves") { REQUIRE(child->parent() == nullptr); } } } GIVEN("a bus that is the only thing keeping its child alive") { auto destroyed = false; auto parent_bus = kstd::make_shared("bus_lifetime_parent"); parent_bus->add_child(kstd::make_shared("bus_lifetime_child", destroyed)); WHEN("the bus is destroyed") { CHECK_FALSE(destroyed); parent_bus.reset(); THEN("the child is destroyed along with it") { REQUIRE(destroyed); } } } }