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