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