aboutsummaryrefslogtreecommitdiff
path: root/kernel/kapi/devices/bus.tests.cpp
blob: 9f95513bf6fc76fd2879c2963337602f5fa726ef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <kapi/devices.hpp>

#include <kstd/memory.hpp>
#include <kstd/string.hpp>

#include <catch2/catch_test_macros.hpp>

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;
  };

  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;
    }

  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<test_bus>("bus_ownership_parent");
    auto child = kstd::make_shared<test_device>("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<test_bus>("bus_lifetime_parent");
    parent_bus->add_child(kstd::make_shared<flagging_device>("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);
      }
    }
  }
}