1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
10 #define TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
12 #include "test_macros.h"
14 #ifndef _LIBCPP_VERSION
15 #error This header may only be used for libc++ tests
18 #if _LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_DEBUG
19 #error The library must be built with the debug mode enabled in order to use this header
27 #include "check_assertion.h"
28 #include "test_allocator.h"
30 // These test make use of 'if constexpr'.
31 #if TEST_STD_VER <= 14
32 #error This header may only be used in C++17 and greater
35 namespace IteratorDebugChecks
{
55 constexpr bool isSequential(ContainerType CT
) {
56 return CT
>= CT_Vector
&& CT
<= CT_ForwardList
;
59 constexpr bool isAssociative(ContainerType CT
) {
60 return CT
>= CT_Map
&& CT
<= CT_MultiSet
;
63 constexpr bool isUnordered(ContainerType CT
) {
64 return CT
>= CT_UnorderedMap
&& CT
<= CT_UnorderedMultiSet
;
67 constexpr bool isSet(ContainerType CT
) {
70 || CT
== CT_UnorderedSet
71 || CT
== CT_UnorderedMultiSet
;
74 constexpr bool isMap(ContainerType CT
) {
77 || CT
== CT_UnorderedMap
78 || CT
== CT_UnorderedMultiMap
;
81 constexpr bool isMulti(ContainerType CT
) {
82 return CT
== CT_MultiMap
84 || CT
== CT_UnorderedMultiMap
85 || CT
== CT_UnorderedMultiSet
;
88 template <class Container
, class ValueType
= typename
Container::value_type
>
89 struct ContainerDebugHelper
{
90 static_assert(std::is_constructible
<ValueType
, int>::value
,
91 "must be constructible from int");
93 static ValueType
makeValueType(int val
= 0, int = 0) {
94 return ValueType(val
);
98 template <class Container
>
99 struct ContainerDebugHelper
<Container
, char> {
100 static char makeValueType(int = 0, int = 0) {
105 template <class Container
, class Key
, class Value
>
106 struct ContainerDebugHelper
<Container
, std::pair
<const Key
, Value
> > {
107 using ValueType
= std::pair
<const Key
, Value
>;
108 static_assert(std::is_constructible
<Key
, int>::value
,
109 "must be constructible from int");
110 static_assert(std::is_constructible
<Value
, int>::value
,
111 "must be constructible from int");
113 static ValueType
makeValueType(int key
= 0, int val
= 0) {
114 return ValueType(key
, val
);
118 template <class Container
, ContainerType CT
,
119 class Helper
= ContainerDebugHelper
<Container
> >
120 struct BasicContainerChecks
{
121 using value_type
= typename
Container::value_type
;
122 using iterator
= typename
Container::iterator
;
123 using const_iterator
= typename
Container::const_iterator
;
124 using allocator_type
= typename
Container::allocator_type
;
125 using traits
= std::iterator_traits
<iterator
>;
126 using category
= typename
traits::iterator_category
;
128 static_assert(std::is_same
<test_allocator
<value_type
>, allocator_type
>::value
,
129 "the container must use a test allocator");
131 static constexpr bool IsBiDir
=
132 std::is_convertible
<category
, std::bidirectional_iterator_tag
>::value
;
136 run_iterator_tests();
137 run_container_tests();
138 run_allocator_aware_tests();
141 static void run_iterator_tests() {
142 TestNullIterators
<iterator
>();
143 TestNullIterators
<const_iterator
>();
144 if constexpr (IsBiDir
) { DecrementBegin(); }
149 static void run_container_tests() {
150 CopyInvalidatesIterators();
151 MoveInvalidatesIterators();
152 if constexpr (CT
!= CT_ForwardList
) {
158 static void run_allocator_aware_tests() {
159 SwapNonEqualAllocators();
160 if constexpr (CT
!= CT_ForwardList
) {
161 // FIXME: This should work for both forward_list and string
162 SwapInvalidatesIterators();
166 static Container
makeContainer(int size
, allocator_type A
= allocator_type()) {
168 if constexpr (CT
== CT_ForwardList
) {
169 for (int i
= 0; i
< size
; ++i
)
170 C
.insert_after(C
.before_begin(), Helper::makeValueType(i
));
172 for (int i
= 0; i
< size
; ++i
)
173 C
.insert(C
.end(), Helper::makeValueType(i
));
174 assert(C
.size() == static_cast<std::size_t>(size
));
179 static value_type
makeValueType(int value
) {
180 return Helper::makeValueType(value
);
185 template <class Iter
>
186 static void TestNullIterators() {
187 // testing null iterator
189 EXPECT_DEATH( ++it
);
190 EXPECT_DEATH( it
++ );
192 if constexpr (CT
!= CT_VectorBool
) {
193 EXPECT_DEATH( it
.operator->() );
195 if constexpr (IsBiDir
) {
196 EXPECT_DEATH( --it
);
197 EXPECT_DEATH( it
-- );
201 static void DecrementBegin() {
202 // testing decrement on begin
203 Container C
= makeContainer(1);
204 iterator i
= C
.end();
205 const_iterator ci
= C
.cend();
208 assert(i
== C
.begin());
211 EXPECT_DEATH( --ci
);
212 EXPECT_DEATH( ci
-- );
215 static void IncrementEnd() {
216 // testing increment on end
217 Container C
= makeContainer(1);
218 iterator i
= C
.begin();
219 const_iterator ci
= C
.begin();
222 assert(i
== C
.end());
225 EXPECT_DEATH( ++ci
);
226 EXPECT_DEATH( ci
++ );
229 static void DerefEndIterator() {
230 // testing deref end iterator
231 Container C
= makeContainer(1);
232 iterator i
= C
.begin();
233 const_iterator ci
= C
.cbegin();
235 if constexpr (CT
!= CT_VectorBool
) {
240 assert(i
== C
.end());
243 if constexpr (CT
!= CT_VectorBool
) {
244 EXPECT_DEATH( i
.operator->() );
245 EXPECT_DEATH( ci
.operator->() );
250 static void CopyInvalidatesIterators() {
251 // copy invalidates iterators
252 Container C1
= makeContainer(3);
253 iterator i
= C1
.begin();
255 if constexpr (CT
== CT_ForwardList
) {
259 EXPECT_DEATH( C2
.erase_after(i
) );
261 EXPECT_DEATH( *i_next
);
263 EXPECT_DEATH( C2
.erase(i
) );
270 static void MoveInvalidatesIterators() {
271 // copy move invalidates iterators
272 Container C1
= makeContainer(3);
273 iterator i
= C1
.begin();
274 Container C2
= std::move(C1
);
276 if constexpr (CT
== CT_ForwardList
) {
277 EXPECT_DEATH( C1
.erase_after(i
) );
280 EXPECT_DEATH( C1
.erase(i
) );
286 static void EraseIter() {
287 // testing erase invalidation
288 Container C1
= makeContainer(2);
289 iterator it1
= C1
.begin();
290 iterator it1_next
= it1
;
293 EXPECT_DEATH( C2
.erase(it1
) ); // wrong container
294 EXPECT_DEATH( C2
.erase(C2
.end()) ); // erase with end
296 EXPECT_DEATH( C1
.erase(it1_next
) ); // invalidated iterator
298 EXPECT_DEATH( C1
.erase(it1
) ); // invalidated iterator
301 static void EraseIterIter() {
302 // testing erase iter iter invalidation
303 Container C1
= makeContainer(2);
304 iterator it1
= C1
.begin();
305 iterator it1_next
= it1
;
308 iterator it2
= C2
.begin();
309 iterator it2_next
= it2
;
311 EXPECT_DEATH( C2
.erase(it1
, it1_next
) ); // begin from wrong container
312 EXPECT_DEATH( C2
.erase(it1
, it2_next
) ); // end from wrong container
313 EXPECT_DEATH( C2
.erase(it2
, it1_next
) ); // both from wrong container
314 C2
.erase(it2
, it2_next
);
317 // Allocator aware tests
318 static void SwapInvalidatesIterators() {
319 // testing swap invalidates iterators
320 Container C1
= makeContainer(3);
321 Container C2
= makeContainer(3);
322 iterator it1
= C1
.begin();
323 iterator it2
= C2
.begin();
325 EXPECT_DEATH( C1
.erase(it1
) );
326 if (CT
== CT_String
) {
327 EXPECT_DEATH(C1
.erase(it2
));
331 EXPECT_DEATH( C1
.erase(it1
) );
334 static void SwapNonEqualAllocators() {
335 // testing swap with non-equal allocators
336 Container C1
= makeContainer(3, allocator_type(1));
337 Container C2
= makeContainer(1, allocator_type(2));
338 Container C3
= makeContainer(2, allocator_type(2));
340 EXPECT_DEATH( swap(C1
, C2
) );
344 BasicContainerChecks() = delete;
347 } // namespace IteratorDebugChecks
349 #endif // TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H