blob: 357cea8e33165e15fe813e49b2849a95bfcd19e5 [file] [log] [blame]
Narayan Kamathaef84a12020-01-02 15:20:13 +00001#include <gtest/gtest.h>
2
3#include "node-inl.h"
4
5#include <algorithm>
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +01006#include <limits>
Narayan Kamathaef84a12020-01-02 15:20:13 +00007#include <memory>
8#include <mutex>
9
10using mediaprovider::fuse::dirhandle;
11using mediaprovider::fuse::handle;
12using mediaprovider::fuse::node;
Narayan Kamath568f17a2020-02-19 13:45:29 +000013using mediaprovider::fuse::NodeTracker;
Narayan Kamathaef84a12020-01-02 15:20:13 +000014
15// Listed as a friend class to struct node so it can observe implementation
16// details if required. The only implementation detail that is worth writing
17// tests around at the moment is the reference count.
18class NodeTest : public ::testing::Test {
19 public:
Narayan Kamath568f17a2020-02-19 13:45:29 +000020 NodeTest() : tracker_(NodeTracker(&lock_)) {}
21
Narayan Kamathaef84a12020-01-02 15:20:13 +000022 uint32_t GetRefCount(node* node) { return node->refcount_; }
23
24 std::recursive_mutex lock_;
Narayan Kamath568f17a2020-02-19 13:45:29 +000025 NodeTracker tracker_;
Narayan Kamathaef84a12020-01-02 15:20:13 +000026
27 // Forward destruction here, as NodeTest is a friend class.
28 static void destroy(node* node) { delete node; }
29
Narayan Kamatheca34252020-02-11 13:08:37 +000030 static void acquire(node* node) { node->Acquire(); }
31
Narayan Kamathaef84a12020-01-02 15:20:13 +000032 typedef std::unique_ptr<node, decltype(&NodeTest::destroy)> unique_node_ptr;
33
34 unique_node_ptr CreateNode(node* parent, const std::string& path) {
Narayan Kamath568f17a2020-02-19 13:45:29 +000035 return unique_node_ptr(node::Create(parent, path, &lock_, &tracker_), &NodeTest::destroy);
Narayan Kamathaef84a12020-01-02 15:20:13 +000036 }
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +010037
38 // Expose NodeCompare for testing.
39 node::NodeCompare cmp;
Narayan Kamathaef84a12020-01-02 15:20:13 +000040};
41
42TEST_F(NodeTest, TestCreate) {
43 unique_node_ptr node = CreateNode(nullptr, "/path");
44
45 ASSERT_EQ("/path", node->GetName());
46 ASSERT_EQ(1, GetRefCount(node.get()));
47 ASSERT_FALSE(node->HasCachedHandle());
48}
49
50TEST_F(NodeTest, TestCreate_withParent) {
51 unique_node_ptr parent = CreateNode(nullptr, "/path");
52 ASSERT_EQ(1, GetRefCount(parent.get()));
53
54 // Adding a child to a parent node increments its refcount.
55 unique_node_ptr child = CreateNode(parent.get(), "subdir");
56 ASSERT_EQ(2, GetRefCount(parent.get()));
57
58 // Make sure the node has been added to the parents list of children.
Narayan Kamatheca34252020-02-11 13:08:37 +000059 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
60 ASSERT_EQ(1, GetRefCount(child.get()));
Narayan Kamathaef84a12020-01-02 15:20:13 +000061}
62
63TEST_F(NodeTest, TestRelease) {
Narayan Kamath568f17a2020-02-19 13:45:29 +000064 node* node = node::Create(nullptr, "/path", &lock_, &tracker_);
Narayan Kamatheca34252020-02-11 13:08:37 +000065 acquire(node);
66 acquire(node);
Narayan Kamathaef84a12020-01-02 15:20:13 +000067 ASSERT_EQ(3, GetRefCount(node));
68
69 ASSERT_FALSE(node->Release(1));
70 ASSERT_EQ(2, GetRefCount(node));
71
72 // A Release that makes refcount go negative should be a no-op.
73 ASSERT_FALSE(node->Release(10000));
74 ASSERT_EQ(2, GetRefCount(node));
75
76 // Finally, let the refcount go to zero.
77 ASSERT_TRUE(node->Release(2));
78}
79
Zim87c7bf82020-01-07 18:11:27 +000080TEST_F(NodeTest, TestRenameWithName) {
81 unique_node_ptr parent = CreateNode(nullptr, "/path");
82
83 unique_node_ptr child = CreateNode(parent.get(), "subdir");
84 ASSERT_EQ(2, GetRefCount(parent.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +000085 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +000086
87 child->Rename("subdir_new", parent.get());
88
89 ASSERT_EQ(2, GetRefCount(parent.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +000090 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
91 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir_new", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +000092
93 ASSERT_EQ("/path/subdir_new", child->BuildPath());
Narayan Kamatheca34252020-02-11 13:08:37 +000094 ASSERT_EQ(1, GetRefCount(child.get()));
Zim87c7bf82020-01-07 18:11:27 +000095}
96
97TEST_F(NodeTest, TestRenameWithParent) {
98 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
99 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
100
101 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
102 ASSERT_EQ(2, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000103 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +0000104
105 child->Rename("subdir", parent2.get());
106 ASSERT_EQ(1, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000107 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +0000108
109 ASSERT_EQ(2, GetRefCount(parent2.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000110 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +0000111
112 ASSERT_EQ("/path2/subdir", child->BuildPath());
Narayan Kamatheca34252020-02-11 13:08:37 +0000113 ASSERT_EQ(1, GetRefCount(child.get()));
Zim87c7bf82020-01-07 18:11:27 +0000114}
115
116TEST_F(NodeTest, TestRenameWithNameAndParent) {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000117 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
118 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
119
120 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
121 ASSERT_EQ(2, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000122 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000123
124 child->Rename("subdir_new", parent2.get());
125 ASSERT_EQ(1, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000126 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
127 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir_new", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000128
129 ASSERT_EQ(2, GetRefCount(parent2.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000130 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir_new", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000131
132 ASSERT_EQ("/path2/subdir_new", child->BuildPath());
Narayan Kamatheca34252020-02-11 13:08:37 +0000133 ASSERT_EQ(1, GetRefCount(child.get()));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000134}
135
136TEST_F(NodeTest, TestBuildPath) {
137 unique_node_ptr parent = CreateNode(nullptr, "/path");
138 ASSERT_EQ("/path", parent->BuildPath());
139
140 unique_node_ptr child = CreateNode(parent.get(), "subdir");
141 ASSERT_EQ("/path/subdir", child->BuildPath());
142
143 unique_node_ptr child2 = CreateNode(parent.get(), "subdir2");
144 ASSERT_EQ("/path/subdir2", child2->BuildPath());
145
146 unique_node_ptr subchild = CreateNode(child2.get(), "subsubdir");
147 ASSERT_EQ("/path/subdir2/subsubdir", subchild->BuildPath());
148}
149
150TEST_F(NodeTest, TestSetDeleted) {
151 unique_node_ptr parent = CreateNode(nullptr, "/path");
152 unique_node_ptr child = CreateNode(parent.get(), "subdir");
153
Narayan Kamatheca34252020-02-11 13:08:37 +0000154 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000155 child->SetDeleted();
Narayan Kamatheca34252020-02-11 13:08:37 +0000156 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000157}
158
159TEST_F(NodeTest, DeleteTree) {
160 unique_node_ptr parent = CreateNode(nullptr, "/path");
161
162 // This is the tree that we intend to delete.
Narayan Kamath568f17a2020-02-19 13:45:29 +0000163 node* child = node::Create(parent.get(), "subdir", &lock_, &tracker_);
164 node::Create(child, "s1", &lock_, &tracker_);
165 node* subchild2 = node::Create(child, "s2", &lock_, &tracker_);
166 node::Create(subchild2, "sc2", &lock_, &tracker_);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000167
Narayan Kamatheca34252020-02-11 13:08:37 +0000168 ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000169 node::DeleteTree(child);
Narayan Kamatheca34252020-02-11 13:08:37 +0000170 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000171}
172
173TEST_F(NodeTest, LookupChildByName_empty) {
174 unique_node_ptr parent = CreateNode(nullptr, "/path");
175 unique_node_ptr child = CreateNode(parent.get(), "subdir");
176
Narayan Kamatheca34252020-02-11 13:08:37 +0000177 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
178 ASSERT_EQ(nullptr, parent->LookupChildByName("", false /* acquire */));
179}
180
181TEST_F(NodeTest, LookupChildByName_refcounts) {
182 unique_node_ptr parent = CreateNode(nullptr, "/path");
183 unique_node_ptr child = CreateNode(parent.get(), "subdir");
184
185 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
186 ASSERT_EQ(1, GetRefCount(child.get()));
187
188 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", true /* acquire */));
189 ASSERT_EQ(2, GetRefCount(child.get()));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000190}
191
192TEST_F(NodeTest, LookupAbsolutePath) {
193 unique_node_ptr parent = CreateNode(nullptr, "/path");
194 unique_node_ptr child = CreateNode(parent.get(), "subdir");
195 unique_node_ptr child2 = CreateNode(parent.get(), "subdir2");
196 unique_node_ptr subchild = CreateNode(child2.get(), "subsubdir");
197
198 ASSERT_EQ(parent.get(), node::LookupAbsolutePath(parent.get(), "/path"));
199 ASSERT_EQ(parent.get(), node::LookupAbsolutePath(parent.get(), "/path/"));
200 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path2"));
201
202 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir"));
203 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir/"));
204 // TODO(narayan): Are the two cases below intentional behaviour ?
205 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path//subdir"));
206 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path///subdir"));
207
208 ASSERT_EQ(child2.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2"));
209 ASSERT_EQ(child2.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2/"));
210
211 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path/subdir3/"));
212
213 ASSERT_EQ(subchild.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2/subsubdir"));
214 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path/subdir/subsubdir"));
215}
216
217TEST_F(NodeTest, AddDestroyHandle) {
218 unique_node_ptr node = CreateNode(nullptr, "/path");
219
Zim7e35c332020-04-03 19:25:00 +0100220 handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000221 node->AddHandle(h);
222 ASSERT_TRUE(node->HasCachedHandle());
223
224 node->DestroyHandle(h);
225 ASSERT_FALSE(node->HasCachedHandle());
226
227 // Should all crash the process as the handle is no longer associated with
228 // the node in question.
229 EXPECT_DEATH(node->DestroyHandle(h), "");
230 EXPECT_DEATH(node->DestroyHandle(nullptr), "");
Sahana Raoee321362020-03-05 19:27:37 +0000231 std::unique_ptr<handle> h2(
Zim7e35c332020-04-03 19:25:00 +0100232 new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000233 EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
234}
Zim89962312020-04-22 21:21:53 +0100235
236TEST_F(NodeTest, CaseInsensitive) {
237 unique_node_ptr parent = CreateNode(nullptr, "/path");
Zim89962312020-04-22 21:21:53 +0100238 unique_node_ptr mixed_child = CreateNode(parent.get(), "cHiLd");
239
Zim0852ba72020-06-09 11:50:33 +0100240 node* upper_child = parent->LookupChildByName("CHILD", false /* acquire */);
241 node* lower_child = parent->LookupChildByName("child", false /* acquire */);
Zim89962312020-04-22 21:21:53 +0100242
Zim0852ba72020-06-09 11:50:33 +0100243 ASSERT_EQ(mixed_child.get(), lower_child);
244 ASSERT_EQ(mixed_child.get(), upper_child);
Zim89962312020-04-22 21:21:53 +0100245}
Nikita Ioffe6d9d9dc2020-06-18 17:35:08 +0100246
247TEST_F(NodeTest, RenameSameNameSameParent) {
248 unique_node_ptr parent = CreateNode(nullptr, "/path1");
249 unique_node_ptr child = CreateNode(parent.get(), "subdir");
250
251 ASSERT_EQ(child.get(), parent->LookupChildByName("SuBdIr", false /* acquire */));
252 ASSERT_EQ(2, GetRefCount(parent.get()));
253
254 child->Rename("subdir", parent.get());
255
256 ASSERT_EQ(child.get(), parent->LookupChildByName("SuBdIr", false /* acquire */));
257 ASSERT_EQ(2, GetRefCount(parent.get()));
258}
259
260TEST_F(NodeTest, RenameRoot) {
261 unique_node_ptr root = CreateNode(nullptr, "/root");
262 ASSERT_EQ(1, GetRefCount(root.get()));
263
264 root->Rename("/i-am-root!", nullptr);
265
266 ASSERT_EQ("/i-am-root!", root->GetName());
267 ASSERT_EQ(1, GetRefCount(root.get()));
268}
269
270TEST_F(NodeTest, NodeCompareDefinesLinearOrder) {
271 unique_node_ptr node_a = CreateNode(nullptr, "a");
272 unique_node_ptr node_b = CreateNode(nullptr, "B");
273 unique_node_ptr node_c = CreateNode(nullptr, "c");
274
275 ASSERT_FALSE(cmp.operator()(node_a.get(), node_a.get()));
276 ASSERT_FALSE(cmp.operator()(node_b.get(), node_b.get()));
277 ASSERT_FALSE(cmp.operator()(node_c.get(), node_c.get()));
278
279 auto check_fn = [&](const node* lhs_node, const node* rhs_node) {
280 ASSERT_TRUE(cmp.operator()(lhs_node, rhs_node));
281 ASSERT_FALSE(cmp.operator()(rhs_node, lhs_node));
282 };
283
284 check_fn(node_a.get(), node_b.get());
285 check_fn(node_b.get(), node_c.get());
286 check_fn(node_a.get(), node_c.get());
287
288 // ("A", 0) < node_a < ("a", max_uintptr_t) < node_b
289 ASSERT_TRUE(cmp.operator()(std::make_pair("A", 0), node_a.get()));
290 ASSERT_TRUE(cmp.operator()(node_a.get(),
291 std::make_pair("A", std::numeric_limits<uintptr_t>::max())));
292 ASSERT_TRUE(cmp.operator()(std::make_pair("A", std::numeric_limits<uintptr_t>::max()),
293 node_b.get()));
294}
295
296TEST_F(NodeTest, LookupChildByName_ChildrenWithSameName) {
297 unique_node_ptr parent = CreateNode(nullptr, "/path");
298 unique_node_ptr foo1 = CreateNode(parent.get(), "FoO");
299 unique_node_ptr foo2 = CreateNode(parent.get(), "fOo");
300 unique_node_ptr bar1 = CreateNode(parent.get(), "BAR");
301 unique_node_ptr bar2 = CreateNode(parent.get(), "bar");
302 unique_node_ptr baz1 = CreateNode(parent.get(), "baZ");
303 unique_node_ptr baz2 = CreateNode(parent.get(), "Baz");
304
305 auto test_fn = [&](const std::string& name, node* first, node* second) {
306 auto node1 = parent->LookupChildByName(name, false /* acquire */);
307 ASSERT_EQ(std::min(first, second), node1);
308 node1->SetDeleted();
309
310 auto node2 = parent->LookupChildByName(name, false /* acquire */);
311 ASSERT_EQ(std::max(first, second), node2);
312 node2->SetDeleted();
313
314 ASSERT_EQ(nullptr, parent->LookupChildByName(name, false /* acquire */));
315 };
316
317 test_fn("foo", foo1.get(), foo2.get());
318 test_fn("bAr", bar1.get(), bar2.get());
319 test_fn("BaZ", baz1.get(), baz2.get());
320}