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