123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359 |
- //===----------------------------------------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
- #define TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
- #include <ciso646>
- #ifndef _LIBCPP_VERSION
- #error This header may only be used for libc++ tests"
- #endif
- #ifndef _LIBCPP_DEBUG
- #error _LIBCPP_DEBUG must be defined before including this header
- #endif
- #include <__debug>
- #include <utility>
- #include <cstddef>
- #include <cstdlib>
- #include <cassert>
- #include <string>
- #include <sstream>
- #include <iostream>
- #include "test_macros.h"
- #include "debug_mode_helper.h"
- #include "assert_checkpoint.h"
- #include "test_allocator.h"
- // These test make use of 'if constexpr'.
- #if TEST_STD_VER <= 14
- #error This header may only be used in C++17 and greater
- #endif
- #ifndef __cpp_if_constexpr
- #error These tests require if constexpr
- #endif
- namespace IteratorDebugChecks {
- enum ContainerType {
- CT_None,
- CT_String,
- CT_Vector,
- CT_VectorBool,
- CT_List,
- CT_Deque,
- CT_ForwardList,
- CT_Map,
- CT_Set,
- CT_MultiMap,
- CT_MultiSet,
- CT_UnorderedMap,
- CT_UnorderedSet,
- CT_UnorderedMultiMap,
- CT_UnorderedMultiSet
- };
- constexpr bool isSequential(ContainerType CT) {
- return CT_Vector >= CT && CT_ForwardList <= CT;
- }
- constexpr bool isAssociative(ContainerType CT) {
- return CT_Map >= CT && CT_MultiSet <= CT;
- }
- constexpr bool isUnordered(ContainerType CT) {
- return CT_UnorderedMap >= CT && CT_UnorderedMultiSet <= CT;
- }
- constexpr bool isSet(ContainerType CT) {
- return CT == CT_Set
- || CT == CT_MultiSet
- || CT == CT_UnorderedSet
- || CT == CT_UnorderedMultiSet;
- }
- constexpr bool isMap(ContainerType CT) {
- return CT == CT_Map
- || CT == CT_MultiMap
- || CT == CT_UnorderedMap
- || CT == CT_UnorderedMultiMap;
- }
- constexpr bool isMulti(ContainerType CT) {
- return CT == CT_MultiMap
- || CT == CT_MultiSet
- || CT == CT_UnorderedMultiMap
- || CT == CT_UnorderedMultiSet;
- }
- template <class Container, class ValueType = typename Container::value_type>
- struct ContainerDebugHelper {
- static_assert(std::is_constructible<ValueType, int>::value,
- "must be constructible from int");
- static ValueType makeValueType(int val = 0, int = 0) {
- return ValueType(val);
- }
- };
- template <class Container>
- struct ContainerDebugHelper<Container, char> {
- static char makeValueType(int = 0, int = 0) {
- return 'A';
- }
- };
- template <class Container, class Key, class Value>
- struct ContainerDebugHelper<Container, std::pair<const Key, Value> > {
- using ValueType = std::pair<const Key, Value>;
- static_assert(std::is_constructible<Key, int>::value,
- "must be constructible from int");
- static_assert(std::is_constructible<Value, int>::value,
- "must be constructible from int");
- static ValueType makeValueType(int key = 0, int val = 0) {
- return ValueType(key, val);
- }
- };
- template <class Container, ContainerType CT,
- class Helper = ContainerDebugHelper<Container> >
- struct BasicContainerChecks {
- using value_type = typename Container::value_type;
- using iterator = typename Container::iterator;
- using const_iterator = typename Container::const_iterator;
- using allocator_type = typename Container::allocator_type;
- using traits = std::iterator_traits<iterator>;
- using category = typename traits::iterator_category;
- static_assert(std::is_same<test_allocator<value_type>, allocator_type>::value,
- "the container must use a test allocator");
- static constexpr bool IsBiDir =
- std::is_convertible<category, std::bidirectional_iterator_tag>::value;
- public:
- static void run() {
- run_iterator_tests();
- run_container_tests();
- run_allocator_aware_tests();
- }
- static void run_iterator_tests() {
- TestNullIterators<iterator>();
- TestNullIterators<const_iterator>();
- if constexpr (IsBiDir) { DecrementBegin(); }
- IncrementEnd();
- DerefEndIterator();
- }
- static void run_container_tests() {
- CopyInvalidatesIterators();
- MoveInvalidatesIterators();
- if constexpr (CT != CT_ForwardList) {
- EraseIter();
- EraseIterIter();
- }
- }
- static void run_allocator_aware_tests() {
- SwapNonEqualAllocators();
- if constexpr (CT != CT_ForwardList ) {
- // FIXME: This should work for both forward_list and string
- SwapInvalidatesIterators();
- }
- }
- static Container makeContainer(int size, allocator_type A = allocator_type()) {
- Container C(A);
- if constexpr (CT == CT_ForwardList) {
- for (int i = 0; i < size; ++i)
- C.insert_after(C.before_begin(), Helper::makeValueType(i));
- } else {
- for (int i = 0; i < size; ++i)
- C.insert(C.end(), Helper::makeValueType(i));
- assert(C.size() == static_cast<std::size_t>(size));
- }
- return C;
- }
- static value_type makeValueType(int value) {
- return Helper::makeValueType(value);
- }
- private:
- // Iterator tests
- template <class Iter>
- static void TestNullIterators() {
- CHECKPOINT("testing null iterator");
- Iter it;
- EXPECT_DEATH( ++it );
- EXPECT_DEATH( it++ );
- EXPECT_DEATH( *it );
- if constexpr (CT != CT_VectorBool) {
- EXPECT_DEATH( it.operator->() );
- }
- if constexpr (IsBiDir) {
- EXPECT_DEATH( --it );
- EXPECT_DEATH( it-- );
- }
- }
- static void DecrementBegin() {
- CHECKPOINT("testing decrement on begin");
- Container C = makeContainer(1);
- iterator i = C.end();
- const_iterator ci = C.cend();
- --i;
- --ci;
- assert(i == C.begin());
- EXPECT_DEATH( --i );
- EXPECT_DEATH( i-- );
- EXPECT_DEATH( --ci );
- EXPECT_DEATH( ci-- );
- }
- static void IncrementEnd() {
- CHECKPOINT("testing increment on end");
- Container C = makeContainer(1);
- iterator i = C.begin();
- const_iterator ci = C.begin();
- ++i;
- ++ci;
- assert(i == C.end());
- EXPECT_DEATH( ++i );
- EXPECT_DEATH( i++ );
- EXPECT_DEATH( ++ci );
- EXPECT_DEATH( ci++ );
- }
- static void DerefEndIterator() {
- CHECKPOINT("testing deref end iterator");
- Container C = makeContainer(1);
- iterator i = C.begin();
- const_iterator ci = C.cbegin();
- (void)*i; (void)*ci;
- if constexpr (CT != CT_VectorBool) {
- i.operator->();
- ci.operator->();
- }
- ++i; ++ci;
- assert(i == C.end());
- EXPECT_DEATH( *i );
- EXPECT_DEATH( *ci );
- if constexpr (CT != CT_VectorBool) {
- EXPECT_DEATH( i.operator->() );
- EXPECT_DEATH( ci.operator->() );
- }
- }
- // Container tests
- static void CopyInvalidatesIterators() {
- CHECKPOINT("copy invalidates iterators");
- Container C1 = makeContainer(3);
- iterator i = C1.begin();
- Container C2 = C1;
- if constexpr (CT == CT_ForwardList) {
- iterator i_next = i;
- ++i_next;
- (void)*i_next;
- EXPECT_DEATH( C2.erase_after(i) );
- C1.erase_after(i);
- EXPECT_DEATH( *i_next );
- } else {
- EXPECT_DEATH( C2.erase(i) );
- (void)*i;
- C1.erase(i);
- EXPECT_DEATH( *i );
- }
- }
- static void MoveInvalidatesIterators() {
- CHECKPOINT("copy move invalidates iterators");
- Container C1 = makeContainer(3);
- iterator i = C1.begin();
- Container C2 = std::move(C1);
- (void) *i;
- if constexpr (CT == CT_ForwardList) {
- EXPECT_DEATH( C1.erase_after(i) );
- C2.erase_after(i);
- } else {
- EXPECT_DEATH( C1.erase(i) );
- C2.erase(i);
- EXPECT_DEATH(*i);
- }
- }
- static void EraseIter() {
- CHECKPOINT("testing erase invalidation");
- Container C1 = makeContainer(2);
- iterator it1 = C1.begin();
- iterator it1_next = it1;
- ++it1_next;
- Container C2 = C1;
- EXPECT_DEATH( C2.erase(it1) ); // wrong container
- EXPECT_DEATH( C2.erase(C2.end()) ); // erase with end
- C1.erase(it1_next);
- EXPECT_DEATH( C1.erase(it1_next) ); // invalidated iterator
- C1.erase(it1);
- EXPECT_DEATH( C1.erase(it1) ); // invalidated iterator
- }
- static void EraseIterIter() {
- CHECKPOINT("testing erase iter iter invalidation");
- Container C1 = makeContainer(2);
- iterator it1 = C1.begin();
- iterator it1_next = it1;
- ++it1_next;
- Container C2 = C1;
- iterator it2 = C2.begin();
- iterator it2_next = it2;
- ++it2_next;
- EXPECT_DEATH( C2.erase(it1, it1_next) ); // begin from wrong container
- EXPECT_DEATH( C2.erase(it1, it2_next) ); // end from wrong container
- EXPECT_DEATH( C2.erase(it2, it1_next) ); // both from wrong container
- C2.erase(it2, it2_next);
- }
- // Allocator aware tests
- static void SwapInvalidatesIterators() {
- CHECKPOINT("testing swap invalidates iterators");
- Container C1 = makeContainer(3);
- Container C2 = makeContainer(3);
- iterator it1 = C1.begin();
- iterator it2 = C2.begin();
- swap(C1, C2);
- EXPECT_DEATH( C1.erase(it1) );
- if (CT == CT_String) {
- EXPECT_DEATH(C1.erase(it2));
- } else
- C1.erase(it2);
- //C2.erase(it1);
- EXPECT_DEATH( C1.erase(it1) );
- }
- static void SwapNonEqualAllocators() {
- CHECKPOINT("testing swap with non-equal allocators");
- Container C1 = makeContainer(3, allocator_type(1));
- Container C2 = makeContainer(1, allocator_type(2));
- Container C3 = makeContainer(2, allocator_type(2));
- swap(C2, C3);
- EXPECT_DEATH( swap(C1, C2) );
- }
- private:
- BasicContainerChecks() = delete;
- };
- } // namespace IteratorDebugChecks
- #endif // TEST_SUPPORT_CONTAINER_DEBUG_TESTS_H
|