blob: 0a33df1ad5a4e247d5d43188d15959445c0c9b4b (
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
#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);
}
}
}
}
SCENARIO("Removing a child from a bus tears down the while subtree", "[devices][bus]")
{
GIVEN("A bus with a child bus")
{
auto root = kstd::make_shared<test_bus>("bus_removal_root");
auto middle = kstd::make_shared<test_bus>("bus_removal_middle");
auto leaf_destroyed = false;
auto leaf = kstd::make_shared<flagging_device>("bus_removal_leaf", leaf_destroyed);
root->add_child(middle);
middle->add_child(leaf);
REQUIRE(root->children().size() == 1);
REQUIRE(middle->children().size() == 1);
WHEN("the middle bus is removed from the root")
{
REQUIRE(root->remove_child(*middle));
THEN("the middle bus's own subtree was torn down")
{
REQUIRE(middle->children().empty());
}
THEN("the leaf is marked as removed but not destroyed")
{
REQUIRE(leaf->state() == kapi::devices::state::removed);
REQUIRE(leaf->parent() == nullptr);
REQUIRE_FALSE(leaf_destroyed);
}
AND_WHEN("dropping the outside leaf reference")
{
leaf.reset();
THEN("the leaf is destroyed")
{
REQUIRE(leaf_destroyed);
}
}
THEN("the root no longer considers the middle its child")
{
REQUIRE(root->children().empty());
REQUIRE(middle->state() == kapi::devices::state::removed);
}
}
}
GIVEN("A bus and a device that was never actually attached to it")
{
auto bus = kstd::make_shared<test_bus>("bus_removal_unrelated_bus");
auto unrelated = kstd::make_shared<test_device>("bus_removal_unrelated_device");
THEN("removing the child fails")
{
REQUIRE_FALSE(bus->remove_child(*unrelated));
}
}
}
|