DomPrefixTreeTests.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzCore/DOM/DomPrefixTree.h>
  9. #include <Tests/DOM/DomFixtures.h>
  10. namespace AZ::Dom::Tests
  11. {
  12. class DomPrefixTreeTests : public DomTestFixture
  13. {
  14. public:
  15. void SetUp() override
  16. {
  17. DomTestFixture::SetUp();
  18. m_runData = AZStd::make_unique<RunData>();
  19. m_runData->m_visitorFn = [this](const Path& path, int value)
  20. {
  21. auto pathString = path.ToString();
  22. AZStd::string visitedPathString = path.ToString();
  23. for (const auto& entry : m_runData->m_visitorResults)
  24. {
  25. AZStd::string entryPathString = entry.first.ToString();
  26. if (m_runData->m_useDepthFirstTraversal)
  27. {
  28. EXPECT_FALSE(visitedPathString.starts_with(entryPathString));
  29. }
  30. else
  31. {
  32. EXPECT_FALSE(entryPathString.starts_with(visitedPathString));
  33. }
  34. }
  35. m_runData->m_visitorResults.emplace_back(path, value);
  36. return true;
  37. };
  38. m_runData->m_visitorTree.SetValue(Path("/foo"), 99);
  39. m_runData->m_visitorTree.SetValue(Path("/foo/0"), 0);
  40. m_runData->m_visitorTree.SetValue(Path("/foo/1"), 42);
  41. m_runData->m_visitorTree.SetValue(Path("/bar/bat"), 1);
  42. m_runData->m_visitorTree.SetValue(Path("/bar/baz"), 2);
  43. }
  44. void TearDown() override
  45. {
  46. m_runData.reset();
  47. DomTestFixture::TearDown();
  48. }
  49. protected:
  50. struct RunData
  51. {
  52. DomPrefixTree<int> m_visitorTree;
  53. DomPrefixTree<int>::VisitorFunction m_visitorFn;
  54. AZStd::vector<AZStd::pair<Path, int>> m_visitorResults;
  55. bool m_useDepthFirstTraversal = false;
  56. };
  57. AZStd::unique_ptr<RunData> m_runData;
  58. size_t VisitorResultCount()
  59. {
  60. return m_runData->m_visitorResults.size();
  61. }
  62. bool ValidateResult(const Path& path, int n)
  63. {
  64. for (const auto& pair : m_runData->m_visitorResults)
  65. {
  66. if (pair.first == path)
  67. {
  68. return pair.second == n;
  69. }
  70. }
  71. return false;
  72. }
  73. void SetVisitDepthFirst(bool visitDepthFirst)
  74. {
  75. m_runData->m_useDepthFirstTraversal = visitDepthFirst;
  76. }
  77. void TestVisitPath(const Path& path, PrefixTreeTraversalFlags flags)
  78. {
  79. if (m_runData->m_useDepthFirstTraversal)
  80. {
  81. flags = flags | PrefixTreeTraversalFlags::TraverseMostToLeastSpecific;
  82. }
  83. else
  84. {
  85. flags = flags | PrefixTreeTraversalFlags::TraverseLeastToMostSpecific;
  86. }
  87. m_runData->m_visitorTree.VisitPath(path, m_runData->m_visitorFn, flags);
  88. }
  89. };
  90. static_assert(!RangeConvertibleToPrefixTree<AZStd::vector<int>, int>, "Non-pair range should not convert to tree");
  91. static_assert(
  92. !RangeConvertibleToPrefixTree<AZStd::vector<AZStd::pair<Path, AZStd::string>>, int>,
  93. "Mismatched value type should not convert to tree");
  94. static_assert(
  95. !RangeConvertibleToPrefixTree<AZStd::vector<AZStd::pair<AZStd::string, int>>, int>,
  96. "Mismatched value type should not convert to tree");
  97. static_assert(
  98. RangeConvertibleToPrefixTree<AZStd::vector<AZStd::pair<Path, int>>, int>,
  99. "Vector with path / key type pairs should convert to tree");
  100. TEST_F(DomPrefixTreeTests, InitializeFromInitializerList)
  101. {
  102. DomPrefixTree<int> tree({
  103. { Path(), 0 },
  104. { Path("/foo/bar"), 1 },
  105. });
  106. EXPECT_EQ(0, *tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath));
  107. EXPECT_EQ(1, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath));
  108. }
  109. TEST_F(DomPrefixTreeTests, InitializeFromRange)
  110. {
  111. AZStd::vector<AZStd::pair<Path, int>> container({
  112. { Path(), 21 },
  113. { Path("/foo/bar"), 42 },
  114. });
  115. DomPrefixTree<int> tree(container);
  116. EXPECT_EQ(21, *tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath));
  117. EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath));
  118. }
  119. TEST_F(DomPrefixTreeTests, GetAndSetRoot)
  120. {
  121. DomPrefixTree<AZStd::string> tree;
  122. tree.SetValue(Path(), "root");
  123. EXPECT_EQ("root", *tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath));
  124. }
  125. TEST_F(DomPrefixTreeTests, GetExactPath)
  126. {
  127. DomPrefixTree<int> tree;
  128. tree.SetValue(Path("/foo/0"), 0);
  129. tree.SetValue(Path("/foo/1"), 42);
  130. tree.SetValue(Path("/foo/foo"), 1);
  131. tree.SetValue(Path("/foo/bar"), 2);
  132. EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::ExactPath));
  133. EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::ExactPath));
  134. EXPECT_EQ(1, *tree.ValueAtPath(Path("/foo/foo"), PrefixTreeMatch::ExactPath));
  135. EXPECT_EQ(2, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath));
  136. EXPECT_EQ(nullptr, tree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath));
  137. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::ExactPath));
  138. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/0/subpath"), PrefixTreeMatch::ExactPath));
  139. }
  140. TEST_F(DomPrefixTreeTests, GetParent)
  141. {
  142. DomPrefixTree<int> tree;
  143. tree.SetValue(Path("/foo/0"), 0);
  144. tree.SetValue(Path("/foo/1"), 42);
  145. EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar"), PrefixTreeMatch::ParentsOnly));
  146. EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar/baz"), PrefixTreeMatch::ParentsOnly));
  147. EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1/0"), PrefixTreeMatch::ParentsOnly));
  148. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::ParentsOnly));
  149. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::ParentsOnly));
  150. }
  151. TEST_F(DomPrefixTreeTests, GetPathOrParent)
  152. {
  153. DomPrefixTree<int> tree;
  154. tree.SetValue(Path("/foo/0"), 0);
  155. tree.SetValue(Path("/foo/1"), 42);
  156. EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::PathAndParents));
  157. EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar"), PrefixTreeMatch::PathAndParents));
  158. EXPECT_EQ(0, *tree.ValueAtPath(Path("/foo/0/bar/baz"), PrefixTreeMatch::PathAndParents));
  159. EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::PathAndParents));
  160. EXPECT_EQ(42, *tree.ValueAtPath(Path("/foo/1/0"), PrefixTreeMatch::PathAndParents));
  161. EXPECT_EQ(nullptr, tree.ValueAtPath(Path(), PrefixTreeMatch::PathAndParents));
  162. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::PathAndParents));
  163. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/path/0"), PrefixTreeMatch::PathAndParents));
  164. }
  165. TEST_F(DomPrefixTreeTests, RemovePath)
  166. {
  167. DomPrefixTree<int> tree;
  168. tree.SetValue(Path(), 20);
  169. tree.SetValue(Path("/foo"), 40);
  170. tree.SetValue(Path("/foo/0"), 80);
  171. tree.EraseValue(Path("/foo"));
  172. EXPECT_EQ(20, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::PathAndParents));
  173. EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::PathAndParents));
  174. }
  175. TEST_F(DomPrefixTreeTests, RemovePathAndChildren)
  176. {
  177. DomPrefixTree<int> tree;
  178. tree.SetValue(Path(), 20);
  179. tree.SetValue(Path("/foo"), 40);
  180. tree.SetValue(Path("/foo/0"), 80);
  181. tree.EraseValue(Path("/foo"), true);
  182. EXPECT_EQ(20, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::PathAndParents));
  183. EXPECT_EQ(20, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::PathAndParents));
  184. }
  185. TEST_F(DomPrefixTreeTests, DetachSubTreeAtExistingPath)
  186. {
  187. DomPrefixTree<int> tree;
  188. tree.SetValue(Path("/foo"), 40);
  189. tree.SetValue(Path("/foo/0"), 80);
  190. DomPrefixTree<int> detachedTree = tree.DetachSubTree(AZ::Dom::Path("/foo"));
  191. // Validate that the values aren't present in the old tree after detaching.
  192. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::ExactPath));
  193. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::ExactPath));
  194. // Validate that the values are present in the detached tree.
  195. EXPECT_EQ(40, *detachedTree.ValueAtPath(Path(), PrefixTreeMatch::ExactPath));
  196. EXPECT_EQ(80, *detachedTree.ValueAtPath(Path("/0"), PrefixTreeMatch::ExactPath));
  197. }
  198. TEST_F(DomPrefixTreeTests, DetachSubTreeAtNonExistingPath)
  199. {
  200. DomPrefixTree<int> tree;
  201. tree.SetValue(Path("/foo"), 40);
  202. tree.SetValue(Path("/foo/0"), 80);
  203. DomPrefixTree<int> detachedTree = tree.DetachSubTree(AZ::Dom::Path("/bar"));
  204. // Validate that the returned tree is empty since a non-existing path is provided.
  205. EXPECT_TRUE(detachedTree.IsEmpty());
  206. // Validate that the values in the original tree are still present.
  207. EXPECT_EQ(40, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::ExactPath));
  208. EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo/0"), PrefixTreeMatch::ExactPath));
  209. }
  210. TEST_F(DomPrefixTreeTests, OverwritePathAddsNewNodeAtExistingPath)
  211. {
  212. DomPrefixTree<int> tree;
  213. tree.SetValue(Path("/foo"), 40);
  214. DomPrefixTree<int> subtree;
  215. subtree.SetValue(Path(), 80);
  216. subtree.SetValue(Path("/1"), 120);
  217. bool isOverwritten = tree.OverwritePath(Path("/foo"), AZStd::move(subtree));
  218. // Validate that the subtree is overwritten and new values are present at the paths in the original tree.
  219. EXPECT_TRUE(isOverwritten);
  220. EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::ExactPath));
  221. EXPECT_EQ(120, *tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::ExactPath));
  222. }
  223. TEST_F(DomPrefixTreeTests, OverwritePathReplacesExistingNodeAndChildren)
  224. {
  225. DomPrefixTree<int> tree;
  226. tree.SetValue(Path("/foo"), 40);
  227. tree.SetValue(Path("/foo/1"), 60);
  228. tree.SetValue(Path("/foo/2"), 70);
  229. DomPrefixTree<int> subtree;
  230. subtree.SetValue(Path(), 80);
  231. subtree.SetValue(Path("/1"), 120);
  232. bool isOverwritten = tree.OverwritePath(Path("/foo"), AZStd::move(subtree));
  233. // Validate that the subtree is overwritten and existing nodes are replaced in the original tree.
  234. EXPECT_TRUE(isOverwritten);
  235. EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo"), PrefixTreeMatch::ExactPath));
  236. EXPECT_EQ(120, *tree.ValueAtPath(Path("/foo/1"), PrefixTreeMatch::ExactPath));
  237. EXPECT_EQ(nullptr, tree.ValueAtPath(Path("/foo/2"), PrefixTreeMatch::ExactPath));
  238. }
  239. TEST_F(DomPrefixTreeTests, OverwritePathSucceedsAtNonExistingPath)
  240. {
  241. DomPrefixTree<int> tree;
  242. tree.SetValue(Path("/foo"), 40);
  243. DomPrefixTree<int> subtree;
  244. subtree.SetValue(Path(), 80);
  245. subtree.SetValue(Path("/1"), 120);
  246. tree.OverwritePath(Path("/foo/bar"), AZStd::move(subtree)); // create the node if needed
  247. // Validate that the subtree is overwritten and new values are present at the paths in the original tree.
  248. EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo/bar"), PrefixTreeMatch::ExactPath));
  249. EXPECT_EQ(120, *tree.ValueAtPath(Path("/foo/bar/1"), PrefixTreeMatch::ExactPath));
  250. }
  251. TEST_F(DomPrefixTreeTests, OverwritePathSucceedsAtPathWithoutParents)
  252. {
  253. DomPrefixTree<int> tree;
  254. tree.SetValue(Path("/foo"), 40);
  255. DomPrefixTree<int> subtree;
  256. subtree.SetValue(Path(), 80);
  257. subtree.SetValue(Path("/1"), 120);
  258. tree.OverwritePath(Path("/foo/bar/baz"), AZStd::move(subtree)); // create all nodes if needed
  259. // Validate that the subtree is overwritten and new values are present at the paths in the original tree.
  260. EXPECT_EQ(80, *tree.ValueAtPath(Path("/foo/bar/baz"), PrefixTreeMatch::ExactPath));
  261. EXPECT_EQ(120, *tree.ValueAtPath(Path("/foo/bar/baz/1"), PrefixTreeMatch::ExactPath));
  262. }
  263. TEST_F(DomPrefixTreeTests, OverwritePathFailsAtNonExistingPathIfNotCreateNodes)
  264. {
  265. DomPrefixTree<int> tree;
  266. tree.SetValue(Path("/foo"), 40);
  267. DomPrefixTree<int> subtree;
  268. subtree.SetValue(Path(), 80);
  269. bool isOverwritten = tree.OverwritePath(Path("/foo/bar"), AZStd::move(subtree), false); // 'bar' node does not exist
  270. // Validate that the subtree could not be overwritten.
  271. EXPECT_FALSE(isOverwritten);
  272. }
  273. TEST_F(DomPrefixTreeTests, OverwritePathFailsAtPathWithMissingParentsIfNotCreateNodes)
  274. {
  275. DomPrefixTree<int> tree;
  276. tree.SetValue(Path("/foo"), 40);
  277. DomPrefixTree<int> subtree;
  278. subtree.SetValue(Path(), 80);
  279. bool isOverwritten = tree.OverwritePath(Path("/foo/bar/baz"), AZStd::move(subtree), false); // Parent entry 'bar' does not exist
  280. // Validate that the subtree could not be overwritten.
  281. EXPECT_FALSE(isOverwritten);
  282. }
  283. TEST_F(DomPrefixTreeTests, ClearTree)
  284. {
  285. DomPrefixTree<int> tree;
  286. tree.SetValue(Path(), 20);
  287. tree.SetValue(Path("/foo"), 40);
  288. tree.Clear();
  289. EXPECT_EQ(-10, tree.ValueAtPathOrDefault(Path("/foo"), -10, PrefixTreeMatch::PathAndParents));
  290. EXPECT_TRUE(tree.IsEmpty());
  291. }
  292. TEST_F(DomPrefixTreeTests, VisitMissingExactPath)
  293. {
  294. TestVisitPath(Path("/bar"), PrefixTreeTraversalFlags::ExcludeChildPaths | PrefixTreeTraversalFlags::ExcludeParentPaths);
  295. EXPECT_EQ(0, VisitorResultCount());
  296. }
  297. TEST_F(DomPrefixTreeTests, VisitExactPath)
  298. {
  299. TestVisitPath(Path("/foo/0"), PrefixTreeTraversalFlags::ExcludeChildPaths | PrefixTreeTraversalFlags::ExcludeParentPaths);
  300. EXPECT_EQ(1, VisitorResultCount());
  301. EXPECT_TRUE(ValidateResult(Path("/foo/0"), 0));
  302. }
  303. TEST_F(DomPrefixTreeTests, VisitChildren)
  304. {
  305. TestVisitPath(Path("/foo"), PrefixTreeTraversalFlags::ExcludeExactPath | PrefixTreeTraversalFlags::ExcludeParentPaths);
  306. EXPECT_EQ(2, VisitorResultCount());
  307. EXPECT_TRUE(ValidateResult(Path("/foo/0"), 0));
  308. EXPECT_TRUE(ValidateResult(Path("/foo/1"), 42));
  309. }
  310. TEST_F(DomPrefixTreeTests, VisitPathAndChildren)
  311. {
  312. TestVisitPath(Path("/foo"), PrefixTreeTraversalFlags::ExcludeParentPaths);
  313. EXPECT_EQ(3, VisitorResultCount());
  314. EXPECT_TRUE(ValidateResult(Path("/foo"), 99));
  315. EXPECT_TRUE(ValidateResult(Path("/foo/0"), 0));
  316. EXPECT_TRUE(ValidateResult(Path("/foo/1"), 42));
  317. }
  318. TEST_F(DomPrefixTreeTests, VisitEntireTree)
  319. {
  320. TestVisitPath(Path(), PrefixTreeTraversalFlags::ExcludeExactPath);
  321. EXPECT_EQ(5, VisitorResultCount());
  322. EXPECT_TRUE(ValidateResult(Path("/foo"), 99));
  323. EXPECT_TRUE(ValidateResult(Path("/foo/0"), 0));
  324. EXPECT_TRUE(ValidateResult(Path("/foo/1"), 42));
  325. EXPECT_TRUE(ValidateResult(Path("/bar/bat"), 1));
  326. EXPECT_TRUE(ValidateResult(Path("/bar/baz"), 2));
  327. }
  328. TEST_F(DomPrefixTreeTests, VisitEntireTree_DepthFirst)
  329. {
  330. SetVisitDepthFirst(true);
  331. TestVisitPath(Path(), PrefixTreeTraversalFlags::ExcludeExactPath);
  332. EXPECT_EQ(5, VisitorResultCount());
  333. EXPECT_TRUE(ValidateResult(Path("/foo"), 99));
  334. EXPECT_TRUE(ValidateResult(Path("/foo/0"), 0));
  335. EXPECT_TRUE(ValidateResult(Path("/foo/1"), 42));
  336. EXPECT_TRUE(ValidateResult(Path("/bar/bat"), 1));
  337. EXPECT_TRUE(ValidateResult(Path("/bar/baz"), 2));
  338. }
  339. TEST_F(DomPrefixTreeTests, VisitParentsAndSelf)
  340. {
  341. TestVisitPath(Path("/foo/0"), PrefixTreeTraversalFlags::ExcludeChildPaths);
  342. EXPECT_EQ(2, VisitorResultCount());
  343. EXPECT_TRUE(ValidateResult(Path("/foo"), 99));
  344. EXPECT_TRUE(ValidateResult(Path("/foo/0"), 0));
  345. }
  346. TEST_F(DomPrefixTreeTests, VisitParentsOnly)
  347. {
  348. TestVisitPath(Path("/foo/0"), PrefixTreeTraversalFlags::ExcludeExactPath | PrefixTreeTraversalFlags::ExcludeChildPaths);
  349. EXPECT_EQ(1, VisitorResultCount());
  350. EXPECT_TRUE(ValidateResult(Path("/foo"), 99));
  351. }
  352. TEST_F(DomPrefixTreeTests, EarlyTerminateVisit)
  353. {
  354. size_t visitCalls = 0;
  355. auto visitorFn = [&visitCalls](const Path&, int)
  356. {
  357. ++visitCalls;
  358. return false;
  359. };
  360. m_runData->m_visitorTree.VisitPath(Path(), visitorFn, PrefixTreeTraversalFlags::TraverseLeastToMostSpecific);
  361. EXPECT_EQ(visitCalls, 1);
  362. visitCalls = 0;
  363. m_runData->m_visitorTree.VisitPath(Path(), visitorFn, PrefixTreeTraversalFlags::TraverseMostToLeastSpecific);
  364. EXPECT_EQ(visitCalls, 1);
  365. }
  366. TEST_F(DomPrefixTreeTests, RemoveChildrenDuringVisit)
  367. {
  368. // Remove children during a depth-first traversal
  369. auto visitorFn = [&](const Path& path, int)
  370. {
  371. m_runData->m_visitorTree.EraseValue(path, true);
  372. return true;
  373. };
  374. m_runData->m_visitorTree.VisitPath(Path(), visitorFn, PrefixTreeTraversalFlags::TraverseMostToLeastSpecific);
  375. // Ensure all entries were successfully deleted
  376. TestVisitPath(Path(), PrefixTreeTraversalFlags::None);
  377. EXPECT_EQ(0, VisitorResultCount());
  378. }
  379. } // namespace AZ::Dom::Tests