blob: 90f0894bc11af329c1d8dcd2b067eaf666549771 [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 Ioffe890e6f22020-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 Ioffe890e6f22020-06-18 17:35:08 +010037
Zim53c2d702020-09-23 12:18:27 +010038 static class node* ForChild(class node* node, const std::string& name,
39 const std::function<bool(class node*)>& callback) {
40 return node->ForChild(name, callback);
41 }
42
Nikita Ioffe890e6f22020-06-18 17:35:08 +010043 // Expose NodeCompare for testing.
44 node::NodeCompare cmp;
Narayan Kamathaef84a12020-01-02 15:20:13 +000045};
46
47TEST_F(NodeTest, TestCreate) {
48 unique_node_ptr node = CreateNode(nullptr, "/path");
49
50 ASSERT_EQ("/path", node->GetName());
51 ASSERT_EQ(1, GetRefCount(node.get()));
52 ASSERT_FALSE(node->HasCachedHandle());
53}
54
55TEST_F(NodeTest, TestCreate_withParent) {
56 unique_node_ptr parent = CreateNode(nullptr, "/path");
57 ASSERT_EQ(1, GetRefCount(parent.get()));
58
59 // Adding a child to a parent node increments its refcount.
60 unique_node_ptr child = CreateNode(parent.get(), "subdir");
61 ASSERT_EQ(2, GetRefCount(parent.get()));
62
63 // Make sure the node has been added to the parents list of children.
Narayan Kamatheca34252020-02-11 13:08:37 +000064 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
65 ASSERT_EQ(1, GetRefCount(child.get()));
Narayan Kamathaef84a12020-01-02 15:20:13 +000066}
67
68TEST_F(NodeTest, TestRelease) {
Narayan Kamath568f17a2020-02-19 13:45:29 +000069 node* node = node::Create(nullptr, "/path", &lock_, &tracker_);
Narayan Kamatheca34252020-02-11 13:08:37 +000070 acquire(node);
71 acquire(node);
Narayan Kamathaef84a12020-01-02 15:20:13 +000072 ASSERT_EQ(3, GetRefCount(node));
73
74 ASSERT_FALSE(node->Release(1));
75 ASSERT_EQ(2, GetRefCount(node));
76
77 // A Release that makes refcount go negative should be a no-op.
78 ASSERT_FALSE(node->Release(10000));
79 ASSERT_EQ(2, GetRefCount(node));
80
81 // Finally, let the refcount go to zero.
82 ASSERT_TRUE(node->Release(2));
83}
84
Zim53c2d702020-09-23 12:18:27 +010085TEST_F(NodeTest, TestRenameName) {
Zim87c7bf82020-01-07 18:11:27 +000086 unique_node_ptr parent = CreateNode(nullptr, "/path");
87
88 unique_node_ptr child = CreateNode(parent.get(), "subdir");
89 ASSERT_EQ(2, GetRefCount(parent.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +000090 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +000091
92 child->Rename("subdir_new", parent.get());
93
94 ASSERT_EQ(2, GetRefCount(parent.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +000095 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
96 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir_new", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +000097
98 ASSERT_EQ("/path/subdir_new", child->BuildPath());
Narayan Kamatheca34252020-02-11 13:08:37 +000099 ASSERT_EQ(1, GetRefCount(child.get()));
Zim87c7bf82020-01-07 18:11:27 +0000100}
101
Zim53c2d702020-09-23 12:18:27 +0100102TEST_F(NodeTest, TestRenameParent) {
Zim87c7bf82020-01-07 18:11:27 +0000103 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
104 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
105
106 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
107 ASSERT_EQ(2, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000108 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +0000109
110 child->Rename("subdir", parent2.get());
111 ASSERT_EQ(1, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000112 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +0000113
114 ASSERT_EQ(2, GetRefCount(parent2.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000115 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir", false /* acquire */));
Zim87c7bf82020-01-07 18:11:27 +0000116
117 ASSERT_EQ("/path2/subdir", child->BuildPath());
Narayan Kamatheca34252020-02-11 13:08:37 +0000118 ASSERT_EQ(1, GetRefCount(child.get()));
Zim87c7bf82020-01-07 18:11:27 +0000119}
120
Zim53c2d702020-09-23 12:18:27 +0100121TEST_F(NodeTest, TestRenameNameAndParent) {
Narayan Kamathaef84a12020-01-02 15:20:13 +0000122 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
123 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
124
125 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
126 ASSERT_EQ(2, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000127 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000128
129 child->Rename("subdir_new", parent2.get());
130 ASSERT_EQ(1, GetRefCount(parent1.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000131 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
132 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir_new", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000133
134 ASSERT_EQ(2, GetRefCount(parent2.get()));
Narayan Kamatheca34252020-02-11 13:08:37 +0000135 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir_new", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000136
137 ASSERT_EQ("/path2/subdir_new", child->BuildPath());
Narayan Kamatheca34252020-02-11 13:08:37 +0000138 ASSERT_EQ(1, GetRefCount(child.get()));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000139}
140
Zim53c2d702020-09-23 12:18:27 +0100141TEST_F(NodeTest, TestRenameNameForChild) {
142 unique_node_ptr parent = CreateNode(nullptr, "/path");
143
144 unique_node_ptr child = CreateNode(parent.get(), "subdir");
145 ASSERT_EQ(2, GetRefCount(parent.get()));
146 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
147
148 parent->RenameChild("subdir", "subdir_new", parent.get());
149
150 ASSERT_EQ(2, GetRefCount(parent.get()));
151 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
152 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir_new", false /* acquire */));
153
154 ASSERT_EQ("/path/subdir_new", child->BuildPath());
155 ASSERT_EQ(1, GetRefCount(child.get()));
156}
157
158TEST_F(NodeTest, TestRenameParentForChild) {
159 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
160 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
161
162 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
163 ASSERT_EQ(2, GetRefCount(parent1.get()));
164 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
165
166 parent1->RenameChild("subdir", "subdir", parent2.get());
167 ASSERT_EQ(1, GetRefCount(parent1.get()));
168 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
169
170 ASSERT_EQ(2, GetRefCount(parent2.get()));
171 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir", false /* acquire */));
172
173 ASSERT_EQ("/path2/subdir", child->BuildPath());
174 ASSERT_EQ(1, GetRefCount(child.get()));
175}
176
177TEST_F(NodeTest, TestRenameNameAndParentForChild) {
178 unique_node_ptr parent1 = CreateNode(nullptr, "/path1");
179 unique_node_ptr parent2 = CreateNode(nullptr, "/path2");
180
181 unique_node_ptr child = CreateNode(parent1.get(), "subdir");
182 ASSERT_EQ(2, GetRefCount(parent1.get()));
183 ASSERT_EQ(child.get(), parent1->LookupChildByName("subdir", false /* acquire */));
184
185 parent1->RenameChild("subdir", "subdir_new", parent2.get());
186 ASSERT_EQ(1, GetRefCount(parent1.get()));
187 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir", false /* acquire */));
188 ASSERT_EQ(nullptr, parent1->LookupChildByName("subdir_new", false /* acquire */));
189
190 ASSERT_EQ(2, GetRefCount(parent2.get()));
191 ASSERT_EQ(child.get(), parent2->LookupChildByName("subdir_new", false /* acquire */));
192
193 ASSERT_EQ("/path2/subdir_new", child->BuildPath());
194 ASSERT_EQ(1, GetRefCount(child.get()));
195}
196
Narayan Kamathaef84a12020-01-02 15:20:13 +0000197TEST_F(NodeTest, TestBuildPath) {
198 unique_node_ptr parent = CreateNode(nullptr, "/path");
199 ASSERT_EQ("/path", parent->BuildPath());
200
201 unique_node_ptr child = CreateNode(parent.get(), "subdir");
202 ASSERT_EQ("/path/subdir", child->BuildPath());
203
204 unique_node_ptr child2 = CreateNode(parent.get(), "subdir2");
205 ASSERT_EQ("/path/subdir2", child2->BuildPath());
206
207 unique_node_ptr subchild = CreateNode(child2.get(), "subsubdir");
208 ASSERT_EQ("/path/subdir2/subsubdir", subchild->BuildPath());
209}
210
211TEST_F(NodeTest, TestSetDeleted) {
212 unique_node_ptr parent = CreateNode(nullptr, "/path");
213 unique_node_ptr child = CreateNode(parent.get(), "subdir");
214
Narayan Kamatheca34252020-02-11 13:08:37 +0000215 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000216 child->SetDeleted();
Narayan Kamatheca34252020-02-11 13:08:37 +0000217 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000218}
219
Zim53c2d702020-09-23 12:18:27 +0100220TEST_F(NodeTest, TestSetDeletedForChild) {
221 unique_node_ptr parent = CreateNode(nullptr, "/path");
222 unique_node_ptr child = CreateNode(parent.get(), "subdir");
223
224 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
225 parent->SetDeletedForChild("subdir");
226 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
227}
228
Narayan Kamathaef84a12020-01-02 15:20:13 +0000229TEST_F(NodeTest, DeleteTree) {
230 unique_node_ptr parent = CreateNode(nullptr, "/path");
231
232 // This is the tree that we intend to delete.
Narayan Kamath568f17a2020-02-19 13:45:29 +0000233 node* child = node::Create(parent.get(), "subdir", &lock_, &tracker_);
234 node::Create(child, "s1", &lock_, &tracker_);
235 node* subchild2 = node::Create(child, "s2", &lock_, &tracker_);
236 node::Create(subchild2, "sc2", &lock_, &tracker_);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000237
Narayan Kamatheca34252020-02-11 13:08:37 +0000238 ASSERT_EQ(child, parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000239 node::DeleteTree(child);
Narayan Kamatheca34252020-02-11 13:08:37 +0000240 ASSERT_EQ(nullptr, parent->LookupChildByName("subdir", false /* acquire */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000241}
242
243TEST_F(NodeTest, LookupChildByName_empty) {
244 unique_node_ptr parent = CreateNode(nullptr, "/path");
245 unique_node_ptr child = CreateNode(parent.get(), "subdir");
246
Narayan Kamatheca34252020-02-11 13:08:37 +0000247 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", false /* acquire */));
248 ASSERT_EQ(nullptr, parent->LookupChildByName("", false /* acquire */));
249}
250
251TEST_F(NodeTest, LookupChildByName_refcounts) {
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 ASSERT_EQ(1, GetRefCount(child.get()));
257
258 ASSERT_EQ(child.get(), parent->LookupChildByName("subdir", true /* acquire */));
259 ASSERT_EQ(2, GetRefCount(child.get()));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000260}
261
262TEST_F(NodeTest, LookupAbsolutePath) {
263 unique_node_ptr parent = CreateNode(nullptr, "/path");
264 unique_node_ptr child = CreateNode(parent.get(), "subdir");
265 unique_node_ptr child2 = CreateNode(parent.get(), "subdir2");
266 unique_node_ptr subchild = CreateNode(child2.get(), "subsubdir");
267
268 ASSERT_EQ(parent.get(), node::LookupAbsolutePath(parent.get(), "/path"));
269 ASSERT_EQ(parent.get(), node::LookupAbsolutePath(parent.get(), "/path/"));
270 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path2"));
271
272 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir"));
273 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir/"));
274 // TODO(narayan): Are the two cases below intentional behaviour ?
275 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path//subdir"));
276 ASSERT_EQ(child.get(), node::LookupAbsolutePath(parent.get(), "/path///subdir"));
277
278 ASSERT_EQ(child2.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2"));
279 ASSERT_EQ(child2.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2/"));
280
281 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path/subdir3/"));
282
283 ASSERT_EQ(subchild.get(), node::LookupAbsolutePath(parent.get(), "/path/subdir2/subsubdir"));
284 ASSERT_EQ(nullptr, node::LookupAbsolutePath(parent.get(), "/path/subdir/subsubdir"));
285}
286
287TEST_F(NodeTest, AddDestroyHandle) {
288 unique_node_ptr node = CreateNode(nullptr, "/path");
289
Zim7e35c332020-04-03 19:25:00 +0100290 handle* h = new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */);
Narayan Kamathaef84a12020-01-02 15:20:13 +0000291 node->AddHandle(h);
292 ASSERT_TRUE(node->HasCachedHandle());
293
294 node->DestroyHandle(h);
295 ASSERT_FALSE(node->HasCachedHandle());
296
297 // Should all crash the process as the handle is no longer associated with
298 // the node in question.
299 EXPECT_DEATH(node->DestroyHandle(h), "");
300 EXPECT_DEATH(node->DestroyHandle(nullptr), "");
Sahana Raoee321362020-03-05 19:27:37 +0000301 std::unique_ptr<handle> h2(
Zim7e35c332020-04-03 19:25:00 +0100302 new handle(-1, new mediaprovider::fuse::RedactionInfo, true /* cached */));
Narayan Kamathaef84a12020-01-02 15:20:13 +0000303 EXPECT_DEATH(node->DestroyHandle(h2.get()), "");
304}
Zim89962312020-04-22 21:21:53 +0100305
306TEST_F(NodeTest, CaseInsensitive) {
307 unique_node_ptr parent = CreateNode(nullptr, "/path");
Zim89962312020-04-22 21:21:53 +0100308 unique_node_ptr mixed_child = CreateNode(parent.get(), "cHiLd");
309
Zim0852ba72020-06-09 11:50:33 +0100310 node* upper_child = parent->LookupChildByName("CHILD", false /* acquire */);
311 node* lower_child = parent->LookupChildByName("child", false /* acquire */);
Zim89962312020-04-22 21:21:53 +0100312
Zim0852ba72020-06-09 11:50:33 +0100313 ASSERT_EQ(mixed_child.get(), lower_child);
314 ASSERT_EQ(mixed_child.get(), upper_child);
Zim89962312020-04-22 21:21:53 +0100315}
Nikita Ioffe890e6f22020-06-18 17:35:08 +0100316
317TEST_F(NodeTest, RenameSameNameSameParent) {
318 unique_node_ptr parent = CreateNode(nullptr, "/path1");
319 unique_node_ptr child = CreateNode(parent.get(), "subdir");
320
321 ASSERT_EQ(child.get(), parent->LookupChildByName("SuBdIr", false /* acquire */));
322 ASSERT_EQ(2, GetRefCount(parent.get()));
323
324 child->Rename("subdir", parent.get());
325
326 ASSERT_EQ(child.get(), parent->LookupChildByName("SuBdIr", false /* acquire */));
327 ASSERT_EQ(2, GetRefCount(parent.get()));
328}
329
330TEST_F(NodeTest, RenameRoot) {
331 unique_node_ptr root = CreateNode(nullptr, "/root");
332 ASSERT_EQ(1, GetRefCount(root.get()));
333
334 root->Rename("/i-am-root!", nullptr);
335
336 ASSERT_EQ("/i-am-root!", root->GetName());
337 ASSERT_EQ(1, GetRefCount(root.get()));
338}
339
340TEST_F(NodeTest, NodeCompareDefinesLinearOrder) {
341 unique_node_ptr node_a = CreateNode(nullptr, "a");
342 unique_node_ptr node_b = CreateNode(nullptr, "B");
343 unique_node_ptr node_c = CreateNode(nullptr, "c");
344
345 ASSERT_FALSE(cmp.operator()(node_a.get(), node_a.get()));
346 ASSERT_FALSE(cmp.operator()(node_b.get(), node_b.get()));
347 ASSERT_FALSE(cmp.operator()(node_c.get(), node_c.get()));
348
349 auto check_fn = [&](const node* lhs_node, const node* rhs_node) {
350 ASSERT_TRUE(cmp.operator()(lhs_node, rhs_node));
351 ASSERT_FALSE(cmp.operator()(rhs_node, lhs_node));
352 };
353
354 check_fn(node_a.get(), node_b.get());
355 check_fn(node_b.get(), node_c.get());
356 check_fn(node_a.get(), node_c.get());
357
358 // ("A", 0) < node_a < ("a", max_uintptr_t) < node_b
359 ASSERT_TRUE(cmp.operator()(std::make_pair("A", 0), node_a.get()));
360 ASSERT_TRUE(cmp.operator()(node_a.get(),
361 std::make_pair("A", std::numeric_limits<uintptr_t>::max())));
362 ASSERT_TRUE(cmp.operator()(std::make_pair("A", std::numeric_limits<uintptr_t>::max()),
363 node_b.get()));
364}
365
366TEST_F(NodeTest, LookupChildByName_ChildrenWithSameName) {
367 unique_node_ptr parent = CreateNode(nullptr, "/path");
368 unique_node_ptr foo1 = CreateNode(parent.get(), "FoO");
369 unique_node_ptr foo2 = CreateNode(parent.get(), "fOo");
370 unique_node_ptr bar1 = CreateNode(parent.get(), "BAR");
371 unique_node_ptr bar2 = CreateNode(parent.get(), "bar");
372 unique_node_ptr baz1 = CreateNode(parent.get(), "baZ");
373 unique_node_ptr baz2 = CreateNode(parent.get(), "Baz");
374
375 auto test_fn = [&](const std::string& name, node* first, node* second) {
376 auto node1 = parent->LookupChildByName(name, false /* acquire */);
377 ASSERT_EQ(std::min(first, second), node1);
378 node1->SetDeleted();
379
380 auto node2 = parent->LookupChildByName(name, false /* acquire */);
381 ASSERT_EQ(std::max(first, second), node2);
382 node2->SetDeleted();
383
384 ASSERT_EQ(nullptr, parent->LookupChildByName(name, false /* acquire */));
385 };
386
387 test_fn("foo", foo1.get(), foo2.get());
388 test_fn("bAr", bar1.get(), bar2.get());
389 test_fn("BaZ", baz1.get(), baz2.get());
390}
Zim53c2d702020-09-23 12:18:27 +0100391
392TEST_F(NodeTest, ForChild) {
393 unique_node_ptr parent = CreateNode(nullptr, "/path");
394 unique_node_ptr foo1 = CreateNode(parent.get(), "FoO");
395 unique_node_ptr foo2 = CreateNode(parent.get(), "fOo");
396 unique_node_ptr foo3 = CreateNode(parent.get(), "foo");
397 foo3->SetDeleted();
398
399 std::vector<node*> match_all;
400 auto test_fn_match_all = [&](node* child) {
401 match_all.push_back(child);
402 return false;
403 };
404
405 std::vector<node*> match_first;
406 auto test_fn_match_first = [&](node* child) {
407 match_first.push_back(child);
408 return true;
409 };
410
411 std::vector<node*> match_none;
412 auto test_fn_match_none = [&](node* child) {
413 match_none.push_back(child);
414 return false;
415 };
416
417 node* node_all = ForChild(parent.get(), "foo", test_fn_match_all);
418 ASSERT_EQ(nullptr, node_all);
419 ASSERT_EQ(2, match_all.size());
420 ASSERT_EQ(std::min(foo1.get(), foo2.get()), match_all[0]);
421 ASSERT_EQ(std::max(foo1.get(), foo2.get()), match_all[1]);
422
423 node* node_first = ForChild(parent.get(), "foo", test_fn_match_first);
424 ASSERT_EQ(std::min(foo1.get(), foo2.get()), node_first);
425 ASSERT_EQ(1, match_first.size());
426 ASSERT_EQ(std::min(foo1.get(), foo2.get()), match_first[0]);
427
428 node* node_none = ForChild(parent.get(), "bar", test_fn_match_none);
429 ASSERT_EQ(nullptr, node_none);
430 ASSERT_TRUE(match_none.empty());
431}