container_debug_tests.h 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===----------------------------------------------------------------------===//
  8. #ifndef TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
  9. #define TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
  10. #include <ciso646>
  11. #ifndef _LIBCPP_VERSION
  12. #error This header may only be used for libc++ tests"
  13. #endif
  14. #ifndef _LIBCPP_DEBUG
  15. #error _LIBCPP_DEBUG must be defined before including this header
  16. #endif
  17. #include <__debug>
  18. #include <utility>
  19. #include <cstddef>
  20. #include <cstdlib>
  21. #include <cassert>
  22. #include <string>
  23. #include <sstream>
  24. #include <iostream>
  25. #include "test_macros.h"
  26. #include "debug_mode_helper.h"
  27. #include "assert_checkpoint.h"
  28. #include "test_allocator.h"
  29. // These test make use of 'if constexpr'.
  30. #if TEST_STD_VER <= 14
  31. #error This header may only be used in C++17 and greater
  32. #endif
  33. #ifndef __cpp_if_constexpr
  34. #error These tests require if constexpr
  35. #endif
  36. namespace IteratorDebugChecks {
  37. enum ContainerType {
  38. CT_None,
  39. CT_String,
  40. CT_Vector,
  41. CT_VectorBool,
  42. CT_List,
  43. CT_Deque,
  44. CT_ForwardList,
  45. CT_Map,
  46. CT_Set,
  47. CT_MultiMap,
  48. CT_MultiSet,
  49. CT_UnorderedMap,
  50. CT_UnorderedSet,
  51. CT_UnorderedMultiMap,
  52. CT_UnorderedMultiSet
  53. };
  54. constexpr bool isSequential(ContainerType CT) {
  55. return CT_Vector >= CT && CT_ForwardList <= CT;
  56. }
  57. constexpr bool isAssociative(ContainerType CT) {
  58. return CT_Map >= CT && CT_MultiSet <= CT;
  59. }
  60. constexpr bool isUnordered(ContainerType CT) {
  61. return CT_UnorderedMap >= CT && CT_UnorderedMultiSet <= CT;
  62. }
  63. constexpr bool isSet(ContainerType CT) {
  64. return CT == CT_Set
  65. || CT == CT_MultiSet
  66. || CT == CT_UnorderedSet
  67. || CT == CT_UnorderedMultiSet;
  68. }
  69. constexpr bool isMap(ContainerType CT) {
  70. return CT == CT_Map
  71. || CT == CT_MultiMap
  72. || CT == CT_UnorderedMap
  73. || CT == CT_UnorderedMultiMap;
  74. }
  75. constexpr bool isMulti(ContainerType CT) {
  76. return CT == CT_MultiMap
  77. || CT == CT_MultiSet
  78. || CT == CT_UnorderedMultiMap
  79. || CT == CT_UnorderedMultiSet;
  80. }
  81. template <class Container, class ValueType = typename Container::value_type>
  82. struct ContainerDebugHelper {
  83. static_assert(std::is_constructible<ValueType, int>::value,
  84. "must be constructible from int");
  85. static ValueType makeValueType(int val = 0, int = 0) {
  86. return ValueType(val);
  87. }
  88. };
  89. template <class Container>
  90. struct ContainerDebugHelper<Container, char> {
  91. static char makeValueType(int = 0, int = 0) {
  92. return 'A';
  93. }
  94. };
  95. template <class Container, class Key, class Value>
  96. struct ContainerDebugHelper<Container, std::pair<const Key, Value> > {
  97. using ValueType = std::pair<const Key, Value>;
  98. static_assert(std::is_constructible<Key, int>::value,
  99. "must be constructible from int");
  100. static_assert(std::is_constructible<Value, int>::value,
  101. "must be constructible from int");
  102. static ValueType makeValueType(int key = 0, int val = 0) {
  103. return ValueType(key, val);
  104. }
  105. };
  106. template <class Container, ContainerType CT,
  107. class Helper = ContainerDebugHelper<Container> >
  108. struct BasicContainerChecks {
  109. using value_type = typename Container::value_type;
  110. using iterator = typename Container::iterator;
  111. using const_iterator = typename Container::const_iterator;
  112. using allocator_type = typename Container::allocator_type;
  113. using traits = std::iterator_traits<iterator>;
  114. using category = typename traits::iterator_category;
  115. static_assert(std::is_same<test_allocator<value_type>, allocator_type>::value,
  116. "the container must use a test allocator");
  117. static constexpr bool IsBiDir =
  118. std::is_convertible<category, std::bidirectional_iterator_tag>::value;
  119. public:
  120. static void run() {
  121. run_iterator_tests();
  122. run_container_tests();
  123. run_allocator_aware_tests();
  124. }
  125. static void run_iterator_tests() {
  126. TestNullIterators<iterator>();
  127. TestNullIterators<const_iterator>();
  128. if constexpr (IsBiDir) { DecrementBegin(); }
  129. IncrementEnd();
  130. DerefEndIterator();
  131. }
  132. static void run_container_tests() {
  133. CopyInvalidatesIterators();
  134. MoveInvalidatesIterators();
  135. if constexpr (CT != CT_ForwardList) {
  136. EraseIter();
  137. EraseIterIter();
  138. }
  139. }
  140. static void run_allocator_aware_tests() {
  141. SwapNonEqualAllocators();
  142. if constexpr (CT != CT_ForwardList ) {
  143. // FIXME: This should work for both forward_list and string
  144. SwapInvalidatesIterators();
  145. }
  146. }
  147. static Container makeContainer(int size, allocator_type A = allocator_type()) {
  148. Container C(A);
  149. if constexpr (CT == CT_ForwardList) {
  150. for (int i = 0; i < size; ++i)
  151. C.insert_after(C.before_begin(), Helper::makeValueType(i));
  152. } else {
  153. for (int i = 0; i < size; ++i)
  154. C.insert(C.end(), Helper::makeValueType(i));
  155. assert(C.size() == static_cast<std::size_t>(size));
  156. }
  157. return C;
  158. }
  159. static value_type makeValueType(int value) {
  160. return Helper::makeValueType(value);
  161. }
  162. private:
  163. // Iterator tests
  164. template <class Iter>
  165. static void TestNullIterators() {
  166. CHECKPOINT("testing null iterator");
  167. Iter it;
  168. EXPECT_DEATH( ++it );
  169. EXPECT_DEATH( it++ );
  170. EXPECT_DEATH( *it );
  171. if constexpr (CT != CT_VectorBool) {
  172. EXPECT_DEATH( it.operator->() );
  173. }
  174. if constexpr (IsBiDir) {
  175. EXPECT_DEATH( --it );
  176. EXPECT_DEATH( it-- );
  177. }
  178. }
  179. static void DecrementBegin() {
  180. CHECKPOINT("testing decrement on begin");
  181. Container C = makeContainer(1);
  182. iterator i = C.end();
  183. const_iterator ci = C.cend();
  184. --i;
  185. --ci;
  186. assert(i == C.begin());
  187. EXPECT_DEATH( --i );
  188. EXPECT_DEATH( i-- );
  189. EXPECT_DEATH( --ci );
  190. EXPECT_DEATH( ci-- );
  191. }
  192. static void IncrementEnd() {
  193. CHECKPOINT("testing increment on end");
  194. Container C = makeContainer(1);
  195. iterator i = C.begin();
  196. const_iterator ci = C.begin();
  197. ++i;
  198. ++ci;
  199. assert(i == C.end());
  200. EXPECT_DEATH( ++i );
  201. EXPECT_DEATH( i++ );
  202. EXPECT_DEATH( ++ci );
  203. EXPECT_DEATH( ci++ );
  204. }
  205. static void DerefEndIterator() {
  206. CHECKPOINT("testing deref end iterator");
  207. Container C = makeContainer(1);
  208. iterator i = C.begin();
  209. const_iterator ci = C.cbegin();
  210. (void)*i; (void)*ci;
  211. if constexpr (CT != CT_VectorBool) {
  212. i.operator->();
  213. ci.operator->();
  214. }
  215. ++i; ++ci;
  216. assert(i == C.end());
  217. EXPECT_DEATH( *i );
  218. EXPECT_DEATH( *ci );
  219. if constexpr (CT != CT_VectorBool) {
  220. EXPECT_DEATH( i.operator->() );
  221. EXPECT_DEATH( ci.operator->() );
  222. }
  223. }
  224. // Container tests
  225. static void CopyInvalidatesIterators() {
  226. CHECKPOINT("copy invalidates iterators");
  227. Container C1 = makeContainer(3);
  228. iterator i = C1.begin();
  229. Container C2 = C1;
  230. if constexpr (CT == CT_ForwardList) {
  231. iterator i_next = i;
  232. ++i_next;
  233. (void)*i_next;
  234. EXPECT_DEATH( C2.erase_after(i) );
  235. C1.erase_after(i);
  236. EXPECT_DEATH( *i_next );
  237. } else {
  238. EXPECT_DEATH( C2.erase(i) );
  239. (void)*i;
  240. C1.erase(i);
  241. EXPECT_DEATH( *i );
  242. }
  243. }
  244. static void MoveInvalidatesIterators() {
  245. CHECKPOINT("copy move invalidates iterators");
  246. Container C1 = makeContainer(3);
  247. iterator i = C1.begin();
  248. Container C2 = std::move(C1);
  249. (void) *i;
  250. if constexpr (CT == CT_ForwardList) {
  251. EXPECT_DEATH( C1.erase_after(i) );
  252. C2.erase_after(i);
  253. } else {
  254. EXPECT_DEATH( C1.erase(i) );
  255. C2.erase(i);
  256. EXPECT_DEATH(*i);
  257. }
  258. }
  259. static void EraseIter() {
  260. CHECKPOINT("testing erase invalidation");
  261. Container C1 = makeContainer(2);
  262. iterator it1 = C1.begin();
  263. iterator it1_next = it1;
  264. ++it1_next;
  265. Container C2 = C1;
  266. EXPECT_DEATH( C2.erase(it1) ); // wrong container
  267. EXPECT_DEATH( C2.erase(C2.end()) ); // erase with end
  268. C1.erase(it1_next);
  269. EXPECT_DEATH( C1.erase(it1_next) ); // invalidated iterator
  270. C1.erase(it1);
  271. EXPECT_DEATH( C1.erase(it1) ); // invalidated iterator
  272. }
  273. static void EraseIterIter() {
  274. CHECKPOINT("testing erase iter iter invalidation");
  275. Container C1 = makeContainer(2);
  276. iterator it1 = C1.begin();
  277. iterator it1_next = it1;
  278. ++it1_next;
  279. Container C2 = C1;
  280. iterator it2 = C2.begin();
  281. iterator it2_next = it2;
  282. ++it2_next;
  283. EXPECT_DEATH( C2.erase(it1, it1_next) ); // begin from wrong container
  284. EXPECT_DEATH( C2.erase(it1, it2_next) ); // end from wrong container
  285. EXPECT_DEATH( C2.erase(it2, it1_next) ); // both from wrong container
  286. C2.erase(it2, it2_next);
  287. }
  288. // Allocator aware tests
  289. static void SwapInvalidatesIterators() {
  290. CHECKPOINT("testing swap invalidates iterators");
  291. Container C1 = makeContainer(3);
  292. Container C2 = makeContainer(3);
  293. iterator it1 = C1.begin();
  294. iterator it2 = C2.begin();
  295. swap(C1, C2);
  296. EXPECT_DEATH( C1.erase(it1) );
  297. if (CT == CT_String) {
  298. EXPECT_DEATH(C1.erase(it2));
  299. } else
  300. C1.erase(it2);
  301. //C2.erase(it1);
  302. EXPECT_DEATH( C1.erase(it1) );
  303. }
  304. static void SwapNonEqualAllocators() {
  305. CHECKPOINT("testing swap with non-equal allocators");
  306. Container C1 = makeContainer(3, allocator_type(1));
  307. Container C2 = makeContainer(1, allocator_type(2));
  308. Container C3 = makeContainer(2, allocator_type(2));
  309. swap(C2, C3);
  310. EXPECT_DEATH( swap(C1, C2) );
  311. }
  312. private:
  313. BasicContainerChecks() = delete;
  314. };
  315. } // namespace IteratorDebugChecks
  316. #endif // TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H