SetsIntrusive.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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 "UserTypes.h"
  9. #include <AzCore/std/containers/intrusive_set.h>
  10. #include <AzCore/std/functional.h>
  11. #include <AzCore/std/containers/array.h>
  12. #define AZ_TEST_VALIDATE_EMPTY_SET(_set) \
  13. EXPECT_EQ(0, _set.size()); \
  14. EXPECT_TRUE(_set.begin() == _set.end()); \
  15. EXPECT_TRUE(_set.rbegin() == _set.rend()); \
  16. EXPECT_TRUE(_set.empty());
  17. #define AZ_TEST_VALIDATE_SET(_set, _NumElements) \
  18. EXPECT_EQ(_NumElements, _set.size()); \
  19. EXPECT_TRUE((_NumElements > 0) ? !_set.empty() : _set.empty()); \
  20. EXPECT_TRUE((_NumElements > 0) ? _set.begin() != _set.end() : _set.begin() == _set.end()); \
  21. EXPECT_FALSE(_set.empty());
  22. namespace UnitTest
  23. {
  24. using namespace AZStd;
  25. using namespace UnitTestInternal;
  26. // My intrusive set class.
  27. // We have 2 hooks in this class. One of each supported type. Base hook which we inherit from the intrusive_set_node node
  28. // and public member hook (m_setHook).
  29. struct MySetClass
  30. : public intrusive_multiset_node<MySetClass>
  31. {
  32. public:
  33. MySetClass(int data = 101)
  34. : m_data(data) {}
  35. intrusive_multiset_node<MySetClass> m_setHook; // Public member hook.
  36. int m_data; // This is left public only for testing purpose.
  37. };
  38. // Compare operators, used for sort, merge, etc.
  39. AZ_FORCE_INLINE bool operator==(const MySetClass& a, const MySetClass& b) { return a.m_data == b.m_data; }
  40. AZ_FORCE_INLINE bool operator!=(const MySetClass& a, const MySetClass& b) { return a.m_data != b.m_data; }
  41. AZ_FORCE_INLINE bool operator<(const MySetClass& a, const MySetClass& b) { return a.m_data < b.m_data; }
  42. AZ_FORCE_INLINE bool operator>(const MySetClass& a, const MySetClass& b) { return a.m_data > b.m_data; }
  43. class IntrusiveSetContainers
  44. : public LeakDetectionFixture
  45. {
  46. public:
  47. template <class T>
  48. struct RemoveLessThan401
  49. {
  50. AZ_FORCE_INLINE bool operator()(const T& element) const { return element.m_data < 401; }
  51. };
  52. template <class T>
  53. struct UniqueForLessThan401
  54. {
  55. AZ_FORCE_INLINE bool operator()(const T& el1, const T& el2) const { return (el1.m_data == el2.m_data && el1.m_data < 401); }
  56. };
  57. template <class T, size_t S>
  58. void FillArray(array<T, S>& myclassArray)
  59. {
  60. for (int i = 0; i < (int)myclassArray.size(); ++i)
  61. {
  62. myclassArray[i].m_data = 101 + i * 100;
  63. }
  64. }
  65. };
  66. TEST_F(IntrusiveSetContainers, CtorAssign)
  67. {
  68. // Create 2 set types, one which hooks to the base hook and one to the public member hook.
  69. typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
  70. typedef intrusive_multiset<MySetClass, intrusive_multiset_member_hook<MySetClass, &MySetClass::m_setHook> > myclass_member_set_type;
  71. // A static array of MyClass, init with default ctor. Used as source for some of the tests.
  72. array<MySetClass, 20> myclassArray;
  73. myclass_base_set_type myclass_base_set;
  74. myclass_member_set_type myclass_member_set;
  75. // default ctor
  76. AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
  77. AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set);
  78. myclass_base_set_type myclass_base_set21(myclassArray.begin(), myclassArray.end());
  79. AZ_TEST_VALIDATE_SET(myclass_base_set21, myclassArray.size());
  80. for (myclass_base_set_type::const_iterator iter = myclass_base_set21.begin(); iter != myclass_base_set21.end(); ++iter)
  81. {
  82. AZ_TEST_ASSERT((*iter).m_data == 101);
  83. }
  84. myclass_member_set_type myclass_member_set21(myclassArray.begin(), myclassArray.end());
  85. AZ_TEST_VALIDATE_SET(myclass_member_set21, myclassArray.size());
  86. for (myclass_member_set_type::const_iterator iter = myclass_member_set21.begin(); iter != myclass_member_set21.end(); ++iter)
  87. {
  88. AZ_TEST_ASSERT((*iter).m_data == 101);
  89. }
  90. // assign
  91. myclass_base_set21.clear();
  92. myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
  93. AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size());
  94. myclass_member_set21.clear();
  95. myclass_member_set.insert(myclassArray.begin(), myclassArray.end());
  96. AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size());
  97. }
  98. TEST_F(IntrusiveSetContainers, SetResizeInsertEraseClear)
  99. {
  100. // Create 2 set types, one which hooks to the base hook and one to the public member hook.
  101. typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
  102. typedef intrusive_multiset<MySetClass, intrusive_multiset_member_hook<MySetClass, &MySetClass::m_setHook> > myclass_member_set_type;
  103. // A static array of MyClass, init with default ctor. Used as source for some of the tests.
  104. array<MySetClass, 20> myclassArray;
  105. myclass_base_set_type myclass_base_set;
  106. myclass_member_set_type myclass_member_set;
  107. // insert
  108. myclass_base_set.clear();
  109. myclass_member_set.clear();
  110. myclass_base_set.insert(*myclassArray.begin());
  111. AZ_TEST_VALIDATE_SET(myclass_base_set, 1);
  112. AZ_TEST_ASSERT(myclass_base_set.begin()->m_data == myclassArray.begin()->m_data);
  113. myclass_member_set.insert(*myclassArray.begin());
  114. AZ_TEST_VALIDATE_SET(myclass_member_set, 1);
  115. AZ_TEST_ASSERT(myclass_member_set.begin()->m_data == myclassArray.begin()->m_data);
  116. myclass_base_set.insert(next(myclassArray.begin()), myclassArray.end());
  117. AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size());
  118. AZ_TEST_ASSERT(myclass_base_set.begin()->m_data == prev(myclassArray.end())->m_data);
  119. myclass_member_set.insert(next(myclassArray.begin()), myclassArray.end());
  120. AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size());
  121. AZ_TEST_ASSERT(myclass_member_set.rbegin()->m_data == prev(myclassArray.end())->m_data);
  122. // erase
  123. myclass_base_set.erase(prev(myclass_base_set.end()));
  124. AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size() - 1);
  125. myclass_base_set.erase(myclass_base_set.begin());
  126. AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size() - 2);
  127. myclass_member_set.erase(prev(myclass_member_set.end()));
  128. AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size() - 1);
  129. myclass_member_set.erase(myclass_member_set.begin());
  130. AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size() - 2);
  131. myclass_base_set.erase(next(myclass_base_set.begin()), myclass_base_set.end());
  132. AZ_TEST_VALIDATE_SET(myclass_base_set, 1);
  133. myclass_member_set.erase(next(myclass_member_set.begin()), myclass_member_set.end());
  134. AZ_TEST_VALIDATE_SET(myclass_member_set, 1);
  135. // clear
  136. myclass_base_set.clear();
  137. AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
  138. myclass_member_set.clear();
  139. AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set);
  140. }
  141. // TODO: LY-87175 Add move and swap operations to intrusive_set
  142. //TEST_F(IntrusiveSetContainers, Swap)
  143. //{
  144. // typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
  145. // typedef intrusive_multiset<MySetClass, intrusive_multiset_member_hook<MySetClass, &MySetClass::m_setHook> > myclass_member_set_type;
  146. // // A static array of MyClass, init with default ctor. Used as source for some of the tests.
  147. // array<MySetClass, 20> myclassArray;
  148. // myclass_base_set_type myclass_base_set;
  149. // myclass_base_set_type myclass_base_set2;
  150. // myclass_member_set_type myclass_member_set;
  151. // myclass_member_set_type myclass_member_set2;
  152. // // swap
  153. // myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
  154. // myclass_member_set.insert(myclassArray.begin(), myclassArray.end());
  155. // myclass_base_set2.swap(myclass_base_set);
  156. // AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
  157. // AZ_TEST_VALIDATE_SET(myclass_base_set2, myclassArray.size());
  158. // myclass_member_set2.swap(myclass_member_set);
  159. // AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set);
  160. // AZ_TEST_VALIDATE_SET(myclass_member_set2, myclassArray.size());
  161. // myclass_base_set2.swap(myclass_base_set);
  162. // AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set2);
  163. // AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size());
  164. // myclass_member_set2.swap(myclass_member_set);
  165. // AZ_TEST_VALIDATE_EMPTY_SET(myclass_member_set2);
  166. // AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size());
  167. // myclass_base_set.erase(*myclass_base_set.rbegin());
  168. // AZ_TEST_VALIDATE_SET(myclass_base_set, myclassArray.size() - 1);
  169. // myclass_member_set.erase(*myclass_base_set.rbegin());
  170. // AZ_TEST_VALIDATE_SET(myclass_member_set, myclassArray.size() - 1);
  171. // myclass_base_set2.insert(myclassArray.back());
  172. // myclass_member_set2.insert(myclassArray.back());
  173. // myclass_base_set.swap(myclass_base_set2);
  174. // AZ_TEST_VALIDATE_SET(myclass_base_set2, myclassArray.size() - 1);
  175. // AZ_TEST_VALIDATE_SET(myclass_base_set, 1);
  176. // myclass_member_set.swap(myclass_member_set2);
  177. // AZ_TEST_VALIDATE_SET(myclass_member_set2, myclassArray.size() - 1);
  178. // AZ_TEST_VALIDATE_SET(myclass_member_set, 1);
  179. // myclass_base_set.clear();
  180. // myclass_base_set2.clear();
  181. // myclass_member_set.clear();
  182. // myclass_member_set2.clear();
  183. //}
  184. TEST_F(IntrusiveSetContainers, RemoveWhileIterating)
  185. {
  186. typedef intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> > myclass_base_set_type;
  187. // A static array of MyClass, init with default ctor. Used as source for some of the tests.
  188. array<MySetClass, 2> myclassArray;
  189. myclass_base_set_type myclass_base_set;
  190. FillArray(myclassArray);
  191. // remove
  192. myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
  193. myclass_base_set_type::const_iterator it = myclass_base_set.begin();
  194. while (it != myclass_base_set.end())
  195. {
  196. myclass_base_set.erase(it++);
  197. }
  198. AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
  199. // repopulate and remove in reverse order
  200. myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
  201. myclass_base_set_type::const_reverse_iterator rit = myclass_base_set.crbegin();
  202. while (rit != myclass_base_set.crend())
  203. {
  204. const MySetClass* item = rit.operator->();
  205. ++rit;
  206. myclass_base_set.erase(item);
  207. }
  208. AZ_TEST_VALIDATE_EMPTY_SET(myclass_base_set);
  209. }
  210. TEST_F(IntrusiveSetContainers, ReverseIterator)
  211. {
  212. using myclass_base_set_type = intrusive_multiset<MySetClass, intrusive_multiset_base_hook<MySetClass> >;
  213. // A static array of MyClass, init with default ctor. Used as source for some of the tests.
  214. constexpr size_t arraySize = 20;
  215. array<MySetClass, arraySize> myclassArray;
  216. myclass_base_set_type myclass_base_set;
  217. FillArray(myclassArray);
  218. myclass_base_set.insert(myclassArray.begin(), myclassArray.end());
  219. // iterate in reverse order
  220. auto myClassReverseBeginIt = myclass_base_set.crbegin();
  221. auto myClassReverseEndIt = myclass_base_set.crend();
  222. EXPECT_NE(myClassReverseEndIt, myClassReverseBeginIt);
  223. // Reverse iterator must be decremented that in order the base() iterator call
  224. // to return an iterator to a valid element
  225. ++myClassReverseBeginIt;
  226. size_t reverseArrayIndex = arraySize - 1;
  227. for (; myClassReverseBeginIt != myClassReverseEndIt; ++myClassReverseBeginIt, --reverseArrayIndex)
  228. {
  229. EXPECT_EQ(reverseArrayIndex * 100 + 101, myClassReverseBeginIt.base()->m_data);
  230. }
  231. // The crend() element base() call should refer to the first element of the array which
  232. // should have a value of 101 + arrayIndex * 100, where arrayIndex is 0.
  233. EXPECT_EQ(101, myClassReverseBeginIt.base()->m_data);
  234. {
  235. // Test empty intrusive container case
  236. myclass_base_set.clear();
  237. auto reverseIterBegin = myclass_base_set.rbegin();
  238. auto reverseIterEnd = myclass_base_set.rend();
  239. EXPECT_EQ(reverseIterBegin, reverseIterEnd);
  240. }
  241. }
  242. }