1 #include <gtest/gtest.h>
2
3 #include "node-inl.h"
4
5 #include <algorithm>
6 #include <limits>
7 #include <memory>
8 #include <mutex>
9
10 using mediaprovider::fuse::handle;
11 using mediaprovider::fuse::node;
12 using mediaprovider::fuse::NodeTracker;
13
14 // Listed as a friend class to struct node so it can observe implementation
15 // details if required. The only implementation detail that is worth writing
16 // tests around at the moment is the reference count.
17 class NodeTest : public ::testing::Test {
18 public:
NodeTest()19 NodeTest() : tracker_(NodeTracker(&lock_)) {}
20
GetRefCount(node * node)21 uint32_t GetRefCount(node* node) { return node->refcount_; }
22
23 std::recursive_mutex lock_;
24 NodeTracker tracker_;
25
26 // Forward destruction here, as NodeTest is a friend class.
destroy(node * node)27 static void destroy(node* node) { delete node; }
28
acquire(node * node)29 static void acquire(node* node) { node->Acquire(); }
30
31 typedef std::unique_ptr<node, decltype(&NodeTest::destroy)> unique_node_ptr;
32
CreateNode(node * parent,const std::string & path,const int transforms=0)33 unique_node_ptr CreateNode(node* parent, const std::string& path, const int transforms = 0) {
34 return unique_node_ptr(
35 node::Create(parent, path, "", true, transforms, 0, &lock_, 0, &tracker_),
36 &NodeTest::destroy);
37 }
38
ForChild(class node * node,const std::string & name,const std::function<bool (class node *)> & callback)39 static class node* ForChild(class node* node, const std::string& name,
40 const std::function<bool(class node*)>& callback) {
41 return node->ForChild(name, callback);
42 }
43
44 // Expose NodeCompare for testing.
45 node::NodeCompare cmp;
46 };
47
TEST_F(NodeTest,TestCreate)48 TEST_F(NodeTest, TestCreate) {
49 unique_node_ptr node = CreateNode(nullptr, "/path");
50
51 ASSERT_EQ("/path", node->GetName());
52 ASSERT_EQ(1, GetRefCount(node.get()));
53 ASSERT_FALSE(node->HasCachedHandle());
54 }
55
TEST_F(NodeTest,TestCreate_withParent)56 TEST_F(NodeTest, TestCreate_withParent) {
57 unique_node_ptr parent = CreateNode(nullptr, "/path");
58 ASSERT_EQ(1, GetRefCount(parent.get()));
59
60 // Adding a child to a parent node increments its refcount.
61 unique_node_ptr child = CreateNode(parent.get(), "subdir");
62 ASSERT_EQ(2, GetRefCount(parent.get()));
63
64 // Make sure the node has been added to the parents list of children.
65 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
66 ASSERT_EQ(1, GetRefCount(child.get()));
67 }
68
TEST_F(NodeTest,TestRelease)69 TEST_F(NodeTest, TestRelease) {
70 node* node = node::Create(nullptr, "/path", "", true, 0, 0, &lock_, 0, &tracker_);
71 acquire(node);
72 acquire(node);
73 ASSERT_EQ(3, GetRefCount(node));
74
75 ASSERT_FALSE(node->Release(1));
76 ASSERT_EQ(2, GetRefCount(node));
77
78 // A Release that makes refcount go negative should be a no-op.
79 ASSERT_FALSE(node->Release(10000));
80 ASSERT_EQ(2, GetRefCount(node));
81
82 // Finally, let the refcount go to zero.
83 ASSERT_TRUE(node->Release(2));
84 }
85
TEST_F(NodeTest,TestRenameName)86 TEST_F(NodeTest, TestRenameName) {
87 unique_node_ptr parent = CreateNode(nullptr, "/path");
88
89 unique_node_ptr child = CreateNode(parent.get(), "subdir");
90 ASSERT_EQ(2, GetRefCount(parent.get()));
91 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
92
93 child->Rename("subdir_new", parent.get());
94
95 ASSERT_EQ(2, GetRefCount(parent.get()));
96 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
97 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir_new", false /* acquire */));
98
99 ASSERT_EQ("/path/subdir_new", child->BuildPath());
100 ASSERT_EQ(1, GetRefCount(child.get()));
101 }
102
TEST_F(NodeTest,TestRenameParent)103 TEST_F(NodeTest, TestRenameParent) {
104 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
105 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
106
107 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
108 ASSERT_EQ(2, GetRefCount(parent1.get()));
109 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
110
111 child->Rename("subdir", parent2.get());
112 ASSERT_EQ(1, GetRefCount(parent1.get()));
113 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
114
115 ASSERT_EQ(2, GetRefCount(parent2.get()));
116 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir", false /* acquire */));
117
118 ASSERT_EQ("/path2/subdir", child->BuildPath());
119 ASSERT_EQ(1, GetRefCount(child.get()));
120 }
121
TEST_F(NodeTest,TestRenameNameAndParent)122 TEST_F(NodeTest, TestRenameNameAndParent) {
123 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
124 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
125
126 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
127 ASSERT_EQ(2, GetRefCount(parent1.get()));
128 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
129
130 child->Rename("subdir_new", parent2.get());
131 ASSERT_EQ(1, GetRefCount(parent1.get()));
132 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
133 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir_new", false /* acquire */));
134
135 ASSERT_EQ(2, GetRefCount(parent2.get()));
136 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir_new", false /* acquire */));
137
138 ASSERT_EQ("/path2/subdir_new", child->BuildPath());
139 ASSERT_EQ(1, GetRefCount(child.get()));
140 }
141
TEST_F(NodeTest,TestRenameNameForChild)142 TEST_F(NodeTest, TestRenameNameForChild) {
143 unique_node_ptr parent = CreateNode(nullptr, "/path");
144
145 unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
146 unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
147 ASSERT_EQ(3, GetRefCount(parent.get()));
148 ASSERT_EQ(child0.get(),
149 parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
150 ASSERT_EQ(child1.get(),
151 parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
152
153 parent->RenameChild("subdir", "subdir_new", parent.get());
154
155 ASSERT_EQ(3, GetRefCount(parent.get()));
156 ASSERT_EQ(nullptr,
157 parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
158 ASSERT_EQ(nullptr,
159 parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
160 ASSERT_EQ(child0.get(),
161 parent->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
162 ASSERT_EQ(child1.get(),
163 parent->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
164
165 ASSERT_EQ("/path/subdir_new", child0->BuildPath());
166 ASSERT_EQ("/path/subdir_new", child1->BuildPath());
167 ASSERT_EQ(1, GetRefCount(child0.get()));
168 ASSERT_EQ(1, GetRefCount(child1.get()));
169 }
170
TEST_F(NodeTest,TestRenameParentForChild)171 TEST_F(NodeTest, TestRenameParentForChild) {
172 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
173 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
174
175 unique_node_ptr child0 = CreateNode(parent1.get(), "subdir", 0 /* transforms */);
176 unique_node_ptr child1 = CreateNode(parent1.get(), "subdir", 1 /* transforms */);
177 ASSERT_EQ(3, GetRefCount(parent1.get()));
178 ASSERT_EQ(child0.get(),
179 parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
180 ASSERT_EQ(child1.get(),
181 parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
182
183 parent1->RenameChild("subdir", "subdir", parent2.get());
184 ASSERT_EQ(1, GetRefCount(parent1.get()));
185 ASSERT_EQ(nullptr,
186 parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
187 ASSERT_EQ(nullptr,
188 parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
189
190 ASSERT_EQ(3, GetRefCount(parent2.get()));
191 ASSERT_EQ(child0.get(),
192 parent2->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
193 ASSERT_EQ(child1.get(),
194 parent2->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
195
196 ASSERT_EQ("/path2/subdir", child0->BuildPath());
197 ASSERT_EQ("/path2/subdir", child1->BuildPath());
198 ASSERT_EQ(1, GetRefCount(child0.get()));
199 ASSERT_EQ(1, GetRefCount(child1.get()));
200 }
201
TEST_F(NodeTest,TestRenameNameAndParentForChild)202 TEST_F(NodeTest, TestRenameNameAndParentForChild) {
203 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
204 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
205
206 unique_node_ptr child0 = CreateNode(parent1.get(), "subdir", 0 /* transforms */);
207 unique_node_ptr child1 = CreateNode(parent1.get(), "subdir", 1 /* transforms */);
208 ASSERT_EQ(3, GetRefCount(parent1.get()));
209 ASSERT_EQ(child0.get(),
210 parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
211 ASSERT_EQ(child1.get(),
212 parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
213
214 parent1->RenameChild("subdir", "subdir_new", parent2.get());
215 ASSERT_EQ(1, GetRefCount(parent1.get()));
216 ASSERT_EQ(nullptr,
217 parent1->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
218 ASSERT_EQ(nullptr,
219 parent1->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
220 ASSERT_EQ(nullptr,
221 parent1->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
222 ASSERT_EQ(nullptr,
223 parent1->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
224
225 ASSERT_EQ(3, GetRefCount(parent2.get()));
226 ASSERT_EQ(nullptr,
227 parent1->LookupChildByName("subdir_new", false /* acquire */, 0 /* transforms */));
228 ASSERT_EQ(nullptr,
229 parent1->LookupChildByName("subdir_new", false /* acquire */, 1 /* transforms */));
230
231 ASSERT_EQ("/path2/subdir_new", child0->BuildPath());
232 ASSERT_EQ("/path2/subdir_new", child1->BuildPath());
233 ASSERT_EQ(1, GetRefCount(child0.get()));
234 ASSERT_EQ(1, GetRefCount(child1.get()));
235 }
236
TEST_F(NodeTest,TestBuildPath)237 TEST_F(NodeTest, TestBuildPath) {
238 unique_node_ptr parent = CreateNode(nullptr, "/path");
239 ASSERT_EQ("/path", parent->BuildPath());
240
241 unique_node_ptr child = CreateNode(parent.get(), "subdir");
242 ASSERT_EQ("/path/subdir", child->BuildPath());
243
244 unique_node_ptr child2 = CreateNode(parent.get(), "subdir2");
245 ASSERT_EQ("/path/subdir2", child2->BuildPath());
246
247 unique_node_ptr subchild = CreateNode(child2.get(), "subsubdir");
248 ASSERT_EQ("/path/subdir2/subsubdir", subchild->BuildPath());
249 }
250
TEST_F(NodeTest,TestSetDeleted)251 TEST_F(NodeTest, TestSetDeleted) {
252 unique_node_ptr parent = CreateNode(nullptr, "/path");
253 unique_node_ptr child = CreateNode(parent.get(), "subdir");
254
255 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
256 child->SetDeleted();
257 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
258 }
259
TEST_F(NodeTest,TestSetDeletedForChild)260 TEST_F(NodeTest, TestSetDeletedForChild) {
261 unique_node_ptr parent = CreateNode(nullptr, "/path");
262 unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
263 unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
264
265 ASSERT_EQ(child0.get(),
266 parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
267 ASSERT_EQ(child1.get(),
268 parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
269 parent->SetDeletedForChild("subdir");
270 ASSERT_EQ(nullptr,
271 parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
272 ASSERT_EQ(nullptr,
273 parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
274 }
275
TEST_F(NodeTest,DeleteTree)276 TEST_F(NodeTest, DeleteTree) {
277 unique_node_ptr parent = CreateNode(nullptr, "/path");
278
279 // This is the tree that we intend to delete.
280 node* child = node::Create(parent.get(), "subdir", "", true, 0, 0, &lock_, 0, &tracker_);
281 node::Create(child, "s1", "", true, 0, 0, &lock_, 0, &tracker_);
282 node* subchild2 = node::Create(child, "s2", "", true, 0, 0, &lock_, 0, &tracker_);
283 node::Create(subchild2, "sc2", "", true, 0, 0, &lock_, 0, &tracker_);
284
285 ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
286 node::DeleteTree(child);
287 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
288 }
289
TEST_F(NodeTest,LookupChildByName_empty)290 TEST_F(NodeTest, LookupChildByName_empty) {
291 unique_node_ptr parent = CreateNode(nullptr, "/path");
292 unique_node_ptr child = CreateNode(parent.get(), "subdir");
293
294 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
295 ASSERT_EQ(nullptr, parent->LookupChildByName("", false /* acquire */));
296 }
297
TEST_F(NodeTest,LookupChildByName_transforms)298 TEST_F(NodeTest, LookupChildByName_transforms) {
299 unique_node_ptr parent = CreateNode(nullptr, "/path");
300 unique_node_ptr child0 = CreateNode(parent.get(), "subdir", 0 /* transforms */);
301 unique_node_ptr child1 = CreateNode(parent.get(), "subdir", 1 /* transforms */);
302
303 ASSERT_EQ(child0.get(), parent->LookupChildByName("subdir", false /* acquire */));
304 ASSERT_EQ(child0.get(),
305 parent->LookupChildByName("subdir", false /* acquire */, 0 /* transforms */));
306 ASSERT_EQ(child1.get(),
307 parent->LookupChildByName("subdir", false /* acquire */, 1 /* transforms */));
308 ASSERT_EQ(nullptr,
309 parent->LookupChildByName("subdir", false /* acquire */, 2 /* transforms */));
310 }
311
TEST_F(NodeTest,LookupChildByName_refcounts)312 TEST_F(NodeTest, LookupChildByName_refcounts) {
313 unique_node_ptr parent = CreateNode(nullptr, "/path");
314 unique_node_ptr child = CreateNode(parent.get(), "subdir");
315
316 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
317 ASSERT_EQ(1, GetRefCount(child.get()));
318
319 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", true /* acquire */));
320 ASSERT_EQ(2, GetRefCount(child.get()));
321 }
322
TEST_F(NodeTest,LookupAbsolutePath)323 TEST_F(NodeTest, LookupAbsolutePath) {
324 unique_node_ptr parent = CreateNode(nullptr, "/path");
325 unique_node_ptr child = CreateNode(parent.get(), "subdir");
326 unique_node_ptr child2 = CreateNode(parent.get(), "subdir2");
327 unique_node_ptr subchild = CreateNode(child2.get(), "subsubdir");
328
329 ASSERT_EQ(parent.get(), node::LookupAbsolutePath(parent.get(), "/path"));
330 ASSERT_EQ(parent.get(), node::LookupAbsolutePath(parent.get(), "/path/"));
331 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path2"));
332
333 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir"));
334 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir/"));
335 // TODO(narayan): Are the two cases below intentional behaviour ?
336 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path//subdir"));
337 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path///subdir"));
338
339 ASSERT_EQ(child2.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2"));
340 ASSERT_EQ(child2.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2/"));
341
342 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path/subdir3/"));
343
344 ASSERT_EQ(subchild.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2/subsubdir"));
345 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path/subdir/subsubdir"));
346 }
347
TEST_F(NodeTest,AddDestroyHandle)348 TEST_F(NodeTest, AddDestroyHandle) {
349 unique_node_ptr node = CreateNode(nullptr, "/path");
350
351 handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */,
352 false /* passthrough */, 0 /* uid */, 0 /* transforms_uid */);
353 node->AddHandle(h);
354 ASSERT_TRUE(node->HasCachedHandle());
355
356 node->DestroyHandle(h);
357 ASSERT_FALSE(node->HasCachedHandle());
358
359 // Should all crash the process as the handle is no longer associated with
360 // the node in question.
361 EXPECT_DEATH(node->DestroyHandle(h), "");
362 EXPECT_DEATH(node->DestroyHandle(nullptr), "");
363 std::unique_ptr<handle> h2(new handle(-1, new mediaprovider::fuse::RedactionInfo,
364 true /* cached */, false /* passthrough */, 0 /* uid */,
365 0 /* transforms_uid */));
366 EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
367 }
368
TEST_F(NodeTest,CheckHandleForUid_foundSingle_shouldRedact)369 TEST_F(NodeTest, CheckHandleForUid_foundSingle_shouldRedact) {
370 unique_node_ptr node = CreateNode(nullptr, "/path");
371
372 off64_t ranges[2] = {0, 1};
373 mediaprovider::fuse::RedactionInfo* infoWithLocation =
374 new mediaprovider::fuse::RedactionInfo(1, ranges);
375
376 handle* h = new handle(-1, infoWithLocation, true /* cached */, false /* passthrough */,
377 1 /* uid */, 0 /* transforms_uid */);
378
379 node->AddHandle(h);
380 std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
381 ASSERT_TRUE(res->should_redact);
382 ASSERT_EQ(res->file_path, "/path");
383 }
384
TEST_F(NodeTest,CheckHandleForUid_foundSingle_shouldNotRedact)385 TEST_F(NodeTest, CheckHandleForUid_foundSingle_shouldNotRedact) {
386 unique_node_ptr node = CreateNode(nullptr, "/path");
387
388 mediaprovider::fuse::RedactionInfo* infoWithoutLocation = new mediaprovider::fuse::RedactionInfo;
389
390 handle* h = new handle(-1, infoWithoutLocation, true /* cached */, false /* passthrough */,
391 1 /* uid */, 0 /* transforms_uid */);
392
393 node->AddHandle(h);
394 std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
395 ASSERT_FALSE(res->should_redact);
396 ASSERT_EQ(res->file_path, "/path");
397 }
398
TEST_F(NodeTest,CheckHandleForUid_foundMultiple_shouldNotRedact)399 TEST_F(NodeTest, CheckHandleForUid_foundMultiple_shouldNotRedact) {
400 unique_node_ptr node = CreateNode(nullptr, "/path");
401
402 off64_t ranges[2] = {0, 1};
403 mediaprovider::fuse::RedactionInfo* infoWithLocation =
404 new mediaprovider::fuse::RedactionInfo(1, ranges);
405 mediaprovider::fuse::RedactionInfo* infoWithoutLocation = new mediaprovider::fuse::RedactionInfo;
406
407 handle* h1 = new handle(-1, infoWithLocation, true /* cached */, false /* passthrough */,
408 1 /* uid */, 0 /* transforms_uid */);
409 handle* h2 = new handle(-1, infoWithoutLocation, true /* cached */, false /* passthrough */,
410 1 /* uid */, 0 /* transforms_uid */);
411
412 node->AddHandle(h1);
413 node->AddHandle(h2);
414 std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
415 ASSERT_FALSE(res->should_redact);
416 ASSERT_EQ(res->file_path, "/path");
417 }
418
TEST_F(NodeTest,CheckHandleForUid_notFound_differentUid)419 TEST_F(NodeTest, CheckHandleForUid_notFound_differentUid) {
420 unique_node_ptr node = CreateNode(nullptr, "/path");
421
422 off64_t ranges[2] = {0, 1};
423 mediaprovider::fuse::RedactionInfo* infoWithLocation =
424 new mediaprovider::fuse::RedactionInfo(1, ranges);
425
426 handle* h = new handle(-1, infoWithLocation, true /* cached */, false /* passthrough */,
427 2 /* uid */, 0 /* transforms_uid */);
428
429 node->AddHandle(h);
430 std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
431 ASSERT_FALSE(res->should_redact);
432 ASSERT_EQ(res->file_path, "");
433 }
434
TEST_F(NodeTest,CheckHandleForUid_notFound_noHandle)435 TEST_F(NodeTest, CheckHandleForUid_notFound_noHandle) {
436 unique_node_ptr node = CreateNode(nullptr, "/path");
437
438 std::unique_ptr<mediaprovider::fuse::FdAccessResult> res(node->CheckHandleForUid(1));
439 ASSERT_FALSE(res->should_redact);
440 ASSERT_EQ(res->file_path, "");
441 }
442
TEST_F(NodeTest,CaseInsensitive)443 TEST_F(NodeTest, CaseInsensitive) {
444 unique_node_ptr parent = CreateNode(nullptr, "/path");
445 unique_node_ptr mixed_child = CreateNode(parent.get(), "cHiLd");
446
447 node* upper_child = parent->LookupChildByName("CHILD", false /* acquire */);
448 node* lower_child = parent->LookupChildByName("child", false /* acquire */);
449
450 ASSERT_EQ(mixed_child.get(), lower_child);
451 ASSERT_EQ(mixed_child.get(), upper_child);
452 }
453
TEST_F(NodeTest,RenameSameNameSameParent)454 TEST_F(NodeTest, RenameSameNameSameParent) {
455 unique_node_ptr parent = CreateNode(nullptr, "/path1");
456 unique_node_ptr child = CreateNode(parent.get(), "subdir");
457
458 ASSERT_EQ(child.get(), parent->LookupChildByName("SuBdIr", false /* acquire */));
459 ASSERT_EQ(2, GetRefCount(parent.get()));
460
461 child->Rename("subdir", parent.get());
462
463 ASSERT_EQ(child.get(), parent->LookupChildByName("SuBdIr", false /* acquire */));
464 ASSERT_EQ(2, GetRefCount(parent.get()));
465 }
466
TEST_F(NodeTest,RenameRoot)467 TEST_F(NodeTest, RenameRoot) {
468 unique_node_ptr root = CreateNode(nullptr, "/root");
469 ASSERT_EQ(1, GetRefCount(root.get()));
470
471 root->Rename("/i-am-root!", nullptr);
472
473 ASSERT_EQ("/i-am-root!", root->GetName());
474 ASSERT_EQ(1, GetRefCount(root.get()));
475 }
476
TEST_F(NodeTest,NodeCompareDefinesLinearOrder)477 TEST_F(NodeTest, NodeCompareDefinesLinearOrder) {
478 unique_node_ptr node_a = CreateNode(nullptr, "a");
479 unique_node_ptr node_b = CreateNode(nullptr, "B");
480 unique_node_ptr node_c = CreateNode(nullptr, "c");
481
482 ASSERT_FALSE(cmp.operator()(node_a.get(), node_a.get()));
483 ASSERT_FALSE(cmp.operator()(node_b.get(), node_b.get()));
484 ASSERT_FALSE(cmp.operator()(node_c.get(), node_c.get()));
485
486 auto check_fn = [&](const node* lhs_node, const node* rhs_node) {
487 ASSERT_TRUE(cmp.operator()(lhs_node, rhs_node));
488 ASSERT_FALSE(cmp.operator()(rhs_node, lhs_node));
489 };
490
491 check_fn(node_a.get(), node_b.get());
492 check_fn(node_b.get(), node_c.get());
493 check_fn(node_a.get(), node_c.get());
494
495 // ("A", 0) < node_a < ("a", max_uintptr_t) < node_b
496 ASSERT_TRUE(cmp.operator()(std::make_pair("A", 0), node_a.get()));
497 ASSERT_TRUE(cmp.operator()(node_a.get(),
498 std::make_pair("A", std::numeric_limits<uintptr_t>::max())));
499 ASSERT_TRUE(cmp.operator()(std::make_pair("A", std::numeric_limits<uintptr_t>::max()),
500 node_b.get()));
501 }
502
TEST_F(NodeTest,LookupChildByName_ChildrenWithSameName)503 TEST_F(NodeTest, LookupChildByName_ChildrenWithSameName) {
504 unique_node_ptr parent = CreateNode(nullptr, "/path");
505 unique_node_ptr foo1 = CreateNode(parent.get(), "FoO");
506 unique_node_ptr foo2 = CreateNode(parent.get(), "fOo");
507 unique_node_ptr bar1 = CreateNode(parent.get(), "BAR");
508 unique_node_ptr bar2 = CreateNode(parent.get(), "bar");
509 unique_node_ptr baz1 = CreateNode(parent.get(), "baZ");
510 unique_node_ptr baz2 = CreateNode(parent.get(), "Baz");
511
512 auto test_fn = [&](const std::string& name, node* first, node* second) {
513 auto node1 = parent->LookupChildByName(name, false /* acquire */);
514 ASSERT_EQ(std::min(first, second), node1);
515 node1->SetDeleted();
516
517 auto node2 = parent->LookupChildByName(name, false /* acquire */);
518 ASSERT_EQ(std::max(first, second), node2);
519 node2->SetDeleted();
520
521 ASSERT_EQ(nullptr, parent->LookupChildByName(name, false /* acquire */));
522 };
523
524 test_fn("foo", foo1.get(), foo2.get());
525 test_fn("bAr", bar1.get(), bar2.get());
526 test_fn("BaZ", baz1.get(), baz2.get());
527 }
528
TEST_F(NodeTest,DestroyDoesntDoubleFree)529 TEST_F(NodeTest, DestroyDoesntDoubleFree) {
530 node* root = node::Create(nullptr, "root", "", true, 0, 0, &lock_, 0, &tracker_);
531 node* child = node::Create(root, "child", "", true, 0, 0, &lock_, 0, &tracker_);
532 node* grandchild = node::Create(child, "grandchild", "", true, 0, 0, &lock_, 0, &tracker_);
533
534 // 'child' is referenced by itself and by 'grandchild'
535 ASSERT_EQ(2, GetRefCount(child));
536 // Kernel forgets about child only
537 ASSERT_FALSE(child->Release(1));
538 // Child only referenced by 'grandchild'
539 ASSERT_EQ(1, GetRefCount(child));
540
541 // Now, destroying the filesystem shouldn't result in a double free
542 node::DeleteTree(root);
543 }
544
TEST_F(NodeTest,ForChild)545 TEST_F(NodeTest, ForChild) {
546 unique_node_ptr parent = CreateNode(nullptr, "/path");
547 unique_node_ptr foo1 = CreateNode(parent.get(), "FoO");
548 unique_node_ptr foo2 = CreateNode(parent.get(), "fOo");
549 unique_node_ptr foo3 = CreateNode(parent.get(), "foo");
550 foo3->SetDeleted();
551
552 std::vector<node*> match_all;
553 auto test_fn_match_all = [&](node* child) {
554 match_all.push_back(child);
555 return false;
556 };
557
558 std::vector<node*> match_first;
559 auto test_fn_match_first = [&](node* child) {
560 match_first.push_back(child);
561 return true;
562 };
563
564 std::vector<node*> match_none;
565 auto test_fn_match_none = [&](node* child) {
566 match_none.push_back(child);
567 return false;
568 };
569
570 node* node_all = ForChild(parent.get(), "foo", test_fn_match_all);
571 ASSERT_EQ(nullptr, node_all);
572 ASSERT_EQ(2, match_all.size());
573 ASSERT_EQ(std::min(foo1.get(), foo2.get()), match_all[0]);
574 ASSERT_EQ(std::max(foo1.get(), foo2.get()), match_all[1]);
575
576 node* node_first = ForChild(parent.get(), "foo", test_fn_match_first);
577 ASSERT_EQ(std::min(foo1.get(), foo2.get()), node_first);
578 ASSERT_EQ(1, match_first.size());
579 ASSERT_EQ(std::min(foo1.get(), foo2.get()), match_first[0]);
580
581 node* node_none = ForChild(parent.get(), "bar", test_fn_match_none);
582 ASSERT_EQ(nullptr, node_none);
583 ASSERT_TRUE(match_none.empty());
584 }
585