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